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

History and motion

A level from 0 to 20 with Up, Down, +5 and Reset. The previous value and the direction of the last change are shown as text; the bar width transitions natively; the value pulses (or dips) on every change via :changed(). Reduced motion turns both off.

Live previewreal runtime
View source

How it works

  • increment()lines 3, 5Adds a step to a number or dimension state. In an :on() rule it is the mutation --count: increment(1).
  • decrement()line 4Subtracts a step from a number or dimension state. In an :on() rule it is the mutation --count: decrement(1).
  • set()line 6Sets a state to a value. In an :on() rule it is a mutation of the state on the left, --x: set(5), with typed expressions.
  • previous()line 10The value of a state before its last effective change.

previous(), direction(), :changed() with native transitions and keyframes, rapid repeated actions and bounds.

Left to native HTML and CSS

transition, @keyframes, @media (prefers-reduced-motion: reduce).

What would normally need JavaScript

A counter with history and restartable animations.

Limitations

  • previous() is the value before the last change; intermediate values of a burst are not kept.
  • Reset to the same value (5 → 5) is not a change: nothing animates.
  • No JavaScript animation runtime: motion is CSS only.

Accessibility

The value is in a polite live region. Motion is off under reduced motion. Not verified with screen readers.

Implementation

2 files · 24 lines
style.cssreactive
/* ---- state and actions (0–20) ---------------------------------------------------------------------- */.meter { --v: state(5); }.meter .up { on: click(increment(--v)); attr: disabled when(--v >= 20); }.meter .down { on: click(decrement(--v)); attr: disabled when(--v <= 0); }.meter .jump { on: click(increment(--v, 5)); attr: disabled when(--v > 15); }.meter .reset { on: click(set(--v, 5)); }/* ---- history: previous() and direction() as real DOM text ------------------------------------------ */.meter .cur { text: var(--v); }.meter .prev { text: previous(--v); }.meter .dir { text: direction(--v); }.meter:when(direction(--v) > 0) .word { text: "went up"; }.meter:when(direction(--v) < 0) .word { text: "went down"; }/* ---- motion: native CSS only ---------------------------------------------------------------------- */.meter .bar { width: calc(var(--v) * 5%); transition: width 300ms ease-out; }.meter:changed(--v) .cur { animation: pulse 400ms ease-out; }.meter:changed(--v):when(direction(--v) < 0) .cur { animation-name: dip; }@keyframes pulse { from { transform: scale(1.3); color: #166534; } }@keyframes dip { from { transform: scale(0.8); color: #b42318; } }@media (prefers-reduced-motion: reduce) {  .meter .bar { transition: none; }  .meter:changed(--v) .cur { animation: none; }}
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/history-motion/. 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.

Related

Multi-step wizard

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.

0.8 syntaxfocus:attr:increment()decrement()

Interactive quiz

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").

0.8 syntaxfocus:attr::when():nth-var()

Live form preview

A profile form (display name, role, accent, email notifications) and a preview card that follows every change immediately. Native reset restores everything. There is no backend: the Save button is disabled, so neither the button nor Enter submits, and nothing is stored (the test checks storage and cookies).

0.8 syntaxbind::when()attr:text: