Foundations

Design tokens

Tokens are the smallest decisions in the system — color values, type sizes, spacing steps, motion durations. Every component consumes them; nothing hard-codes.

Documentedby Derek Fidler

What design tokens are

A token is a named value that stands in for a primitive. Instead of typing #0a0e1a in fifteen places, we define --foreground once and let every component reference it. When the brand evolves, we update the token; the system follows.

Tokens have two layers. Primitives are raw values (HSL triples, rem sizes, ms durations). Semantic tokens name a role (--background, --muted-foreground) and reference a primitive. Components only consume semantic tokens.

Categories

  • Color — surfaces, text, borders, status. Documented at Color.
  • Typography — font families, sizes, line heights, letter spacing. Documented at Typography.
  • Space— 4pt scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. Tailwind's default spacing scale, used as-is.
  • Radius--radius base of 0.5rem, with derived sm, md, lg, xl steps.
  • Motion — durations and easings. Documented at Motion.

Where they live

Token primitives are CSS variables defined in packages/ui/src/styles/tokens.css on :root (and again under .darkwhen dark mode lands). The portal's apps/portal/app/globals.css imports them and exposes them as Tailwind theme variables via @theme inline.

packages/ui/src/styles/tokens.css

:root {
  --background: 0 0% 100%;
  --foreground: 222 47% 11%;
  --muted: 210 40% 96%;
  --muted-foreground: 215 16% 47%;
  --border: 214 32% 91%;
  --radius: 0.5rem;
}

apps/portal/app/globals.css

@theme inline {
  --color-background: hsl(var(--background));
  --color-foreground: hsl(var(--foreground));
  --color-muted: hsl(var(--muted));
}

Consuming a token

From a Tailwind class:

tsx

<div className="bg-card text-foreground border-border">
  <p className="text-muted-foreground">Quiet text.</p>
</div>

From inline styles:

tsx

<span style={{ background: "hsl(var(--muted))" }}>…</span>

Do

Always reach for a semantic token before a primitive. If the right token doesn't exist, propose one rather than reaching for an arbitrary value.

Naming rules

  • Role, not value. --muted-foregroundtells you what it's for. --gray-500doesn't.
  • Lowercase, kebab-case. No camelCase, no SCREAMING_SNAKE.
  • No numeric suffixeson semantic tokens. Numbers go on primitives, if at all (we mostly skip them — Tailwind's spacing scale is enough).
  • Pair foregrounds with backgrounds. Every surface token has a matching -foreground sibling so contrast pairs are explicit.