Skip to content

DOMAIN:DESIGN:DESIGN_TOKENS

OWNER: alexander (Design System Engineer, shared) ALSO_USED_BY: floris, floor, felice UPDATED: 2026-03-26 SCOPE: token taxonomy, naming conventions, formats, theming, dark mode, per-client brand customization PREREQUISITE: design-system-engineering.md (DESIGN.md structure)


TOKENS:OVERVIEW

WHAT: design tokens are the single source of truth for visual design decisions WHAT: they are platform-agnostic values that define color, spacing, typography, elevation, motion, and more WHY: tokens decouple design intent from implementation — change a token value, and every component updates WHY: tokens enable theming (dark mode, brand customization) without modifying component code

PRINCIPLE: design is code, not art — tokens ARE the design system, not a supplement to it PRINCIPLE: agents cannot read Figma files — tokens in DESIGN.md are the only design source they consume


TOKENS:THREE_TIER_TAXONOMY

TIER_1 — PRIMITIVE (BASE) TOKENS

WHAT: raw values that define what exists in the system PURPOSE: reference only — never applied directly to components NAMING: --{category}-{scale} (e.g., --color-blue-500, --spacing-4)

EXAMPLES:

/* Colors — full palette */
--color-blue-50: #EFF6FF;
--color-blue-100: #DBEAFE;
--color-blue-200: #BFDBFE;
--color-blue-300: #93C5FD;
--color-blue-400: #60A5FA;
--color-blue-500: #3B82F6;
--color-blue-600: #2563EB;
--color-blue-700: #1D4ED8;
--color-blue-800: #1E40AF;
--color-blue-900: #1E3A8A;
--color-blue-950: #172554;

/* Spacing — 4px base grid */
--spacing-0: 0;
--spacing-1: 4px;
--spacing-2: 8px;
--spacing-3: 12px;
--spacing-4: 16px;
--spacing-5: 20px;
--spacing-6: 24px;
--spacing-8: 32px;
--spacing-10: 40px;
--spacing-12: 48px;
--spacing-16: 64px;

/* Typography — scale */
--font-size-xs: 0.75rem;   /* 12px */
--font-size-sm: 0.875rem;  /* 14px */
--font-size-base: 1rem;    /* 16px */
--font-size-lg: 1.125rem;  /* 18px */
--font-size-xl: 1.25rem;   /* 20px */
--font-size-2xl: 1.5rem;   /* 24px */
--font-size-3xl: 1.875rem; /* 30px */

/* Border radius */
--radius-none: 0;
--radius-sm: 2px;
--radius-md: 6px;
--radius-lg: 8px;
--radius-xl: 12px;
--radius-full: 9999px;

RULE: primitive tokens are exhaustive — every color, every spacing step, every type size RULE: primitive tokens use neutral names (no intent, no context) RULE: keep primitive palette manageable — 10-12 steps per color family is sufficient


TIER_2 — SEMANTIC TOKENS

WHAT: purpose-driven tokens that reference primitive tokens PURPOSE: describe intent — what the token is FOR, not what it looks like NAMING: --{category}-{context}-{variant} (e.g., --color-text-primary, --color-surface-brand)

EXAMPLES:

/* Text colors */
--color-text-primary: var(--color-gray-900);
--color-text-secondary: var(--color-gray-600);
--color-text-muted: var(--color-gray-400);
--color-text-inverse: var(--color-white);
--color-text-brand: var(--color-blue-600);
--color-text-success: var(--color-green-700);
--color-text-warning: var(--color-amber-700);
--color-text-error: var(--color-red-700);

/* Surface colors */
--color-surface-primary: var(--color-white);
--color-surface-secondary: var(--color-gray-50);
--color-surface-elevated: var(--color-white);
--color-surface-brand: var(--color-blue-600);
--color-surface-success: var(--color-green-50);
--color-surface-warning: var(--color-amber-50);
--color-surface-error: var(--color-red-50);

/* Border colors */
--color-border-default: var(--color-gray-200);
--color-border-strong: var(--color-gray-400);
--color-border-focus: var(--color-blue-500);
--color-border-error: var(--color-red-500);

/* Interactive colors */
--color-interactive-default: var(--color-blue-600);
--color-interactive-hover: var(--color-blue-700);
--color-interactive-active: var(--color-blue-800);
--color-interactive-disabled: var(--color-gray-300);

/* Spacing — semantic */
--spacing-inline-xs: var(--spacing-1);
--spacing-inline-sm: var(--spacing-2);
--spacing-inline-md: var(--spacing-4);
--spacing-inline-lg: var(--spacing-6);
--spacing-stack-xs: var(--spacing-1);
--spacing-stack-sm: var(--spacing-2);
--spacing-stack-md: var(--spacing-4);
--spacing-stack-lg: var(--spacing-8);
--spacing-section: var(--spacing-16);

