Create a single source of truth for design decisions that powers every platform and framework. Covers token taxonomy, naming conventions, transformation pipelines, multi-platform distribution, theming, and the patterns that bridge design and engineering.
Design tokens are a standardized, machine-readable representation of visual design language—such as colors, spacing, typography, and shadows—used to enforce consistency across frontend systems. They matter when multiple teams, frameworks, or platforms need to share and synchronize design decisions, especially in large-scale, multi-team environments where UI drift is inevitable.
Design tokens are structured data that encode a design system’s variables. They are typically stored as JSON, YAML, or a flat key-value format, with each token representing a visual property and its values across contexts.
A token file might look like this:
{
"color": {
"primary": {
"base": "#3498db",
"hover": "#2980b9",
"active": "#1f618d"
},
"secondary": {
"base": "#e67e22",
"hover": "#d35400"
}
},
"spacing": {
"xs": 4,
"sm": 8,
"md": 16,
"lg": 24,
"xl": 32
},
"typography": {
"fontFamily": "Inter, sans-serif",
"fontSize": {
"xs": 12,
"sm": 14,
"md": 16,
"lg": 18,
"xl": 20
},
"fontWeight": {
"regular": 400,
"medium": 500,
"bold": 700
}
},
"border": {
"radius": {
"sm": 4,
"md": 8,
"lg": 12
},
"width": {
"sm": 1,
"md": 2,
"lg": 3
}
}
}
Tokens are versioned and shared via a common source—typically a Git repository, a CDN, or a design system registry. They are consumed by frontend build tools, CSS preprocessors, and runtime frameworks.
To use design tokens in a frontend project, a build process must resolve the token values and output them to CSS or JavaScript.
A typical setup uses postcss with postcss-design-tokens to generate CSS :root variables from a token JSON file.
// postcss.config.js
module.exports = {
plugins: [
require('postcss-design-tokens')({
tokens: 'tokens.json',
output: 'css/tokens.css',
format: 'css',
transform: (token) => {
const key = `--${token.name.replace(/\.|:/g, '-')}`;
return { [key]: token.value };
}
})
]
};
This generates:
:root {
--color-primary-base: #3498db;
--color-primary-hover: #2980b9;
--color-secondary-base: #e67e22;
--spacing-xs: 4px;
--spacing-sm: 8px;
--typography-fontFamily: Inter, sans-serif;
--typography-fontSize-md: 16px;
}
In a SASS project, tokens are parsed and used via @use statements.
// _tokens.scss
@use 'tokens.json' as tokens;
// Now access tokens directly
.button {
background-color: tokens.$color-primary-base;
padding: tokens.$spacing-md tokens.$spacing-lg;
font-size: tokens.$typography-fontSize-md;
border-radius: tokens.$border-radius-md;
font-weight: tokens.$typography-fontWeight-bold;
}
The tokens.json file is imported into SASS as a map, enabling full type inference and compile-time validation.
In a framework like React, tokens may be loaded at runtime to support dynamic theming.
A React app fetches the token file on startup and applies it via a useEffect hook.
// useTokens.js
import { useEffect, useState } from 'react';
export function useTokens(url) {
const [tokens, setTokens] = useState({});
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setTokens(data);
// Apply to document root
Object.entries(data).forEach(([group, values]) => {
Object.entries(values).forEach(([key, value]) => {
const cssVar = `--${group}-${key}`;
document.documentElement.style.setProperty(cssVar, value);
});
});
})
.catch(err => {
console.error('Failed to load design tokens:', err);
// Fall back to defaults
setTokens(defaultTokens);
});
}, [url]);
return tokens;
}
// App.js
function App() {
const tokens = useTokens('/tokens/light.json');
return (
<div className="app">
<h1>App with {tokens.color?.primary?.base}</h1>
<button className="button">Click me</button>
</div>
);
}
Switching themes involves loading a new token file and updating the DOM.
function ThemeSwitcher() {
const [theme, setTheme] = useState('light');
const switchTheme = (newTheme) => {
fetch(`/tokens/${newTheme}.json`)
.then(res => res.json())
.then(tokens => {
// Update CSS variables
Object.entries(tokens).forEach(([group, values]) => {
Object.entries(values).forEach(([key, value]) => {
const cssVar = `--${group}-${key}`;
document.documentElement.style.setProperty(cssVar, value);
});
});
// Update theme class
document.body.className = `theme-${newTheme}`;
})
.catch(err => {
console.error('Theme switch failed:', err);
// Apply fallback theme
document.documentElement.style.setProperty('--color-primary-base', '#3498db');
});
setTheme(newTheme);
}
return (
<div>
<button onClick={() => switchTheme('light')}>Light</button>
<button onClick={() => switchTheme('dark')}>Dark</button>
</div>
);
}
Tokens can vary by context—device, theme, or interaction state. A single token may have multiple values, each tied to a condition.
A token file may include variant keys:
{
"color": {
"primary": {
"base": "#3498db",
"hover": "#2980b9",
"active": "#1f618d",
"focus": "#2980b9",
"disabled": "#95a5a6"
}
},
"spacing": {
"md": {
"base": 16,
"tablet": 24,
"mobile": 12
}
}
}
To resolve a token at runtime, a system evaluates conditions such as prefers-color-scheme, width, or data-theme.
@media (prefers-color-scheme: dark) {
:root {
--color-primary-base: #5dade2;
--color-background-base: #1a1a1a;
}
}
@media (max-width: 768px) {
:root {
--spacing-md: 12px;
--typography-fontSize-lg: 16px;
}
}
function resolveToken(token, context) {
const { theme, device, state } = context;
// Prioritize: base → theme → device → state
const path = [token, theme, device, state];
let value = token;
for (const key of path) {
if (key && typeof key === 'object') {
value = key.value || value;
}
}
return value;
}
This approach enables dynamic, context-aware design systems where a single token definition supports multiple states.
A missing or malformed tokens.json file causes visual drift.
// Error in build log
Error: Failed to load design tokens: 404 Not Found
Response: {
"error": "Token file not found",
"requested": "/tokens/light.json"
}
After loading tokens, --color-primary-base is not used in any element. The issue is that :root variables must be declared in a stylesheet that loads before the component styles.
/* CSS that doesn't apply tokens */
.button {
background-color: var(--color-primary-base); /* fails: undefined */
}
Fix: Ensure the token CSS is imported before component CSS.
When multiple contexts apply, the system must resolve which value takes precedence.
A token spacing.md is defined at the root level and in a mobile context. On a tablet, the mobile value is used, but the desktop value is ignored.
{
"spacing": {
"md": 16,
"mobile": {
"md": 12
},
"tablet": {
"md": 20
}
}
}
The system must collect all relevant contexts and resolve them in order. A common mistake is applying only the top-level value and ignoring nested contexts.
The frontend app uses tokens version 1.2, but the design system has introduced a new color.accent token. The app fails to render accent colors.
// tokens.json (v1.2)
{
"color": {
"primary": "#3498db"
}
}
The new accent token is missing in the build. To detect this, the build process should validate the token schema.
Two teams define spacing.md—one as a number, the other as a string.
{
"spacing": {
"md": 16,
"md": "medium" // conflicting
}
}
The later definition overwrites the earlier. To prevent this, the token system should enforce unique keys or use a hierarchical naming.
category.subcategory.value (e.g., color.primary.base, typography.fontSize.sm).validate-tokens tokens.json.tokens/ directory with a tokens.schema.json file for validation.// tokens.schema.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"color": { "type": "object" },
"spacing": { "type": "object" },
"typography": { "type": "object" },
"border": { "type": "object" }
},
"required": ["color", "spacing"]
}
Use jsonschema to validate:
jsonschema -i tokens.json -s tokens.schema.json
jsonschema (Python) or ajv (Node.js) for schema validation.postcss-design-tokens for CSS generation.yargs or commander for CLI tools to generate and validate tokens.npm run build:tokens to compile tokens to multiple formats (CSS, SASS, JavaScript).Design tokens are not just a data format—they are a workflow. They connect design to code, enabling teams to define, version, and consume visual language across platforms. The key to success is consistent naming, proper build integration, and robust runtime resolution. When tokens fail, the cause is often not the token itself, but the pipeline around it. The most subtle failures—missing CSS variables, version mismatches, and context resolution—arise not from missing features, but from unspoken assumptions about how tokens are consumed.
This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy