Documents v0.9.0 · previews run the real runtime · experimental features are marked exp

Form mirror

Type in the field and the preview follows; the Clear button resets both.

Live previewreal runtime

Type a name, then press Clear.

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 --name from another rule does not change the field. For a field that must follow the state, use state("", 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
style.cssreactive
/* ---- 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(""); }
state()Function · hover any red tokenMakes each matching element the owner of an independent state with an initial value.Reference →

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.

Related

Draggable card

Move an element using declarative drag events.

experimental:on(drag)event()

Attribute sync

Keep real DOM attributes synchronized with CSS state.

experimental--attr-*toggle()