/* Typography — semantic */
--font-size-body: var(--font-size-base);
--font-size-caption: var(--font-size-sm);
--font-size-label: var(--font-size-sm);
--font-size-heading-page: var(--font-size-2xl);
--font-size-heading-section: var(--font-size-xl);
--font-size-heading-subsection: var(--font-size-lg);

/* Elevation */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);

RULE: semantic tokens ALWAYS reference primitive tokens via var() RULE: keep semantic tier under 150 tokens — more is not better RULE: every semantic token must have clear usage documentation in DESIGN.md


TIER_3 — COMPONENT TOKENS

WHAT: tokens scoped to specific components PURPOSE: describe WHERE a token is used NAMING: --{component}-{element}-{property}-{state} (e.g., --button-primary-bg-hover)

EXAMPLES:

/* Button tokens */
--button-primary-bg: var(--color-interactive-default);
--button-primary-bg-hover: var(--color-interactive-hover);
--button-primary-bg-active: var(--color-interactive-active);
--button-primary-bg-disabled: var(--color-interactive-disabled);
--button-primary-text: var(--color-text-inverse);
--button-primary-border: transparent;
--button-primary-radius: var(--radius-md);
--button-primary-padding-x: var(--spacing-inline-md);
--button-primary-padding-y: var(--spacing-stack-sm);

/* Input tokens */
--input-bg: var(--color-surface-primary);
--input-border: var(--color-border-default);
--input-border-focus: var(--color-border-focus);
--input-border-error: var(--color-border-error);
--input-text: var(--color-text-primary);
--input-placeholder: var(--color-text-muted);
--input-radius: var(--radius-md);
--input-padding-x: var(--spacing-inline-sm);
--input-padding-y: var(--spacing-stack-sm);

/* Card tokens */
--card-bg: var(--color-surface-elevated);
--card-border: var(--color-border-default);
--card-radius: var(--radius-lg);
--card-shadow: var(--shadow-sm);
--card-padding: var(--spacing-stack-lg);

RULE: component tokens are optional — use only when a component needs to deviate from semantic tokens RULE: component tokens ALWAYS reference semantic tokens via var() RULE: if you are creating more than 10 component tokens for one component, the design system needs simplification


TOKENS:NAMING_CONVENTIONS

FORMAT

--{category}-{context}-{property}-{variant}-{state}

CATEGORY: color, spacing, font, radius, shadow, z, motion CONTEXT: text, surface, border, interactive (semantic tier) OR button, input, card (component tier) PROPERTY: bg, text, border, size, weight (when needed for disambiguation) VARIANT: primary, secondary, brand, success, warning, error STATE: hover, active, focus, disabled (component tier only)

EXAMPLES

GOOD: --color-text-primary GOOD: --color-surface-error GOOD: --button-primary-bg-hover GOOD: --spacing-stack-md

BAD: --blue-text (primitive value in semantic name) BAD: --btn-bg (abbreviation — spell it out) BAD: --color-1 (meaningless) BAD: --header-top-left-margin (layout-specific, not token)


TOKENS:OUTPUT_FORMATS

CSS_CUSTOM_PROPERTIES (primary)

WHERE: web projects (Next.js, Nuxt, vanilla) HOW: tokens defined in :root selector, theme overrides in [data-theme] selector

:root {
  /* Primitive */
  --color-blue-500: #3B82F6;
  /* Semantic */
  --color-interactive-default: var(--color-blue-500);
}

TAILWIND_CONFIG (web projects)

WHERE: projects using Tailwind CSS HOW: tokens mapped to Tailwind theme extension

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: 'var(--color-interactive-default)',
        'text-primary': 'var(--color-text-primary)',
        'surface-primary': 'var(--color-surface-primary)',
      },
      spacing: {
        'inline-sm': 'var(--spacing-inline-sm)',
        'stack-md': 'var(--spacing-stack-md)',
      },
    },
  },
};

RULE: Tailwind config references CSS custom properties — single source of truth RULE: never hardcode hex values in Tailwind config

SWIFT_CONSTANTS (iOS projects)

WHERE: native iOS projects (SwiftUI) HOW: tokens exported as Swift Color and CGFloat constants

// DesignTokens.swift
enum Colors {
    static let textPrimary = Color("TextPrimary") // from asset catalog
    static let surfacePrimary = Color("SurfacePrimary")
    static let interactiveDefault = Color("InteractiveDefault")
}

enum Spacing {
    static let inlineSm: CGFloat = 8
    static let stackMd: CGFloat = 16
    static let sectionGap: CGFloat = 64
}

JSON (source format, build pipeline)

WHERE: token source files, used by build tools (Style Dictionary, Tokens Studio) HOW: W3C Design Tokens Community Group format

{
  "color": {
    "blue": {
      "500": { "$value": "#3B82F6", "$type": "color" }
    },
    "text": {
      "primary": { "$value": "{color.gray.900}", "$type": "color" }
    }
  }
}

TOKENS:THEMING — DARK_MODE

APPROACH

METHOD: redefine semantic tokens under [data-theme="dark"] selector RULE: primitive tokens stay the same — only semantic mappings change RESULT: every component automatically adapts because it references semantic tokens

