Skip to content

sus-core vs Vue 3 - audit and improvement plan

Date: June 30, 2026
Status: primary audit completed, tasks assigned

⚠️ Historical planning doc — several "to-fix" items below are already DONE. Corrections:

  • §6.1 SusThemeService.Current (reactive Prop<​SusTheme>) existsWatch(SusThemeService.Current, …) works.
  • §6.2 SusResolutionService removed — screen adaptation is breakpoints only (SusBreakpointService).
  • Router KeepAlive is an off-DOM cache (SusScreenOutlet detaches + caches the screen), notdisplay:none and not a core SusKeepAlive wrapper. See sus-router/docs/04-routeview.md. The display:none / SusKeepAlive mentions below are stale.

Full audit of all 19 Runtime files of sus-core and compiler. Comparison with Vue 3 for each subsystem: what is there, what is not, what to fix.


Content

  1. Reactivity
  2. Component model
  3. Template directives
  4. Built-in components
  5. Application infrastructure

5b. Unity 6+ UITK - unused native features 6. Technical debt (bugs/inconsistencies) 7. Consolidated priority list 8. App: Vue pivot table → sus-core


Legend

MarkerMeaning
🔥Critical - blocks development of each screen/form
⚠️Important - simplifies development, removes boilerplate
🔧Technical debt - fix in current code
📦The future is not a blocker, architectural improvements
Already implemented

1. Reactivity

1.1 ✅ Prop<​T> - analogue of Vue ref<T>

Status: implemented.
File: sus-core/Runtime/Prop.cs

Reactive property with Changed event, Unity data binding, auto-tracking from Computed<​T> through DependencyTracker. Implicit operator for reading without .Value.


1.2 ✅ Computed<​T> - analogue of Vue computed<T>

Status: implemented (July 1, 2026 - added IReactiveSource).
Files: sus-core/Runtime/Computed.cs, DependencyTracker.cs, IReactiveSource.cs

Auto tracking via [ThreadStatic] collector. On the first reading, executes fn, subscribes to all read Prop<​T> / Computed<​T>, caches. When changing any dependency - MarkDirty() (only at the front false→true), the next reading recalculates.

Important: from July 1 Computed<​T> himself is IReactiveSource:

  • Value causes DependencyTracker.RegisterSource(this) — external tracking sees computed as a source
  • MarkDirty() forwards invalidation to its subscribers (push up the chain)
  • Chains Prop → Computed A → Computed B → BindText work

1.3 ✅ Watch(Prop, callback) - analogue of Vue watch(ref, fn)

Status: implemented.
Files: sus-core/Runtime/SusComponent.cs:26-33, WatchHandle.cs

Single Prop watcher. Returns WatchHandle : IDisposable — when Dispose unsubscribes.


1.4 ✅ watchEffect(fn) - analogue of Vue watchEffect

Status: ✅ IMPLEMENTED.
Vue equivalent: watchEffect(() => { ... })

WatchEffect(Action fn) on SusComponent auto-tracks all Prop / Computed reads inside fn and re-runs when any dependency changes. Returns WatchHandle for dispose.

csharp
WatchEffect(() => {
    var total = hp.Value + maxHp.Value;
    var label = name.Value;
    UpdateUI(total, label);  // auto-restart when hp, maxHp or name changes
});

See sus-core/Runtime/SusComponent.cs and 03-reactivity.md.


1.5 ⚠️ Deep reactivity - similar to Vue reactive(obj)

Status: ❌ NOT IMPLEMENTED.
Vue equivalent: const state = reactive({ hp: 100, name: "Unit" })

Problem

Prop<​T> - only one meaning. For an object with 5 fields, you need 5 separate ones Prop<​T>. If the field changes directly ( unit.HP = 50), UI is not updated - there is no property-change tracking mechanism.

What to do

Option A (minimal): Document the pattern in README/ARCHITECTURE:

csharp
// Each mutable field → separate Prop<​T>
public Prop<​int> HP = new(100);
public Prop<​int> MaxHP = new(100);
public Prop<​string> Name = new("Unit");

