Manual Style Tokens

Tokens

elements man style/tokens Read as markdown

Every visual decision in the system is a CSS custom property, a token. App CSS consumes tokens; it does not hardcode values. This is what makes rebranding, dark mode, and density adjustments one-line changes.

The three-tier model

Tokens are layered in three tiers, each building on the one below.

Tier 1: the scale (primitives). tokens/scale.css defines the raw, non-color scales: the type ramp, weights, leading and tracking, the spacing scale, radius, shadows, motion, z-index layering, breakpoint reference values, and container widths. These are the vocabulary: --space-4, --text-lg, --radius-md. They don't change between themes. See scale.

Tier 2: the semantic theme. themes/default.css defines the color ramps (one neutral + four status ramps in OKLCH) and then maps them onto semantic tokens: --bg, --ink, --rule, --accent, the status triples, focus rings, and the per-component slots. It also flips those semantic tokens for dark mode. This is the tier your app reads from. See color and components.

Tier 3: the optional brand theme. A theme file imported after the default repoints the accent block (and optionally a brand ramp) to color the whole system. themes/elements.css is the shipped example; themes/customer-example.css is the documented recipe. See theming.

scale.css        primitives        --space-4, --text-lg, --radius-md
   ↓
themes/default   semantic tokens   --ink → --neutral-900, --accent → --neutral-900
   ↓
brand theme      repoint accent    --accent → your brand ramp   (optional)

Consume semantic tokens, not raw ramps

The neutral and status ramps (--neutral-500, --danger-600, …) exist so the semantic tokens have something to point at. In app CSS, reach for the semantic token, not the ramp step:

/* Do this: follows the theme and dark mode automatically */
.notice {
  color: var(--ink-soft);
  background: var(--bg-soft);
  border: 1px solid var(--rule);
}

/* Not this: a raw ramp step won't flip in dark mode */
.notice {
  color: var(--neutral-700);
  background: var(--neutral-50);
}

--ink-soft is --neutral-700 in light mode and --neutral-300 in dark mode. Point at the semantic name and both come free. Reach past it to the ramp and you opt out of theming.

Where the tiers live

Tier File Layer
Scale primitives tokens/scale.css tokens
Semantic theme + colors + dark mode themes/default.css tokens
Brand theme (opt-in) themes/elements.css tokens

All three sit in @layer tokens, low in the cascade, so your app's own token overrides, placed later, always win. See overriding.

  • color: the neutral and status ramps and every semantic color token.
  • scale: type, spacing, radius, shadow, motion, and the rest of the primitives.
  • components: the per-component token slots.