Implementing Design Tokens for Web Application Design System
Design tokens are named variables storing atomic design decisions: colors, font sizes, spacing, border radii, shadows, animation durations. Tokens are not just CSS variables. They're a contract between designer and developer, implemented in a platform-independent format (JSON/YAML), compiled into the needed output for each platform: CSS custom properties, JS/TS objects, Swift constants, Android XML.
Why Tokens Instead of Variables
Directly replacing #1A73E8 with --color-primary is a variable, not a token. A token carries semantics: --color-action-primary-default, --color-action-primary-hover, --color-action-primary-disabled. The difference is fundamental: changing brand color requires changing one value in one place, not hunting all occurrences of "primary blue" across the codebase.
Real pain in teams of 5+ without tokens:
- designer changes
Blue/600in Figma, dev doesn't know that's#1D4ED8used in 12 places with different hex values - theming requires style duplication instead of variable switching
- onboarding new dev takes days instead of hours — no single source of truth
Token Structure: Three-Level Model
Primitive Tokens (Global Tokens) — raw values without context:
{
"color": {
"blue": {
"50": { "value": "#EFF6FF" },
"500": { "value": "#3B82F6" },
"900": { "value": "#1E3A5F" }
},
"gray": {
"0": { "value": "#FFFFFF" },
"100": { "value": "#F3F4F6" },
"900": { "value": "#111827" }
}
},
"spacing": {
"1": { "value": "4px" },
"2": { "value": "8px" },
"4": { "value": "16px" },
"8": { "value": "32px" }
}
}
Semantic Tokens (Alias Tokens) — binding meaning to primitives:
{
"color": {
"action": {
"primary": {
"default": { "value": "{color.blue.500}" },
"hover": { "value": "{color.blue.600}" },
"disabled":{ "value": "{color.gray.300}" }
}
},
"surface": {
"default": { "value": "{color.gray.0}" },
"subtle": { "value": "{color.gray.100}" }
},
"text": {
"primary": { "value": "{color.gray.900}" },
"secondary": { "value": "{color.gray.600}" }
}
}
}
Component Tokens — specific component variables:
{
"button": {
"background": { "value": "{color.action.primary.default}" },
"background-hover": { "value": "{color.action.primary.hover}" },
"radius": { "value": "{border-radius.md}" },
"padding-x": { "value": "{spacing.4}" }
}
}
Transformation Tools
Style Dictionary (Amazon) — de-facto standard for token compilation:
// config.js
module.exports = {
source: ['tokens/**/*.json'],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'dist/css/',
files: [{
destination: 'tokens.css',
format: 'css/variables',
options: { outputReferences: true }
}]
},
js: {
transformGroup: 'js',
buildPath: 'dist/js/',
files: [{
destination: 'tokens.js',
format: 'javascript/es6'
}]
}
}
};
npx style-dictionary build --config config.js
CSS Output:
:root {
--color-blue-500: #3B82F6;
--color-action-primary-default: var(--color-blue-500);
--button-background: var(--color-action-primary-default);
}
Token Pipeline with Theme Support:
// Dark theme — separate semantic tokens
platforms: {
'css/dark': {
transformGroup: 'css',
prefix: 'dt',
buildPath: 'dist/css/',
files: [{
destination: 'tokens-dark.css',
format: 'css/variables',
filter: (token) => token.filePath.includes('themes/dark'),
options: {
selector: '[data-theme="dark"]'
}
}]
}
}
Figma Integration
Tokens in Figma are managed via:
- Tokens Studio (plugin) — bidirectional sync with JSON via GitHub/GitLab
- Figma Variables (built-in since 2023) — native support, but limited export API
With Tokens Studio workflow:
- Designer changes token in Figma
- Plugin pushes change to
tokens/update-blue-palettebranch - CI runs Style Dictionary, generates CSS/JS
- PR with updated artifacts — code review and merge
Tailwind CSS + Tokens
Tailwind can be fully parameterized via tokens:
// tailwind.config.js
const tokens = require('./dist/js/tokens');
module.exports = {
theme: {
extend: {
colors: {
'action-primary': tokens.ColorActionPrimaryDefault,
'surface-subtle': tokens.ColorSurfaceSubtle,
},
spacing: {
// mapping from tokens
},
borderRadius: {
sm: tokens.BorderRadiusSm,
md: tokens.BorderRadiusMd,
}
}
}
};
Better to use CSS custom properties in Tailwind 4 via @theme:
@theme {
--color-primary: var(--color-action-primary-default);
--radius-button: var(--button-radius);
}
Versioning and Governance
Tokens are part of codebase. They live in repository (usually design system monorepo or separate package) and published as npm package:
@company/[email protected]
├── dist/css/tokens.css
├── dist/js/tokens.js
├── dist/js/tokens.d.ts
└── dist/scss/_tokens.scss
Semantic versioning is critical: token rename is breaking change (major), new token is minor, value fix is patch or minor depending on context.
Token changelog should be readable for designers, not just developers.
Typical Timeline
Initial implementation (audit existing styles → define primitive/semantic tokens → setup Style Dictionary → integrate in one project) for medium web app — 8–12 working days. If bidirectional Figma sync via Tokens Studio + CI/CD pipeline needed — +3–5 days. Full legacy project migration from hard-coded values to tokens — estimated separately by codebase volume.