Option B (full-fledged, 📦 future): Reactive<T> where T : INotifyPropertyChanged - a wrapper that listens PropertyChanged and disables Computed<​T>. Useful for ViewModel pattern.

Priority: ⚠️ (document now, Reactive - 📦)


2. Component model

2.1 ✅ Emit / On / Off - analogue of Vue defineEmits

Status: implemented.
Files: sus-core/Runtime/SusComponent.Events.cs

Typed events: Emit<TEvent>(string name, TEvent data), On<TEvent>(name, handler), Off<TEvent>(name, handler). Events are bubbled through the UI Toolkit EventBase.


2.2 ✅ BuildSlot / RegisterSlotContent - analogue of Vue <slot>

Status: implemented.
Files: sus-core/Runtime/SusComponent.Slots.cs, SlotProps.cs

Named slots + scoped props (SlotPropMap) + content registration ( v-slot).


2.2b ✅ Props between components - analogous to Vue prop-passing

Status: implemented (2026-07-01).
Files: sus-core/Runtime/SusComponent.Bind.cs, BuildMethodGenerator.cs

  • Literal props (variant="primary") — SetChildProp with IgnoreCase, mutates .Value existing Prop<​T> (does not replace the instance → preserves the child’s internal bindings).
  • Reactive props (:variant="expr") — BindChildProp (runtime helper with IgnoreCase, symmetric SetChildProp). Generator issues BindChildProp(child, "propName", () => expr).
  • Scalar conversion: string → bool/int/float/enum/string through ConvertScalar (enum via Enum.Parse).
  • Diagnostics: LogWarning/ LogError in the dev build in case of conversion errors.

2.3 ✅ Lifecycle: 4 hooks

Status: implemented (partially).
File: sus-core/Runtime/SusComponent.Lifecycle.cs

Hook SusVue HookStatus
Created() setup() /constructor
Mounted() onMounted()
Updated() onUpdated()✅ (each frame)
Unmounted() onUnmounted()

2.4 🔥 Provide / Inject - analogue of Vue provide / inject

Status: ❌ NOT IMPLEMENTED.
Vue equivalent:

js
// Parent:
provide('theme', themeRef)
// Child (any depth):
const theme = inject('theme')

Problem

Without Provide/Inject - prop drilling through the entire hierarchy. Theme, localization, game state, router - everything needs to be dragged through each intermediate component.

csharp
// Now - antipattern:
<sus:App>
  <sus:MainMenu theme={theme} locale={locale} /> // ← drag theme
    <sus:SettingsPanel theme={theme} /> // ← drag again
      <sus:ThemeToggle theme={theme} /> // ← and again

What to do

  1. Add to SusComponent:
csharp
 // Provide value - visible to all children
 protected void Provide<T>(string key, T value);

 // Get value - searches up the hierarchy
 protected T Inject<T>(string key);

 // Check availability
 protected bool HasInjection(string key);
  1. Storage - Dictionary<​string, object> on SusComponent.
  2. Inject looks for itself → parent → parent.parent → ... until it finds or root.
  3. For reactive values ​​- put Prop<​T>, the consumer subscribes.

Files:

  • sus-core/Runtime/SusComponent.cs - methods Provide / Inject
  • sus-core/Runtime/SusComponent.Inject.cs - implementation in a separate partial
  • sus-core/Docs/11-api-reference.md — Provide/Inject section

Priority: 🔥


2.5 ⚠️ BeforeMounted() / BeforeUnmounted()

Status: ❌ NOT IMPLEMENTED.
Vue equivalent: onBeforeMount(), onBeforeUnmount()

Problem

Unmounted() called in OnDetachFromPanelHandler after detach. In Vue beforeUnmount - to to clear resources while the DOM is still alive. Both are needed:

csharp
// Desired order:
// 1. BeforeMounted() - before Build(), but after the constructor
// 2. Build() + LoadCompanionStyleSheets()
// 3. Mounted() - after attaching to the panel
// ...
// 4. BeforeUnmounted() - BEFORE removal from the panel (clearing subscriptions, timers)
// 5. Unmounted() - AFTER removal (final cleanup)

