Design tokens
Explore live semantic tokens and switch the accent preset.
Why tokens
A component should never name a color. When --primary-default resolves through --brand-base, retheming is a one-line change in palettes.css — and every button, badge, and focus ring in the app follows. Tokens also carry the design decisions: status families publish a *-text stop that clears 4.5:1, so a warning label is always the family's legible ink, never its fill.
Color is authored in OKLCH, where lightness is perceptual: ramps read evenly, and a theme flip is a lightness move that preserves hue and chroma. Theme-varying tokens are written once as light-dark(light, dark) pairs — there is no parallel dark block to keep in sync.
Reading a token
Tokens resolve in one direction only: semantic → palette → role. This is the chain behind the accent of the page you are reading:
:root { --primary-default: var(--brand-base); /* semantic → brand role */ --primary-hover: var(--brand-strong); /* darker stop for hover */ --primary-foreground: var(--brand-on); /* ink that clears 4.5:1 */ --accent: var(--brand-wash-strong); /* nav pills, chips, tabs */ --ring: var(--brand-focus); /* focus indicator */}Semantic families
Four families cover the canvas. Anything sitting on the canvas flips with the theme; anything that is its own surface holds its verified contrast pair.
Surface
--background
Canvas behind everything
--card
Raised surfaces: cards, menus, sheets
--secondary
Quiet tinted surfaces
--muted
Wells, tracks, hover fills
--border
Hairline rules between surfaces
--input
Field borders
Ink
--foreground
Primary text and icons
--muted-foreground
Secondary copy, metadata
--placeholder-foreground
Field placeholders
--accent-foreground
Links, selected nav, emphasis
Status
--success
Signed gains, healthy state
--warning
Attention, aging, pending
--destructive
Errors, loss, irreversible
--ring
Focus indicator, active tabs
Accent
--primary-default
The earned action fill
--primary-hover
Fill hover — darker stop
--primary-foreground
Ink on the fill
--accent
Wash: nav pills, chips, active tabs
--accent-wash
Quietest tinted surface
Brand presets
The accent identity lives in one file as 8 presets. Each ships an 11-stop ramp plus role picks for fills, washes, focus, and ink. Switching happens at runtime with a single attribute on the document element: the role tokens (--primary, --accent, --ring) are derived once at :root, so that is where the ramp gets swapped.
<html lang="en" data-brand="sky">
{/* every accent token on the page now resolves from sky */}
</html>Shipped default. Light ink on the action fill.
Light ink on a teal fill the status palette never approaches.
Light ink on an olive fill; 27° from warning, so mind the harmony strip.
Light ink on a muted terracotta, separated from warning by chroma.
Light ink on a deep violet action fill.
Light ink on a vivid magenta action fill.
Light ink on an orchid fill, 71° clear of the danger family.
The one navy-ink fill: luminous, and told from Azure by lightness.
Reference table
The accent chain, token by token, as shipped in globals.css:
| Token | Resolves to | Used by |
|---|---|---|
--primary-default | --brand-base | Primary buttons, checked controls |
--primary-hover | --brand-strong | Button hover state |
--primary-foreground | --brand-on | Labels on the accent fill |
--accent | --brand-wash-strong | Active nav, chips, tabs |
--accent-wash | --brand-wash | Quiet accent surfaces |
--ring | --brand-focus | Focus indicators |
- Token
--primary-default- Resolves to
--brand-base- Used by
- Primary buttons, checked controls
- Token
--primary-hover- Resolves to
--brand-strong- Used by
- Button hover state
- Token
--primary-foreground- Resolves to
--brand-on- Used by
- Labels on the accent fill
- Token
--accent- Resolves to
--brand-wash-strong- Used by
- Active nav, chips, tabs
- Token
--accent-wash- Resolves to
--brand-wash- Used by
- Quiet accent surfaces
- Token
--ring- Resolves to
--brand-focus- Used by
- Focus indicators
A note on dark mode: washes are mixed from the live canvas with color-mix(), so they track the background in both themes. The theme toggle in the header shows the pair in action.