Skip to content

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:

globals.csscss
: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.

layout.tsxtsx
<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
    --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.