Docs
Signals
Class props are signals. Read with label(), write with label(value), tween with yield* label(next, duration). Bind children with () => this.label().
A signal is a value that can change over time. Other values that read it update when it changes.
On a custom element, every public prop is a signal. Declare it with @initial and @signal on the class. Do not store animated state in a plain field or a hand-rolled createSignal on the class.
Three calls
The action depends on how many arguments you pass.
Read:
const value = this.label();Write immediately:
this.label('Hello');Tween (inside a generator):
yield* this.label('Hello', 0.3);Colors work the same after @colorSignal:
yield* this.textColor('#68ABDF', 0.4);Bind children to functions
Pass a function into JSX so the child keeps reading the signal:
this.add(
<Txt
text={() => this.label()}
fill={() => this.textColor()}
fontSize={() => this.textSize()}
/>,
);A one-shot text assignment that calls label() copies the string once. Later edits to label will not reach the Txt.
A signal can also compute from other signals:
const width = () => this.label().length * this.textSize() * 0.55 + this.textSize();Use that pattern when a chip or keycap must size to its string. Do not let flex measure the Txt. See Layout.
Class signals vs createSignal
createSignal from @vidova/core is for a local value inside a method. It is not a replacement for a class prop.
Forbidden for props and animated class state:
// Wrong. Not a class signal. Tweening it does not update the element.
const label = createSignal('Hello');Required:
@initial('Hello')
@signal()
public declare readonly label: SimpleSignal<string, this>;Do not cast a closure to SimpleSignal. It is not reactive and never animates.
Node properties are already signals. this.opacity(), this.scale(), and this.position() tween the same way. Do not declare a class field with those names. The field overwrites the node property.
How to wire props onto a class: Authoring. How to sequence tweens: Animation.
