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

persist()

declarationsince 0.9.0experimentalrequires runtime

Keeps a state across reloads, in localStorage or, for the tab’s session, in sessionStorage, under a key you choose.

On this page · 10 sections+

Syntax

--<state>: state(<initial>, persist("<key>" [, local | session]));

registry syntax: --state: state(initial, persist("key" [, local | session]))

Parameters

keyquoted <string>required
1 to 64 characters from A–Z, a–z, 0–9, _, ., : and -. Stored as rcss:<key>.
storagelocal or sessionoptional
Default local (localStorage). session uses sessionStorage.

Effect

No value. The state starts from its stored value, and later changes are stored.

Example

.prefs {  --theme: state(light, persist("docs-theme"));}.prefs .theme:on(click) {  --theme: cycle(light, dark);}.prefs .name {  content: var(--theme);}.prefs:when(--theme == dark) {  background: #1c1c1e;  color: #f2f2f2;}
state()Function · hover any red tokenMakes each matching element the owner of an independent state with an initial value.Reference →
Resultnative button
waiting for interaction

Correct and incorrect usage

correctA counter kept for the tab’s session, shared by two widgets

count.cssreactive
.pc { --n: state(0, persist("visits", session)); }.pc .inc:on(click) { --n: increment(1); }.pc .v { content: var(--n); }

Every .pc on the page owns its own state, but they share the key, so a click in one updates the others.

incorrectA key with a space

theme.cssreactive
.prefs { --theme: state(light, persist("my theme")); }

A compile error: keys use letters, digits and _ . : - only.

Behavior

  • Experimental: storage policies such as Safari’s private mode and partitioned storage in iframes still need manual testing.
  • In this site’s previews storage is unavailable, because they run in sandboxed iframes. The example works but starts over on reload. Copy it into a page of your own to see the value kept.
  • Initialization. A valid stored value first; otherwise the DOM source of a bind() (a valid attribute, or a control the user or the browser already edited); otherwise the declared initial value. The result is then written to the bound attribute or control.
  • Typed validation. Values are stored as JSON, {"t":type,"v":value,"u":unit}. A stored value is used only if it fits the declared type, and keywords must be identifiers or #hex colors, so stored text can never become url() or extra declarations. Anything else is ignored, with one note.
  • Coalesced writes. At most one setItem per key every 250 ms, and the last value always wins. Pending writes are flushed when the page is hidden (visibilitychange) or left (pagehide). A drag that updates a persisted position writes about 4 times per second.
  • Shared keys. States with the same key and storage share one value in the page. With localStorage, other tabs of the same origin follow through the storage event; applying such a change never writes back.
  • Failures. Unavailable storage (sandboxed iframes, some privacy modes), quota errors and unreadable values give one note. The state keeps working, without persistence.
  • With bind(), in either order, each at most once: state("", bind(value), persist("name")).

Supported contexts

  • A modifier of state(), wherever state() is allowed. A custom bundle needs the persist feature.

Limitations

  • Kept per browser profile and origin. It never synchronizes across devices or browsers.
  • A state bound to a password, one-time-code or payment field is never persisted (one diagnostic).
  • The library never clears storage: a key stays after its rule is removed. Remove it with localStorage.removeItem("rcss:<key>").
  • Only declared states are stored. Nothing is read from the DOM on its own.

Examples

Experimental · syntax may change between minor versions