Agent Skills: Design Tokens Structure

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.

UncategorizedID: dylantarre/design-system-skills/design-tokens-structure

Install this agent skill to your local

pnpm dlx add-skill https://github.com/dylantarre/design-system-skills/tree/HEAD/skills/tokens/design-tokens-structure

Skill Files

Browse the full folder contents for design-tokens-structure.

Download Skill

Loading file tree…

skills/tokens/design-tokens-structure/SKILL.md

Skill Metadata

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);
}