A textarea, a counter, and a submit button - each declared with `bind` against one shared variable, no event wiring.
A textarea, a live character count that color-changes near the limit, and a submit button that disables when over the limit or empty. Three independent elements, no direct references between them - each one declares what it shows in terms of a single shared reactive variable.
<div class="char-counter">
<textarea rows="3" placeholder="Write a short message…"
_="bind $msg to me"></textarea>
<div class="char-counter-meta">
<span class="counter"
_="live put $msg.length + ' / 280' into me
bind .warn to $msg.length > 240
bind .over to $msg.length > 280"></span>
<button _="bind @disabled to $msg.length > 280 or $msg is empty">
Post
</button>
</div>
</div>
The whole pattern is four binds and one live:
bind $msg to me -- on the textarea
live put $msg.length + ' / 280' into me -- on the counter
bind .warn to $msg.length > 240
bind .over to $msg.length > 280
bind @disabled to $msg.length > 280 or $msg is empty -- on the button
bind $msg to me two-way-binds the textarea's value to a global reactive
variable $msg. Type into the textarea, $msg updates. Set $msg from
anywhere, the textarea updates. Now the counter and the button can both
subscribe to it without ever touching the textarea directly.
The counter combines one live and two binds:
live put $msg.length + ' / 280' into me - auto-tracks $msg.length
as a dependency and re-runs the put whenever it changes, so the text
always reflects the current count.bind .warn to $msg.length > 240 - reactively binds the .warn
class to a boolean expression. When the expression flips to true, the
class is added; when it flips to false, the class is removed. The
runtime tracks $msg.length as a dependency automatically.bind .over to $msg.length > 280 - same idea, second class for the
hard-limit state.The submit button has just one bind:
bind @disabled to $msg.length > 280 or $msg is empty - reactively
binds the disabled attribute to a boolean. When the message is empty
or over the limit, the attribute is present; otherwise it's removed. The
button has no idea the textarea exists - it only depends on $msg.The mental model: every UI element declares what it shows in terms of
reactive variables, and the runtime wires the dependencies for you.
bind covers four bindings:
| Form | Effect |
|---|---|
bind $var to me |
Two-way: variable ↔ input value |
bind .className to [boolean] |
One-way: class present iff expression true |
bind @attrName to [boolean] |
One-way: attribute present iff expression true |
bind @attrName to [expression] |
One-way: attribute set to expression result |
Adding a fourth element that depends on $msg (a preview, a word count,
an emoji indicator) requires zero changes to anything else - just write
its own bind or live against $msg.