| name | design-tokens-structure |
| description | Architects token systems with primitive, semantic, and component layers. Use when structuring tokens from scratch, adding multi-theme support, setting up token aliasing, or organizing token hierarchies. |
Design Tokens Structure
Overview
Establish the architectural foundation for a scalable design token system. Defines the three-layer token hierarchy (primitive → semantic → component) that enables theming, multi-brand support, and maintainable design systems.
When to Use
- Starting a new design system from scratch
- Restructuring an existing token system
- Adding multi-theme or multi-brand support
- Converting hardcoded values to tokens
- Creating a token governance strategy
Implementation Checklist
Copy this checklist when architecting a token system:
Token Architecture Setup:
- [ ] Audit existing values and establish naming conventions
- [ ] Define primitive tokens (raw values, context-free)
- [ ] Define semantic tokens (purpose-based, reference primitives)
- [ ] Create theme overrides (dark.json, brand variants)
- [ ] Add component tokens for complex stateful components
- [ ] Configure build output (CSS variables, Tailwind, JSON)
Quick Reference: Token Layers
| Layer | Also Called | Purpose | Example |
|---|---|---|---|
| Primitive | Core, Base, Global | Raw values, context-free | blue-500: #3b82f6 |
| Semantic | Alias, Purpose, Role | Meaning-based references | color-primary: {blue-500} |
| Component | Specific, Local | Component-scoped tokens | button-bg: {color-primary} |
The Three-Layer Architecture
┌─────────────────────────────────────────────────────────┐
│ COMPONENT TOKENS │
│ button-bg, card-border, input-focus-ring │
│ ↓ references │
├─────────────────────────────────────────────────────────┤
│ SEMANTIC TOKENS │
│ color-primary, color-bg-surface, spacing-page │
│ ↓ references │
├─────────────────────────────────────────────────────────┤
│ PRIMITIVE TOKENS │
│ blue-500, gray-100, spacing-16, radius-8 │
│ (raw values only) │
└─────────────────────────────────────────────────────────┘
Layer 1: Primitive Tokens
Raw design values with no semantic meaning. These are the building blocks.
Characteristics
- Context-free (no "primary", "background", etc.)
- Named by their intrinsic property (color: hue, spacing: size)
- Never change between themes
- Comprehensive palette of options
Structure
{
"primitive": {
"color": {
"gray": {
"50": { "value": "#f9fafb" },
"100": { "value": "#f3f4f6" },
"200": { "value": "#e5e7eb" },
"300": { "value": "#d1d5db" },
"400": { "value": "#9ca3af" },
"500": { "value": "#6b7280" },
"600": { "value": "#4b5563" },
"700": { "value": "#374151" },
"800": { "value": "#1f2937" },
"900": { "value": "#111827" },
"950": { "value": "#030712" }
},
"blue": {
"50": { "value": "#eff6ff" },
"100": { "value": "#dbeafe" },
"200": { "value": "#bfdbfe" },
"300": { "value": "#93c5fd" },
"400": { "value": "#60a5fa" },
"500": { "value": "#3b82f6" },
"600": { "value": "#2563eb" },
"700": { "value": "#1d4ed8" },
"800": { "value": "#1e40af" },
"900": { "value": "#1e3a8a" }
},
"green": { /* success colors */ },
"red": { /* error colors */ },
"amber": { /* warning colors */ }
},
"spacing": {
"0": { "value": "0" },
"1": { "value": "0.25rem" },
"2": { "value": "0.5rem" },
"3": { "value": "0.75rem" },
"4": { "value": "1rem" },
"5": { "value": "1.25rem" },
"6": { "value": "1.5rem" },
"8": { "value": "2rem" },
"10": { "value": "2.5rem" },
"12": { "value": "3rem" },
"16": { "value": "4rem" },
"20": { "value": "5rem" },
"24": { "value": "6rem" }
},
"radius": {
"none": { "value": "0" },
"sm": { "value": "0.125rem" },
"md": { "value": "0.375rem" },
"lg": { "value": "0.5rem" },
"xl": { "value": "0.75rem" },
"2xl": { "value": "1rem" },
"full": { "value": "9999px" }
},
"font": {
"family": {
"sans": { "value": "Inter, system-ui, sans-serif" },
"mono": { "value": "JetBrains Mono, monospace" }
},
"size": {
"xs": { "value": "0.75rem" },
"sm": { "value": "0.875rem" },
"md": { "value": "1rem" },
"lg": { "value": "1.125rem" },
"xl": { "value": "1.25rem" },
"2xl": { "value": "1.5rem" },
"3xl": { "value": "1.875rem" },
"4xl": { "value": "2.25rem" }
},
"weight": {
"normal": { "value": "400" },
"medium": { "value": "500" },
"semibold": { "value": "600" },
"bold": { "value": "700" }
}
}
}
}
Layer 2: Semantic Tokens
Purpose-based tokens that reference primitives. These tokens change between themes.
Characteristics
- Named by purpose/role, not appearance
- Reference primitive tokens only
- The only layer that changes per theme
- Describe "what for", not "what it looks like"
Structure
{
"semantic": {
"color": {
"bg": {
"primary": { "value": "{primitive.color.gray.50}" },
"secondary": { "value": "{primitive.color.gray.100}" },
"tertiary": { "value": "{primitive.color.gray.200}" },
"inverse": { "value": "{primitive.color.gray.900}" },
"brand": { "value": "{primitive.color.blue.500}" }
},
"text": {
"primary": { "value": "{primitive.color.gray.900}" },
"secondary": { "value": "{primitive.color.gray.600}" },
"tertiary": { "value": "{primitive.color.gray.500}" },
"inverse": { "value": "{primitive.color.gray.50}" },
"brand": { "value": "{primitive.color.blue.600}" },
"link": { "value": "{primitive.color.blue.600}" }
},
"border": {
"primary": { "value": "{primitive.color.gray.200}" },
"secondary": { "value": "{primitive.color.gray.300}" },
"focus": { "value": "{primitive.color.blue.500}" }
},
"status": {
"success": { "value": "{primitive.color.green.500}" },
"warning": { "value": "{primitive.color.amber.500}" },
"error": { "value": "{primitive.color.red.500}" },
"info": { "value": "{primitive.color.blue.500}" }
},
"interactive": {
"primary": { "value": "{primitive.color.blue.500}" },
"primary-hover": { "value": "{primitive.color.blue.600}" },
"primary-active": { "value": "{primitive.color.blue.700}" },
"secondary": { "value": "{primitive.color.gray.100}" },
"secondary-hover": { "value": "{primitive.color.gray.200}" }
}
},
"spacing": {
"page": { "value": "{primitive.spacing.6}" },
"section": { "value": "{primitive.spacing.12}" },
"element": { "value": "{primitive.spacing.4}" },
"inline": { "value": "{primitive.spacing.2}" }
},
"radius": {
"interactive": { "value": "{primitive.radius.md}" },
"container": { "value": "{primitive.radius.lg}" },
"pill": { "value": "{primitive.radius.full}" }
}
}
}
Dark Theme Override
{
"semantic": {
"color": {
"bg": {
"primary": { "value": "{primitive.color.gray.950}" },
"secondary": { "value": "{primitive.color.gray.900}" },
"tertiary": { "value": "{primitive.color.gray.800}" },
"inverse": { "value": "{primitive.color.gray.50}" }
},
"text": {
"primary": { "value": "{primitive.color.gray.50}" },
"secondary": { "value": "{primitive.color.gray.400}" },
"tertiary": { "value": "{primitive.color.gray.500}" },
"inverse": { "value": "{primitive.color.gray.900}" }
},
"border": {
"primary": { "value": "{primitive.color.gray.800}" },
"secondary": { "value": "{primitive.color.gray.700}" }
}
}
}
}
Layer 3: Component Tokens
Component-specific tokens that reference semantic tokens. Optional but powerful for complex components.
Characteristics
- Scoped to a specific component
- Reference semantic tokens (rarely primitives)
- Enable component-level customization
- Used for complex, stateful components
Structure
{
"component": {
"button": {
"primary": {
"bg": { "value": "{semantic.color.interactive.primary}" },
"bg-hover": { "value": "{semantic.color.interactive.primary-hover}" },
"text": { "value": "{semantic.color.text.inverse}" },
"border": { "value": "transparent" },
"radius": { "value": "{semantic.radius.interactive}" },
"padding-x": { "value": "{primitive.spacing.4}" },
"padding-y": { "value": "{primitive.spacing.2}" }
},
"secondary": {
"bg": { "value": "{semantic.color.interactive.secondary}" },
"bg-hover": { "value": "{semantic.color.interactive.secondary-hover}" },
"text": { "value": "{semantic.color.text.primary}" },
"border": { "value": "{semantic.color.border.primary}" }
},
"disabled": {
"bg": { "value": "{primitive.color.gray.100}" },
"text": { "value": "{primitive.color.gray.400}" }
}
},
"input": {
"bg": { "value": "{semantic.color.bg.primary}" },
"text": { "value": "{semantic.color.text.primary}" },
"placeholder": { "value": "{semantic.color.text.tertiary}" },
"border": { "value": "{semantic.color.border.primary}" },
"border-focus": { "value": "{semantic.color.border.focus}" },
"radius": { "value": "{semantic.radius.interactive}" },
"padding-x": { "value": "{primitive.spacing.3}" },
"padding-y": { "value": "{primitive.spacing.2}" }
},
"card": {
"bg": { "value": "{semantic.color.bg.primary}" },
"border": { "value": "{semantic.color.border.primary}" },
"radius": { "value": "{semantic.radius.container}" },
"padding": { "value": "{semantic.spacing.element}" },
"shadow": { "value": "{primitive.shadow.md}" }
}
}
}
CSS Output
Generated Variables
/* Primitives - available but rarely used directly */
:root {
--primitive-color-gray-50: #f9fafb;
--primitive-color-gray-900: #111827;
--primitive-color-blue-500: #3b82f6;
--primitive-spacing-4: 1rem;
/* ... */
}
/* Semantic - the main tokens you use */
:root {
--color-bg-primary: var(--primitive-color-gray-50);
--color-text-primary: var(--primitive-color-gray-900);
--color-interactive-primary: var(--primitive-color-blue-500);
--spacing-element: var(--primitive-spacing-4);
/* ... */
}
/* Dark theme overrides semantic only */
[data-theme="dark"] {
--color-bg-primary: var(--primitive-color-gray-950);
--color-text-primary: var(--primitive-color-gray-50);
/* primitives stay the same */
}
/* Component tokens (optional) */
:root {
--button-primary-bg: var(--color-interactive-primary);
--button-primary-text: var(--color-text-inverse);
--card-bg: var(--color-bg-primary);
--card-padding: var(--spacing-element);
}
Usage in Components
/* Use semantic tokens */
.card {
background: var(--color-bg-primary);
color: var(--color-text-primary);
border: 1px solid var(--color-border-primary);
padding: var(--spacing-element);
}
/* Or use component tokens if defined */
.card {
background: var(--card-bg);
padding: var(--card-padding);
}
/* Never use primitives directly in components */
/* ❌ Bad */
.card {
background: var(--primitive-color-gray-50);
}
File Organization
Recommended Structure
tokens/
├── primitive/
│ ├── colors.json
│ ├── spacing.json
│ ├── typography.json
│ ├── radius.json
│ └── shadows.json
├── semantic/
│ ├── light.json # Light theme semantic mappings
│ └── dark.json # Dark theme overrides
├── component/
│ ├── button.json
│ ├── input.json
│ └── card.json
└── index.json # Combines all for build
Flat vs Nested
Nested (Recommended for large systems):
{
"color": {
"bg": {
"primary": { "value": "..." }
}
}
}
Output: --color-bg-primary
Flat (Simpler for small systems):
{
"color-bg-primary": { "value": "..." }
}
Output: --color-bg-primary
Naming Conventions
Pattern: {category}-{property}-{variant}-{state}
| Category | Examples |
|---|---|
| color | color-bg-primary, color-text-secondary |
| spacing | spacing-page, spacing-inline |
| radius | radius-interactive, radius-container |
| font | font-size-lg, font-weight-bold |
| shadow | shadow-md, shadow-lg |
Semantic Naming Guidelines
| Use | Avoid |
|---|---|
color-text-primary |
color-dark-gray |
color-bg-surface |
color-white |
color-interactive-primary |
color-blue |
spacing-page |
spacing-24px |
color-status-error |
color-red |
When to Add Each Layer
| Situation | Layers Needed |
|---|---|
| Simple app, single theme | Primitive + Semantic |
| Multi-theme (light/dark) | Primitive + Semantic (per theme) |
| Complex components | All three layers |
| Multi-brand (white-label) | All three + brand-specific overrides |
| Design tool sync | Primitive only to start |
Anti-Patterns
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Semantic references semantic | Circular/complex chains | Semantic → Primitive only |
| Using primitives in components | Breaks theming | Use semantic tokens |
| Color names in semantic | Leaks visual info | Use purpose names |
| Too many component tokens | Maintenance burden | Only for complex states |
| Skipping semantic layer | Can't theme | Always have semantic layer |
Migrating Existing Tokens
Step 1: Audit Current Usage
Find all hardcoded values and existing variables.
Step 2: Create Primitive Layer
Extract unique values into primitives.
Step 3: Create Semantic Layer
Map primitives to purposes.
Step 4: Update Components
Replace hardcoded values with semantic tokens.
Step 5: Add Themes
Create theme-specific semantic overrides.
/* Before */
.button {
background: #3b82f6;
color: white;
}
/* After */
.button {
background: var(--color-interactive-primary);
color: var(--color-text-inverse);
}