What to do

  1. Add virtual void BeforeMounted() - called in the constructor between Created() And Build().
  2. Add virtual void BeforeUnmounted() - called in OnDetachFromPanelHandler to _updateItem.Pause().

File: sus-core/Runtime/SusComponent.Lifecycle.cs

Priority: ⚠️


2.6 📦 Declarative props — defineProps with default/validate

Status: ❌ NOT IMPLEMENTED.
Vue equivalent: defineProps({ title: { type: String, default: "Untitled" } })

Problem

Fields in <​script> - just C# fields. There is no difference between “this is a prop” and “this is an internal state”. There are no default values ​​through the attribute, no validation.

What to do (future)

Add attribute [Prop(default: ..., validator: ...)] or [UxmlAttribute] for generation. Now - document the current approach (public fields = props).

Priority: 📦


3. Template directives

3.1 ✅ v-if, v-show, v-for, :text, @click - reactive bindings

Status: implemented (July 1, 2026 - bindings transferred to ReactiveEffect).
Files: SusComponent.Bind.cs, BuildMethodGenerator.cs, TemplateParser.cs

From July 1st everything Bind* methods ( BindText, BindShow, BindVisibility, BindClass, BindList, BindListFor) work through a common reactive primitive ReactiveEffect:

  • Each binding is performed under DependencyTracker.Track — auto-collection of dependencies (Prop/Computed)
  • Subscription to invalidation - changing any Prop/Computed causes the binding to be reapplied
  • Batching through ScheduleBindUpdate (one pass per frame), deduplication via HashSet<Action>
  • Auto-clearing of subscriptions when detaching a component (DisposeAllBindings)

3.2 🔥 v-model - two-way binding

Status: ❌ NOT IMPLEMENTED.
Vue equivalent: <input v-model="text" />

Problem

The most common pattern in forms. Now:

csharp
// Manual subscription - boilerplate for each TextField:
var field = new TextField();
field.value = name.Value;
field.RegisterCallback<ChangeEvent<string>>(evt => name.Value = evt.newValue);

What to do

  1. Add BindModel V SusComponent.Bind.cs:
csharp
 // Two-way binding for TextField, Slider, Toggle, DropdownField
 protected void BindModel(TextField field, Prop<​string> prop);
 protected void BindModel(Slider slider, Prop<​float> prop);
 protected void BindModel(Toggle toggle, Prop<​bool> prop);
 protected void BindModel(DropdownField dropdown, Prop<​string> prop);
  1. In the compiler: parse v-model="propName" → generate BindModel(el, propName).

Files:

  • sus-core/Runtime/SusComponent.Bind.cs - new methods BindModel
  • sus-core/Editor/SourceGenerator/Parser/TemplateParser.cs - parsing v-model
  • sus-core/Editor/SourceGenerator/Generator/BuildMethodGenerator.cs - generation BindModel

Priority: 🔥


3.3 🔥 v-else-if / v-else

Status: ❌ NOT IMPLEMENTED.
Vue equivalent:

html
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>C</div>

Problem

Now we have to duplicate the condition with inversion:

html
<!-- Antipattern - duplication of logic: -->
<ui:VisualElement v-if="State == 'A'" />
<ui:VisualElement v-if="State != 'A' && State == 'B'" />
<ui:VisualElement v-if="State != 'A' && State != 'B'" />

WITH v-else-if/ v-else - a clean chain, the compiler will construct the conditions itself.

What to do

  1. TemplateParser is already building a flat tree of siblings. When found v-else-if / v-else on sibling → link to previous v-if to the group.
  2. BuildMethodGenerator generates a chain if/else if/else.

Files:

  • sus-core/Editor/SourceGenerator/Parser/TemplateParser.cs — grouping v-if/v-else-if/v-else
  • sus-core/Editor/SourceGenerator/Generator/BuildMethodGenerator.cs — chain generation

Priority: 🔥


3.4 ⚠️ :class object syntax

Status: ❌ Single BindClass only.
Vue equivalent: :class="{ active: isActive, error: hasError }"

Problem

Now :class="'my-class'" - line. For several conditional classes - manual addition:

xml
<!-- The current approach is 2 separate directives (not supported by the compiler): -->
<ui:VisualElement :class="'active'" :class="'error'" />

