Documents v0.9.0 · previews run the real runtime · experimental features are marked exp
Form interactions
An order form: a bound name with live text, pickup or shipping (the address field appears and becomes required for shipping), a quantity with native min/max validity, a terms checkbox that enables submit, native :valid/:invalid/:user-invalid styling and native reset. method="dialog": nothing is sent or stored.
bind:lines 3, 4, 5, 6Connects a native form control two-way with the nearest state of that name.
text:lines 9, 10, 11, 12Writes one reactive value into the element’s own DOM text: a real text node, never HTML.
attr:lines 15, 16, 17Derives one allowlisted HTML attribute of the matched element, such as disabled or aria-expanded, from a reactive value.
:when()line 10Matches elements for which the condition is true, with states resolved from that element.
Native form semantics driven by state: bindings, real text, conditional required, derived disabled, validity and reset.
Left to native HTML and CSS
Constraint validation (required, min/max), :valid, :invalid, :user-invalid, form reset, Enter submits, Space toggles checkboxes, method="dialog".
What would normally need JavaScript
Conditional required fields and enabling submit.
Limitations
One fixed message per state; validationMessage is not readable from CSS.
A hidden address keeps its value (it stays form data).
Reset restores the HTML defaults; the states follow after the reset event.
Accessibility
Labels on every control, native validation bubbles, required exposed natively. Not verified with screen readers.
Implementation
2 files · 17 lines
style.cssreactive
/* ---- state: bound to the native controls (form reset restores them) ------------------------------ */.order{--name:state("");--delivery:state(pickup);--qty:state(1);--terms:state(false);}.order#name{bind:--name;}.order.deliveryinput{bind:--delivery;}.order#qty{bind:--qty;}.order#terms{bind:--terms;}/* ---- real DOM text (text:) ---------------------------------------------------------------------------- */.order.who{text:var(--name);}.order:when(--name=="").who{text:"guest";}.order.qty-text{text:var(--qty);}.order.mode{text:when(--delivery==ship,"Ship","Pick up");}/* ---- derived attributes: the address applies only for shipping; submit waits for the terms ---- */.order.address-field{attr:hiddenwhen(--delivery==ship,false,true);}.order#address{attr:requiredwhen(--delivery==ship);}.order#submit{attr:disabledwhen(--terms,false,true);}
state()Function · hover any red tokenMakes each matching element the owner of an independent state with an initial value.Reference →
Source: the library's examples/form-interactions/. style.css from its first section comment; the layout above it runs but is not shown. Written in the 0.8 syntax (on:, bind:, text:, attr:), which 0.9.0 still runs; see Migrating from 0.8 for the 0.9 forms.
Two product cards. Each has a quantity (1–10, buttons and a number field), a color (radio group), gift wrapping (checkbox), a live total (real DOM text, 0.5.1) and a native reset. It is a local UI: no checkout, no network requests, nothing stored.
Four steps (Introduction, Preferences, Configuration, Review) plus a completion panel, with Back, Next and Finish, a step indicator, a progress bar and a "Step N of 4" status. Two independent instances. Nothing is submitted.
Three questions with three options each. The first answer counts. After answering, the quiz shows whether it was right, marks the correct option, shows an explanation and enables Next. At the end it shows the score and a message, and "Try again" starts a fresh session. Question data and the answer key are static HTML (class="correct").