Design Tokens Explained

Learn about Design Tokens: The Complete Guide
On this page

Design tokens are named, versioned design decisions stored as key-value pairs. The widely-taught three-tier model splits them into global tokens (raw palette), semantic tokens (intent-mapped aliases), and component tokens (per-component wiring). DESIGN.md expresses all three tiers through its colors, typography, spacing, rounded, and components frontmatter keys plus a {reference} syntax for aliasing.

What design tokens are#

A design token is a name-value pair that replaces a hardcoded value in your codebase with a shared, documented decision. Instead of scattering #3B82F6 across forty CSS files, you define it once as tertiary: "#3B82F6" and reference the name everywhere.

Tokens let you:

  • Update a value in one place and propagate it to every consumer.
  • Communicate intent: tertiary tells a reader this color is for interactive elements, #3B82F6 does not.
  • Export to multiple targets (CSS custom properties, Tailwind config, W3C DTCG) from a single source of truth.

The three-tier model#

Most design systems and tooling tutorials describe a three-tier hierarchy:

Tier 1: Global tokens (also called primitive or raw tokens)#

Global tokens are the raw palette. They have no opinion about where a value should be used; they just enumerate what exists.

# Global tier: every value the system knows about
colors:
  blue-500: "#3B82F6"
  blue-700: "#1D4ED8"
  gray-50:  "#F9FAFB"
  gray-900: "#111827"
  red-600:  "#DC2626"

These are stable and change rarely. Renaming them is a breaking change.

Tier 2: Semantic tokens (also called alias or role tokens)#

Semantic tokens map an intent onto a global token. They answer the question: "where is this used?" rather than "what color is this?"

# Semantic tier: intent over value
colors:
  primary:    "#111827"   # dominant text / brand chrome
  secondary:  "#6B7280"   # supporting text, metadata
  tertiary:   "#3B82F6"   # interactive elements only
  surface:    "#FFFFFF"   # card / panel backgrounds
  on-surface: "#111827"   # text on a surface
  error:      "#DC2626"   # destructive actions, error states

DESIGN.md works at this tier by default. The spec recommends these exact names (primary, secondary, tertiary, neutral, surface, on-surface, error) because agents and exporters recognize them.

Tier 3: Component tokens#

Component tokens bind semantic values to a specific UI component. They are the most specific and the most volatile tier.

components:
  button-primary:
    backgroundColor: "{colors.tertiary}"   # semantic ref
    textColor: "#ffffff"
    typography: "{typography.label-md}"
    rounded: "{rounded.md}"
    padding: 12px
  button-primary-hover:
    backgroundColor: "{colors.secondary}"

The {colors.tertiary} notation is DESIGN.md's token reference syntax. A reference that points to an undefined token triggers a broken-ref error at lint time. This makes the tier boundaries explicit and machine-checkable.

How DESIGN.md maps onto the three tiers#

DESIGN.md is primarily a semantic + component token file. Each frontmatter section corresponds to a tier:

DESIGN.md sectionTierExample key
colorsSemanticprimary, surface, on-surface
typographySemanticheadline-lg, body-md, label-md
spacingSemantic / Globalsm, md, lg
roundedSemantic / Globalnone, sm, md, full
componentsComponentbutton-primary, card, input

The {reference} syntax is the aliasing mechanism that connects tiers. A component token can reference any semantic token by path:

components:
  card:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.on-surface}"
    rounded: "{rounded.lg}"
    padding: 24px

When the tertiary color changes from blue to teal, every component that references {colors.tertiary} picks up the new value automatically, both in the rendered tool preview and in any export you generate.

A worked example#

Here is a minimal DESIGN.md for a product called "Slate UI" that uses all three tiers:

---
name: Slate UI
version: alpha
colors:
  primary:    "#0F172A"
  secondary:  "#475569"
  tertiary:   "#3B82F6"
  neutral:    "#F8FAFC"
  surface:    "#FFFFFF"
  on-surface: "#0F172A"
  error:      "#EF4444"
typography:
  headline-lg:
    fontFamily: "Inter"
    fontSize: 36px
    fontWeight: 700
    lineHeight: 1.1
    letterSpacing: "-0.02em"
  body-md:
    fontFamily: "Inter"
    fontSize: 16px
    fontWeight: 400
    lineHeight: 1.65
  label-md:
    fontFamily: "Inter"
    fontSize: 12px
    fontWeight: 500
    letterSpacing: "0.06em"
rounded:
  sm: 4px
  md: 8px
  lg: 16px
spacing:
  sm: 8px
  md: 16px
  lg: 32px
components:
  button-primary:
    backgroundColor: "{colors.tertiary}"
    textColor: "#ffffff"
    typography: "{typography.label-md}"
    rounded: "{rounded.md}"
    padding: 12px
  card:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.on-surface}"
    rounded: "{rounded.lg}"
    padding: 24px
---

The colors + typography + spacing + rounded sections form tiers 1 and 2. The components section is tier 3. The {...} references wire tier 3 back to tier 2.

Why naming discipline matters#

Consistent naming at the semantic tier is what makes a design system interoperable. DESIGN.md's linter enforces several naming-related rules:

  • missing-primary: warns when colors exist but no primary key is defined.
  • broken-ref: errors when a {reference} points to a token that does not exist.
  • orphaned-tokens: warns when a color token is defined but never referenced by any component.

These rules enforce the contract between tiers: every semantic token should have a consumer, and every component token should point to a real value.

Up to the guide#

Try it in the tool