What to do

  1. TemplateParser parses :class="{ active: isActive, error: hasError }" → list of pairs (className, condition).
  2. BuildMethodGenerator generates several BindClass(el, "active", () => isActive), BindClass(el, "error", () => hasError).

Files: the same compiler ones as for directives.

Priority: ⚠️


3.5 ⚠️ Event modifiers: .stop, .prevent, .once

Status: ❌ NOT IMPLEMENTED.
Vue equivalent: @click.stop="handler", @click.once="handler"

What to do

  1. TemplateParser parses @click.stop="handler" → flags stopPropagation, once in the AST node.
  2. BuildMethodGenerator generates a wrapper:
csharp
 // @click.stop="handler" →
 el.RegisterCallback<ClickEvent>(evt => {
     evt.StopPropagation();
     handler();
 });

 // @click.once="handler" →
 bool _once = false;
 el.RegisterCallback<ClickEvent>(evt => {
     if (!_once) { _once = true; handler(); }
 });

Files: TemplateParser.cs, BuildMethodGenerator.cs

Priority: ⚠️


4. Built-in components

4.1 📦 KeepAlive - DOM tree caching

Status: ❌ NOT IMPLEMENTED.
Vue equivalent: <KeepAlive><component :is="view" /></KeepAlive>

Relevant for sus-router. Push/Replace navigation hides the screen (display: none) instead of being removed from the DOM. Mounted() is not called again, the Prop state is preserved.

Priority: 📦


4.2 📦 Transition / TransitionGroup - animations

Status: ❌ NOT IMPLEMENTED.

UITK supports animations via experimental.animation. You can wrap fade/slide in a SusTransition component.

Priority: 📦


5. Application infrastructure

5.1 SusApp — analog of createApp().use().mount()

Status: ✅ Implemented (Sharq.Core.SusApp).

Vue equivalent: const app = createApp(App).use(router).use(pinia).mount('#app')

Fluent bootstrap: TSS → tokens → configure/router → mount → theme. Documented entry point; ensures OverlayHost. See 00-integration.md (Critical + Step 6).

csharp
SusApp.Create(uiDocument)
    .UseTheme(SusTheme.Dark)
    .Mount<App>();

Older notes below about “NOT IMPLEMENTED” are obsolete — keep this section as the source of truth.


5.2 ✅ Devtools - SusComponent inspector

Status: ✅ IMPLEMENTED (SusDevtools).

Attach with SusDevtools.Attach(root) — inspects the SusComponent tree, Prop values, and allows runtime edits. Complements UI Toolkit Debugger (which only shows VisualElements).


5b. Unity 6+ UITK - unused native features

Date: June 30, 2026 - separate audit
Conclusion: the project practically does not use Unity 6+ UI Toolkit features. Below is what Unity provides natively and how it can be replaced or supplemented in sus-core.

Tested features (all NOT USED)

#Unity 6+ featureWhere is it usedProject status
1dataSource on VisualElementNative data-binding: el.dataSource = vm❌ Nowhere
2DataBinding / SetBinding() el.SetBinding("text", new DataBinding(...))❌ → ✅ BindModelNative
3BindingMode.ToTarget / .ToSource / .TwoWayTwo-way binding without code❌ → ✅ BindModelNative
4PropertyPath PropertyPath.FromName("Unit.Name") - nested properties❌ Nowhere
5[UxmlAttribute]Expose properties in UI Builder✅ Auto-generated companion property from [CreateProperty]
6CustomBindingCustom binding types❌ Nowhere
7USS :hoverNative hover: .btn:hover { ... }❌ Everything via C# MouseEnterEvent
8USS :focusFocus: .input:focus { ... }❌ Nowhere
9USS :enabled / :disabledStates: .btn:disabled { opacity: 0.5; }❌ Nowhere
10USS transition transition: background-color 0.2s ease;❌ Nowhere (experimental in Unity 6)
11TwoPaneSplitViewBuilt-in split control❌ Nowhere
12MultiColumnListView / MultiColumnTreeViewTables with sorting❌ Nowhere
13RuntimePanelUtilsUtilities: ScreenToPanel, coordinates❌ Nowhere
14PanelSettings.themeStyleSheetTopic via .tss (we replaced it with root.styleSheets.Add())❌ Consciously refused

