State History
guideEvery state remembers its value before the last change. previous(), direction() and :changed() turn that into animations that know which way a value moved.
On this page · 6 sections+−
previous()
previous(--count) is the value before the last effective change, and the initial value until then. It shares the state’s history, so it costs no extra storage.
direction()
direction(--count) is 1, 0 or -1: whether the last change of a number or dimension state went up, nowhere or down. For other types it is unknown.
:changed()
:changed(--count) matches from the frame after a change until the animations it started have finished, so the same animation replays on every change.
An animated counter
.counter { --count: state(0); --last: previous(--count); /* --count as a position in the strip */ --pos: state(6);}.plus:on(click) { --count: increment(1); --pos: increment(1);}.minus:on(click) { --count: decrement(1); --pos: decrement(1);}output { animation-duration: 240ms;}.counter:changed(--count) output { animation-name: when( direction(--count) > 0, slide-up, slide-down );}.trail span:nth-var(--pos) { border-color: oklch(0.55 0.2 35); color: oklch(0.55 0.2 35);}.trail span:nth-var(--pos) ~ span { display: none;}| name | value | previous | changes |
|---|---|---|---|
| Waiting for the preview to start. | |||
- Interact with the preview to record events.
Keyframes
@keyframes slide-up { from { translate: 0 40%; opacity: 0; }}@keyframes slide-down { from { translate: 0 -40%; opacity: 0; }}Compared with JavaScript
The JavaScript version removes the class, forces a reflow with offsetWidth and adds the class again to restart the animation. :changed() replaces that trick and also waits for the animation to finish before it stops matching.
Turn the animation off for people who ask for less motion. The number still updates instantly.
@media (prefers-reduced-motion: reduce) { .counter:changed(--count) output { animation: none; }}Limits
- Only one step of history is kept. A bounded
history(--x, n)is planned, not shipped. - An infinite animation keeps
:changed()matching until the next change. - In very large pages, finding the animations a change started costs roughly running animations × page size in Chromium.