:root {
  --color-text-primary: var(--color-gray-900);
  --color-text-secondary: var(--color-gray-600);
  --color-surface-primary: var(--color-white);
  --color-surface-secondary: var(--color-gray-50);
  --color-border-default: var(--color-gray-200);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
}

[data-theme="dark"] {
  --color-text-primary: var(--color-gray-100);
  --color-text-secondary: var(--color-gray-400);
  --color-surface-primary: var(--color-gray-900);
  --color-surface-secondary: var(--color-gray-800);
  --color-border-default: var(--color-gray-700);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.3);
}

DARK_MODE_RULES

RULE: never invert primitive colors — create explicit dark semantic mappings RULE: test contrast ratios in dark mode separately (light-on-dark has different perception) RULE: shadows need more opacity in dark mode (dark shadows on dark backgrounds are invisible) RULE: avoid pure black (#000) backgrounds — use gray-900 or gray-950 RULE: status colors (success, warning, error) need lighter variants in dark mode RULE: respect prefers-color-scheme media query as default, allow manual override

IMPLEMENTATION

// Theme toggle
function setTheme(theme: 'light' | 'dark' | 'system') {
  if (theme === 'system') {
    const preferred = window.matchMedia('(prefers-color-scheme: dark)').matches
      ? 'dark' : 'light';
    document.documentElement.dataset.theme = preferred;
  } else {
    document.documentElement.dataset.theme = theme;
  }
  localStorage.setItem('theme', theme);
}

TOKENS:CLIENT_BRAND_CUSTOMIZATION

APPROACH

GE builds client projects on a shared design system — brand customization is achieved by overriding primitive color tokens per client.

RULE: client brand tokens are a fourth layer — they override primitives, which cascade through semantic and component tiers RULE: client brand tokens live in a client-specific CSS file, loaded after base tokens

IMPLEMENTATION

/* Base tokens (ge-design-system.css) */
:root {
  --color-brand-500: #3B82F6; /* GE blue — default */
  --color-brand-600: #2563EB;
  --color-brand-700: #1D4ED8;
}

/* Client override (client-acme.css) */
:root {
  --color-brand-500: #10B981; /* ACME green */
  --color-brand-600: #059669;
  --color-brand-700: #047857;
}

WHAT_IS_CUSTOMIZABLE

CUSTOMIZABLE: brand primary color (and its scale) CUSTOMIZABLE: brand secondary color (optional) CUSTOMIZABLE: logo (image asset, not token) CUSTOMIZABLE: font family (if client has brand font with web license) CUSTOMIZABLE: border radius preference (sharp vs rounded)

NOT_CUSTOMIZABLE: spacing scale (consistency across projects) NOT_CUSTOMIZABLE: typography scale (tested for readability) NOT_CUSTOMIZABLE: semantic color mappings (tested for accessibility) NOT_CUSTOMIZABLE: shadow system (consistent elevation language)

PROCESS

STEP: alexander receives client brand guidelines (colors, fonts, logo) STEP: alexander maps brand colors to primitive token scale (50-950) STEP: alexander verifies contrast ratios with new brand colors (accessibility) STEP: alexander generates client CSS override file STEP: alexander documents brand tokens in client project DESIGN.md STEP: developers load client CSS after base design system CSS


TOKENS:GOVERNANCE

WHO_CAN_CHANGE_TOKENS

PRIMITIVE: alexander only — requires design review SEMANTIC: alexander only — requires design + accessibility review COMPONENT: alexander or lead developer — requires design review CLIENT_BRAND: alexander — requires client approval

TOKEN_CHANGE_PROCESS

STEP: propose change in DESIGN.md with rationale STEP: alexander reviews for design consistency STEP: julian reviews for accessibility impact (contrast, target sizes) STEP: merge to design system STEP: all projects using the design system pick up changes automatically

COMMON_PITFALL

PITFALL: developer cannot find the right token, invents a new one IMPACT: token sprawl, inconsistency, maintenance burden FIX: documented token reference in DESIGN.md, searchable token catalog FIX: code review catches hardcoded values and unlisted tokens


TOKENS:AGENT_INSTRUCTIONS

FOR alexander: - you define all tokens at all three tiers - document every token in DESIGN.md with name, value, and usage context - verify contrast ratios when defining color tokens - generate client brand override files for each new project - keep semantic tier under 150 tokens

FOR floris, floor: - consume tokens from DESIGN.md — never hardcode color, spacing, or typography values - use CSS custom properties via var() — never reference primitive tokens directly in components - if you need a token that does not exist, request it from alexander — do not create it - use Tailwind theme extension that references CSS custom properties

FOR felice: - during visual QA, verify that components use correct tokens (no hardcoded values) - verify dark mode renders correctly - verify client brand overrides apply consistently


READ_ALSO: domains/design/design-system-engineering.md, domains/design/figma-handoff.md, domains/design/component-library.md, domains/design/responsive-design.md