5b.1 🔥 USS :hover / :focus / :disabled - replace C# handlers

Problem

Now hover and focus are done through C#:

csharp
el.RegisterCallback<MouseEnterEvent>(OnHoverEnter);
el.RegisterCallback<MouseLeaveEvent>(OnHoverLeave);
// → AddToClassList("hovered") / RemoveFromClassList("hovered")

This is a boilerplate for each interactive element. Unity USS supports :hover from 2022.2 - you can remove 90% of C# handlers.

What to do

css
/* Instead of C# - pure USS: */
.sus-button {
    background-color: var(--sus-btn-primary-bg);
    transition: background-color 0.15s ease;  /* experimental in Unity 6 */
}
.sus-button:hover {
    background-color: var(--sus-btn-primary-bg-hover);
}
.sus-button:disabled {
    opacity: 0.4;
}
.sus-input:focus {
    border-color: var(--sus-input-border-focus);
}

Effect: delete MouseEnterEvent/ MouseLeaveEvent from each component, where the hover is purely visual (color/background change). C# remains only for logic (tooltips, previews).

Priority: 🔥 - gives instant winnings on all interactive components


5b.2 ⚠️ Native data binding vs our BindText/BindShow

What does Unity provide?

csharp
// Unity native:
var label = new Label();
label.dataSource = myViewModel;
label.SetBinding("text", new DataBinding {
    bindingMode = BindingMode.ToTarget,
    dataSourcePath = PropertyPath.FromName("UnitName")
});

// Our current approach:
BindText(label, () => unitName.Value);

Comparison

CriterionUnity native bindingOur BindText/BindShow
CodeMore boilerplateIn short (1 line)
Double-sidedBindingMode.TwoWay - out of the boxNeed manual BindModel
INotifyBindablePropertyChanged✅ Prop is ALREADY implementing✅ Prop is ALREADY implementing
UI Builder✅ Sees bindings❌ Doesn't see
Auto-track dependencies❌ No (property change only)✅ Computed + DependencyTracker
watchEffect❌ Incompatible✅ Planned
computed❌ Incompatible✅ Implemented

Solution: DO NOT replace, but supplement

Unity native binding can't track dependencies (no computed, No watchEffect) is a fundamental limitation. Our jet system ( Prop<​T> + Computed<​T>) remains the core.

What to do: add optional BindNative() for cases where computed is not needed:

csharp
// Fast way (Unity native) - for simple props:
el.SetBinding("text", new DataBinding {
    dataSource = myProp,  // Prop<​T> : INotifyBindablePropertyChanged
    dataSourcePath = PropertyPath.FromName("Value"),
    bindingMode = BindingMode.TwoWay // ← v-model out of the box!
});

// Slow way (ours) - for computed/watchEffect/complex logic:
BindText(el, () => computedValue.Value);

This gives v-model for free via BindingMode.TwoWay.

Priority: ⚠️


5b.3 🔥 [UxmlAttribute] — visibility of props in UI Builder

Problem

SusComponent already has [UxmlElement] (auto-generated). But props ( Prop<​T>) are not visible in UI Builder because there are no [UxmlAttribute].

What to do

IN Prop<​T> add attribute [UxmlAttribute] for Value (or a separate property):

csharp
[UxmlElement]
public partial class SusButton : SusComponent
{
    [UxmlAttribute("text")]
    public Prop<​string> Text { get; set; } = new("Button");
}

This will allow you to change props directly in UI Builder without code.

Priority: 🔥 - Unlocks UI Builder for designers


5b.4 Summary: what to implement from Unity 6+

#FeaturePriorityWhat will give
1USS :hover / :focus / :disabled🔥Remove C# hover handlers from all components
2[UxmlAttribute] on Prop🔥Visibility of props in UI Builder
3USS transition (experimental)⚠️Smooth hover/focus animations on USS
4SetBinding + BindingMode.TwoWay BindModelNativev-model out of the box for simple props
5TwoPaneSplitView and other controls📦Fewer controls
6RuntimePanelUtils.ScreenToPanel📦Correct coordinates for OverlayHost

