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: 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