Design Tokens Explained
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:
tertiarytells a reader this color is for interactive elements,#3B82F6does 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 section | Tier | Example key |
|---|---|---|
colors | Semantic | primary, surface, on-surface |
typography | Semantic | headline-lg, body-md, label-md |
spacing | Semantic / Global | sm, md, lg |
rounded | Semantic / Global | none, sm, md, full |
components | Component | button-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 noprimarykey 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#
- DESIGN.md Overview: the file format explained from first principles
- Colors reference: color token names, reference syntax, and contrast-ratio lint
- Components reference: component sub-tokens and variant conventions
- CLI and Lint Rules: all nine built-in rules with severity levels