DO NOT implement (fundamentally incompatible with our reactivity):

  • Complete transition to Unity native binding - no computed/watchEffect/tracking
  • PanelSettings.themeStyleSheet - has already been replaced by root.styleSheets.Add()

6. Technical debt (bugs/inconsistencies)

6.1 🔧 SusThemeService - no reactive Current

File: sus-core/Runtime/SusThemeService.cs
Problem: SetTheme changes CSS classes, but components cannot subscribe to the theme change. No Prop<​SusTheme>.

Correction:

csharp
public class SusThemeService
{
    public static readonly SusThemeService Instance = new();
    public static Prop<​SusTheme> Current { get; } = new(SusTheme.Dark);

    private SusThemeService() { }

    public void SetTheme(VisualElement root, SusTheme theme)
    {
        Current.Value = theme;
        // ... existing class toggle logic ...
    }
}

Now any component can Watch(SusThemeService.Current, ...).


6.2 SusResolutionService — removed

Status: deleted. Do not reintroduce High/Low resolution classes. Screen-size adaptation is only SusBreakpointService + .breakpoint-* token overrides.

The former resolution feed path is preserved on breakpoints: every component GeometryChanged pushes cascadeRoot.resolvedStyle.width onto SusBreakpointService.Attach(cascadeRoot).Update(width). See 06-responsive.md.


6.3 🔧 dismissOnClickOutside in OverlayHost - not implemented

File: sus-core/Runtime/OverlayHost.cs:29
Problem: Parameter dismissOnClickOutside is accepted, but the click-out is not tracked.

Correction:

  1. Register ClickEvent on the root panel.
  2. When clicked, check whether the target of the click is inside the overlay element? If not → call OnDismiss and delete.
  3. Register/cancel only when there is at least one overlay with dismissOnClickOutside: true.

6.4 🔧 HasSlotContent() - no slot check from code

File: sus-core/Runtime/SusComponent.Slots.cs
Problem: Cannot be verified HasSlot("header") from C#. Vue: useSlots().header.

Fix: Add method:

csharp
protected bool HasSlotContent(string slotName) =>
    _registeredSlots.ContainsKey(slotName);

6.5 🔧 SusBreakpointService - redundant per-component instance

Files: sus-core/Runtime/SusComponent.cs:129, SusBreakpointService.cs
Problem: Each component creates its own SusBreakpointService. For 200 components - 200 services that duplicate the same breakpoint.

Correction: Make shared to root (like SusThemeService), or at least a singleton on panel.visualTree:

csharp
// Option: cache on rootVisualElement via userData
var bp = root.userData as SusBreakpointService;
if (bp == null) {
    bp = new SusBreakpointService();
    root.userData = bp;
}

7. Consolidated priority list

🔥 Critical (block development)

  • [x] 1. v-model - two-way binding for TextField, Slider, Toggle, DropdownField. Affects: SusComponent.Bind.cs, TemplateParser.cs, BuildMethodGenerator.cs.
  • [x] 2. Provide / Inject — remove prop drilling. Topic, locale, game state - available at any depth without transmission through the chain. Affects: SusComponent.cs (new partial), SusComponent.Inject.cs.
  • [x] 3. v-else-if / v-else - pure conditional chains without duplicating conditions. Affects: TemplateParser.cs, BuildMethodGenerator.cs.
  • [ ] 4. USS :hover / :focus / :disabled - replace C# MouseEnterEvent/ MouseLeaveEvent to native USS pseudo-classes. Remove boilerplate from each interactive component. Affects: everything .uss, all components.
  • [x] 5. [UxmlAttribute] on Prop** - visibility of props in UI Builder. Affects: ** Prop.cs, auto-generation.

