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

Reactive Values

concept

Value functions produce values the stylesheet can’t compute on its own: randomness, the pointer, visibility, the viewport and re-evaluation over time or on events.

On this page · 8 sections+

Sources at a glance

FunctionReturnsUpdates when
random(min, max)A value of the input type, per elementNever, unless inside live() or on()
pick(a, b, …)One of the valuesNever, unless inside live() or on()
live(expr, interval)The value of exprEvery interval, default 1s, minimum 50ms
on(event, expr)The value of exprOnce when bound, then on each event on that element
mouse(x) mouse(y)A length in pxPointer moves, at most once per frame
visible(threshold)1 or 0Intersection changes
viewport(width)100dvw / 100dvhNatively, by the browser

These are the values’ behavior in ordinary declarations. Inside a mutation of an :on() rule they are read once, at the event: see below.

random() and pick()

random(min, max) gives a value between min and max, of the same type, per element and per call. An optional step makes the result a multiple of the step above min. pick() chooses one of its values.

confetti.cssreactive
.confetti {  rotate: random(-30deg, 30deg);  translate: random(0px, 400px, by 20px) 0;  background: pick(#f2542d, #57534e, #d9d5cf);}
random()Function · hover any red tokenA random value between min and max. The browser’s native random() handles the calls it supports; Reactive CSS runs the rest, and rolls a new value at each event inside a mutation.Reference →

Two syntaxes

SyntaxWhere it comes fromStep
random(<random-key>?, min, max, step?)The CSS Values 5 draft that Chromium and WebKit implementA plain third value: random(0px, 400px, 20px)
random([element-shared,] min, max [, by step])Reactive CSS, kept from an earlier draftby 20px

The third value is always a step, never an update interval. To re-roll over time, use live(random(…), 2s).

Native first

Reactive CSS leaves to the browser what the browser can do:

  • Native. A top-level call that the browser supports as written, when it gives a value per element, whatever its units or step. Reactive CSS checks this once with a two-element probe, not by browser name.
  • Native only. A call with a native sharing key (--name, element-scoped, property-scoped, fixed <n>) is not claimed at all where the browser supports it: no binding, no diagnostic. Where the browser lacks it, one diagnostic says so; Reactive CSS does not guess what the key means.
  • Reactive CSS runtime. by <step> and element-shared, which browsers reject; calls nested in live() or on(); calls inside mutations; and any call the browser cannot evaluate.

Browsers without native random() get the Reactive CSS polyfill for the whole list above, following the current draft for the plain step: a zero or negative step is ignored, and max below min gives min. Support differs between browsers and versions; see Browser Compatibility.

Note · random() in a custom property

--r: random(0, 1) behaves differently by engine. Handled natively, the custom property keeps the call as text, so each element that uses var(--r) rolls its own value. With the polyfill, the element that declares --r gets one value, and its descendants inherit it. To get the same result everywhere, put random() directly in the property that uses it.

Re-evaluating with live() and on()

live() re-evaluates its expression on a timer; random() and pick() inside it re-roll. on() does the same on an event.

.die {  --roll: on(click, random(1, 6, by 1));  counter-reset: n var(--roll);}.die::before {  content: counter(n);}.greet {  --word: on(click, pick("Hello", "Hi", "Hey"));}.greet::after {  content: var(--word);}
on()Function · hover any red tokenRe-evaluates an expression each time an event fires on the element. The value function, not the :on() event rule.Reference →
Resultnative buttons
waiting for interaction
Note · Three kinds of on

on(event, expr) is a value function that re-evaluates a value. :on(event) in a selector makes an event rule that changes state. Each is unambiguous in its context. The on() value function’s event list includes pointerdown, keyup, animationend, transitionend and toggle, among others.

particles.cssreactive
.particle {  translate: live(random(0px, 500px), 2s) live(random(0px, 300px), 2s);  transition: translate 1s ease;}
live()Function · hover any red tokenRe-evaluates an expression on an interval.Reference →

mouse()

mouse(x) and mouse(y) return the pointer position in px: viewport coordinates by default, or from the element’s top-left border-box corner with local. Both are 0px before the first pointer move.

.pad {  --x: mouse(x, local);  --y: mouse(y, local);}.dot {  translate: var(--x) var(--y);}
mouse()Function · hover any red tokenThe pointer position in px, in viewport coordinates or relative to the element.Reference →
Resultpointer only
waiting for interaction

Viewport mode is a shared source: one pointer listener for the page and one write per declaration, not per element. local mode measures each element and does not update on scroll alone.

visible()

visible() is 1 when at least threshold (default 0) of the element intersects the viewport, else 0.

reveal.cssreactive
.reveal {  opacity: calc(0.2 + visible(0.3) * 0.8);  transition: opacity 400ms;}
visible()Function · hover any red token1 while at least the threshold of the element intersects the viewport, else 0.Reference →

viewport()

viewport(width) and viewport(height) compile to native 100dvw and 100dvh, with no runtime. They are a convenience for nesting, as in random(0px, viewport(width)).

Inside mutations

In a mutation of an :on() rule, these values are snapshots taken at the event, not subscriptions:

FunctionIn a mutation
random(min, max, step)Rolled again at each event. Both step and by step work.
pick(a, b, …)Chosen again at each event
mouse(x), mouse(y)The event’s pointer position; pointer, mouse and drag triggers only
element(width), element(height)The rule element’s border-box size at the event
viewport(width), viewport(height)The window’s inner size, in px
attr(name, fallback)The rule element’s attribute at the event
dice.cssreactive
.die {  --face: state(1);}.die:on(click) {  --face: set(random(1, 6, 1));}
state()Function · hover any red tokenMakes each matching element the owner of an independent state with an initial value.Reference →

live(), on() and visible() are not available in mutations. Details and the event() fields: Event data.

Experimental · syntax may change between minor versions