The problem
Declarative bindings cover text, attributes, events, and form controls. Some libraries — especially web components — expose property APIs that do not round-trip through HTML attributes. numeric-text updates its display only via.update(), not setAttribute('value', …).
You need a reactive primitive that runs imperative sync logic when tracked signals change, with optional cleanup on re-run and teardown.
How Aero helps
Top-level$effect(() => { … }) in <script is:state> registers a side effect compiled to the runtime Effect class (the same scheduler used internally for DOM binds).
let bindings and reactive props inside the callback subscribe the effect. When those signals change, Aero runs any cleanup returned from the previous run, then runs the callback again.
Syntax
Invalid usage
When to use $effect vs bindings
Keep markup declarative; keep imperative sync in state scripts alongside handlers.
Cleanup pattern
Example: numeric-text wrapper
Web components that animate via.update(value, animated) need a side effect — attribute bindings alone set properties but skip animation semantics.
Use
$root.querySelector(...) to scope DOM lookups to the current page or component instance. bind:this for direct element refs is a planned follow-up.Related
- Bindings reference — declarative DOM wiring
- Reactive props —
Aero.bindable()for child-mutable props - CSP and compiled mounts —
$effectemits real functions in the mount preamble