Form mirror
Type in the field and the preview follows; the Clear button resets both.
Type a name, then press Clear.
| name | value | previous | changes |
|---|---|---|---|
| Waiting for the preview to start. | |||
- Interact with the preview to record events.
How it works
- state()lines 3, 6Makes each matching element the owner of an independent state with an initial value.
- bind()line 6state(v, bind(value)) and state(v, bind(checked)) keep a form control and its own state equal in both directions. bind(<attribute>) reflects a state into one of its owner’s attributes.
- :on()lines 10, 16An event rule: when the event happens on the matched element (or inside it, for bubbling events), the rule’s state mutations run. The cascade picks one winning mutation per state, per trigger and per element.
- event()line 10Inside a mutation of an :on() rule: one field of the current event, such as event(local-x), event(key) or event(value), read once as a typed value.
- contentlines 12, 13On elements, a content value that reads state also becomes the element’s DOM text: a real text node, never HTML. The native declaration is kept as written.
A field copied into a state that its neighbors can read. Typing writes the field’s value into a state on the form, the preview reads that state as DOM text, and the Clear button resets the form and the state together.
state("", bind(value)) gives the input a state of its own, kept equal to the field in both directions. Only the input and its descendants can read that state, and an input has no children, so the field uses it to style itself. The preview beside it reads a second state on the section around the form, which an :on(input) rule fills with event(value). See the bind() reference.
Native CSS alternative
None: CSS can match :placeholder-shown or :invalid, but it cannot read a field’s text into another element.
Limitations
- The copy is one-way: a change of
--namefrom another rule does not change the field. For a field that must follow the state, usestate("", bind(value))on the field itself. - The value is not stored: a reload empties the field (unless the browser restores form fields, in which case the preview stays at “stranger” until the next keystroke).
Implementation
2 files · 16 lines/* ---- Reactive CSS -------------------------------------------------------------------------- *//* The state belongs to the section, which contains the form, the field and the preview. */.mirror { --name: state(""); }/* bind(value): the field and its own state stay equal, both ways. Only the field can read it. */.mirror input { --typed: state("", bind(value)); }.mirror input:when(--typed != "") { border-color: var(--accent); }/* Field → section: each keystroke copies the value up, so the preview beside the field can read it. */.mirror input:on(input) { --name: set(event(value)); }.mirror .name { content: when(--name == "", "stranger", ""); }.mirror:when(--name != "") .name { content: var(--name); }/* The Clear button resets the form natively; the reset rule clears the state with it. */.mirror:on(reset) { --name: set(""); }Source: site/src/catalog/api/form-mirror/. style.css from its Reactive CSS divider; the layout above it and the shared api.css run but are not shown.