⚠️ Important (makes development easier)

  • [x] 6. watchEffect(fn) - less boilerplate: one subscription to several Props instead of N separate Watches. Affects: SusComponent.cs, Docs/03-reactivity.md.
  • [x] 7. :class object syntax - several conditional classes on one element. Affects: TemplateParser.cs, BuildMethodGenerator.cs.
  • [x] 8. Event modifiers (.stop, .once) - frequent patterns in handlers. Affects: TemplateParser.cs, BuildMethodGenerator.cs.
  • [x] 9. BeforeMounted() / BeforeUnmounted() is the correct order for clearing resources. Affects: SusComponent.Lifecycle.cs, SusComponent.cs.
  • [x] 10. SetBinding + BindingMode.TwoWay - v-model for simple props via Unity native binding. Affects: SusComponent.Bind.cs, optional.

🔧 Technical debt (fix in current code)

  • [x] 11. SusThemeService - add Prop<​SusTheme> Current (topic reactivity). File: SusThemeService.cs.
  • [x] 12. SusResolutionServiceremoved; use breakpoints only. Files: deleted SusResolutionService.cs; geometry hook keeps SusBreakpointService only.
  • [x] 13. dismissOnClickOutside - implement logic in OverlayHost. File: OverlayHost.cs.
  • [x] 14. HasSlotContent(string name) - slot check from C#. File: SusComponent.Slots.cs.
  • [x] 15. SusBreakpointService - make it generic for root instead of per-component. Files: SusComponent.cs, SusBreakpointService.cs.

📦 Future (not blockers)

  • [x] 16. KeepAlive - caching screens when navigating via SusKeepAlive (sus-core) + keepAlive:true in SusRouteConfig (sus-router).

  • [x] 17. Transition — animation system (Fade, SlideLeft, SlideRight) in SusRouteTransition (sus-router).

  • [x] 18. Declarative propsdefineProps with default/validate.

  • [x] 19. SusApp SusApp.Create().UseTheme().UseRouter().Mount<T>() / Run() (see 00-integration Critical).

  • [x] 20. Devtools-panel - inspection of SusComponent, Prop values, tree (F12-panel).

  • [x] 21. Deep reactivityReactive<T> for ViewModel (now - document the “every field = Prop” pattern).


Application: Vue pivot table → sus-core

Vue Featuresus-core equivalentStatus
ref() Prop<​T>
computed() Computed<​T> + DependencyTracker
watch() Watch(prop, callback) WatchHandle
watchEffect() WatchEffect(Action fn)
reactive(obj)"Each field = Prop" (pattern documented)
defineProps[CreateProperty(default: X, validate: "...")]
defineEmits Emit<TEvent>() / On<TEvent>()
provide / inject Provide<T> / Inject<T>
Lifecycle (8 hooks)6 hooks (Created/BeforeMounted/Mounted/Updated/BeforeUnmounted/Unmounted)⚠️
defineExpose— (C# reference)⚠️
v-model BindModel (manual two-way, without PropertyPath)
v-if / v-else-if / v-else v-if / v-else-if / v-else
v-show BindShow
v-for + :key BindList<T> / BindListFor<T>
:text BindText
:class (object):class="{ cls: cond }" BindClass
@click / @event RegisterCallback
Event modifiers (.stop/.once)@click.stop / @click.once
v-once / v-memo
<Teleport> OverlayHost ( AddToOverlay / RemoveFromOverlay)
<Transition> / <TransitionGroup> SusRouteTransition (Fade/SlideLeft/SlideRight)
<KeepAlive> SusKeepAlive + SusRouteConfig.KeepAlive
<Suspense>
Async components
app.use(plugin)
app.config sus.config.json (compile-time)⚠️
Composables (use*())— (static helpers / DI)⚠️
DevtoolsSusDevtools (F12 - tree + Prop editor)

Unity 6+ features (not Vue, but important)

Unity featureStatusPriority
USS :hover / :focus / :disabled❌ Everything through C# events🔥
USS transition (experimental)❌ Not used⚠️
[UxmlAttribute]✅ Comanion property is generated by auto🔥
dataSource + SetBinding + DataBinding BindModelNative (optional, for simple props)
BindingMode.TwoWay (v-model native) BindModelNative — SetBinding c TwoWay
Unity TwoPaneSplitView❌ Not used📦
RuntimePanelUtils❌ Not used📦

Open-core: Core & Router MIT · Kit & Game commercial