Implementing Style Dictionary for Multi-Framework Token Sync
Design tokens are the foundation of any scalable system, but synchronizing them across React, Angular, and Vue codebases presents unique challenges. Here's our battle-tested approach to token architecture that scales.
Why Design Tokens Matter
Design tokens are the atomic values that define your visual design language—colors, typography, spacing, shadows, borders, and more. They're the translation layer between design decisions and code implementation, and they're essential for maintaining consistency at scale.
Without a robust token system, every developer makes independent decisions about spacing, every designer picks slightly different shades, and your product slowly drifts into visual chaos. With tokens, there's a single source of truth that ensures spacing.md means exactly the same thing whether it's rendered in React, Angular, native iOS, or an email template.
Style Dictionary, created by Amazon, has emerged as the industry standard for managing design tokens. It's platform-agnostic, extensible, and handles the complexity of generating tokens for multiple output formats from a single source definition.
Architecture Overview
Before diving into implementation, let's establish the architectural pattern we recommend for enterprise-scale token systems:
tokens/
├── src/
│ ├── global/
│ │ ├── color.json # Base color palette
│ │ ├── typography.json # Font families, sizes, weights
│ │ ├── spacing.json # Spacing scale
│ │ └── shadow.json # Shadow definitions
│ ├── semantic/
│ │ ├── color.json # Semantic color mappings
│ │ └── component.json # Component-level tokens
│ └── themes/
│ ├── light.json # Light theme overrides
│ └── dark.json # Dark theme overrides
├── build/
│ ├── css/ # CSS custom properties output
│ ├── scss/ # SCSS variables output
│ ├── js/ # JavaScript/TypeScript output
│ └── ios/ # iOS Swift output
└── config/
└── style-dictionary.config.js This three-tier architecture—global, semantic, and theme—provides the flexibility needed for complex applications while maintaining clarity about what each token represents.
Tier 1: Global Tokens
Global tokens define the raw values available in your system. They're named descriptively based on what they are, not how they're used. These tokens rarely change and form the foundation of your design language.
Color Example
src/global/color.json
{
"color": {
"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" }
},
"gray": {
"50": { "value": "#f9fafb" },
"100": { "value": "#f3f4f6" },
"900": { "value": "#111827" }
},
"white": { "value": "#ffffff" },
"black": { "value": "#000000" }
}
} Spacing Example
src/global/spacing.json
{
"spacing": {
"0": { "value": "0" },
"1": { "value": "4px" },
"2": { "value": "8px" },
"3": { "value": "12px" },
"4": { "value": "16px" },
"5": { "value": "20px" },
"6": { "value": "24px" },
"8": { "value": "32px" },
"10": { "value": "40px" },
"12": { "value": "48px" },
"16": { "value": "64px" }
}
} Naming convention: Global tokens use descriptive names (blue-500, spacing-4) rather than semantic names. This makes them stable and reusable across different contexts.
Tier 2: Semantic Tokens
Semantic tokens reference global tokens and assign meaning based on usage. They describe what the value is for, not what it is. This abstraction layer is what enables theming—you can change the underlying values without updating every usage.
src/semantic/color.json
{
"color": {
"background": {
"primary": { "value": "{color.white}" },
"secondary": { "value": "{color.gray.50}" },
"inverse": { "value": "{color.gray.900}" }
},
"text": {
"primary": { "value": "{color.gray.900}" },
"secondary": { "value": "{color.gray.600}" },
"inverse": { "value": "{color.white}" },
"link": { "value": "{color.blue.600}" }
},
"border": {
"default": { "value": "{color.gray.200}" },
"focus": { "value": "{color.blue.500}" }
},
"status": {
"error": { "value": "{color.red.600}" },
"warning": { "value": "{color.amber.500}" },
"success": { "value": "{color.green.600}" },
"info": { "value": "{color.blue.500}" }
}
}
}
Notice how semantic tokens reference global tokens using the {color.blue.500} syntax. Style Dictionary resolves these references during build, producing flat output values.
Tier 3: Theme Tokens
Theme tokens override semantic tokens for specific themes. They use the same token names but point to different underlying values.
src/themes/dark.json
{
"color": {
"background": {
"primary": { "value": "{color.gray.900}" },
"secondary": { "value": "{color.gray.800}" },
"inverse": { "value": "{color.white}" }
},
"text": {
"primary": { "value": "{color.gray.100}" },
"secondary": { "value": "{color.gray.400}" },
"inverse": { "value": "{color.gray.900}" },
"link": { "value": "{color.blue.400}" }
},
"border": {
"default": { "value": "{color.gray.700}" },
"focus": { "value": "{color.blue.400}" }
}
}
} Style Dictionary Configuration
The power of Style Dictionary lies in its transform and format system. Here's a production-ready configuration that outputs to CSS, SCSS, JavaScript, and TypeScript:
config/style-dictionary.config.js
const StyleDictionary = require('style-dictionary');
// Custom transform for CSS custom properties
StyleDictionary.registerTransform({
name: 'name/kebab',
type: 'name',
transformer: (token) => token.path.join('-').toLowerCase()
});
module.exports = {
source: ['src/**/*.json'],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'build/css/',
files: [{
destination: 'tokens.css',
format: 'css/variables',
options: {
outputReferences: true
}
}]
},
scss: {
transformGroup: 'scss',
buildPath: 'build/scss/',
files: [{
destination: '_tokens.scss',
format: 'scss/variables'
}]
},
js: {
transformGroup: 'js',
buildPath: 'build/js/',
files: [{
destination: 'tokens.js',
format: 'javascript/es6'
}, {
destination: 'tokens.d.ts',
format: 'typescript/es6-declarations'
}]
},
json: {
transformGroup: 'js',
buildPath: 'build/json/',
files: [{
destination: 'tokens.json',
format: 'json/nested'
}]
}
}
}; Output Examples
Running style-dictionary build produces:
build/css/tokens.css
:root {
--color-background-primary: #ffffff;
--color-background-secondary: #f9fafb;
--color-text-primary: #111827;
--color-text-secondary: #4b5563;
--spacing-1: 4px;
--spacing-2: 8px;
--spacing-4: 16px;
} build/js/tokens.js
export const ColorBackgroundPrimary = "#ffffff";
export const ColorBackgroundSecondary = "#f9fafb";
export const ColorTextPrimary = "#111827";
export const Spacing1 = "4px";
export const Spacing2 = "8px"; Multi-Framework Integration
The beauty of Style Dictionary is that each framework consumes the output format that works best for it:
React (CSS-in-JS)
import {
ColorBackgroundPrimary,
ColorTextPrimary,
Spacing4
} from '@company/tokens';
const styles = {
container: {
backgroundColor: ColorBackgroundPrimary,
color: ColorTextPrimary,
padding: Spacing4
}
}; Vue (CSS Custom Properties)
<style>
@import '@company/tokens/tokens.css';
.container {
background-color: var(--color-background-primary);
color: var(--color-text-primary);
padding: var(--spacing-4);
}
</style> Angular (SCSS)
@import '@company/tokens/tokens';
.container {
background-color: $color-background-primary;
color: $color-text-primary;
padding: $spacing-4;
} Theme Switching Strategy
For runtime theme switching, CSS custom properties are the most efficient approach. Build theme-specific CSS files and swap them via a data attribute:
/* tokens-light.css */
:root, [data-theme="light"] {
--color-background-primary: #ffffff;
--color-text-primary: #111827;
}
/* tokens-dark.css */
[data-theme="dark"] {
--color-background-primary: #111827;
--color-text-primary: #f9fafb;
} // Theme toggle implementation
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}
// Initialize from preference
const savedTheme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
setTheme(savedTheme); CI/CD Integration
Token builds should be part of your continuous integration pipeline:
.github/workflows/tokens.yml
name: Build Tokens
on:
push:
paths:
- 'tokens/src/**'
pull_request:
paths:
- 'tokens/src/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm run build:tokens
- run: npm run test:tokens
- uses: actions/upload-artifact@v3
with:
name: token-build
path: tokens/build/ Pro tip: Add visual regression tests that capture token changes. Tools like Chromatic can detect when token modifications affect component appearance.
Common Pitfalls and Solutions
Pitfall: Circular References
Token A references Token B which references Token A—Style Dictionary will fail.
Solution: Maintain strict tiering. Global tokens never reference other tokens. Semantic tokens only reference global. Theme tokens only reference global or semantic.
Pitfall: Inconsistent Naming
Teams using different naming conventions leads to confusion and bugs.
Solution: Document and enforce naming conventions. Use linting to validate token names match patterns.
Pitfall: Missing TypeScript Types
JavaScript consumers don't get autocomplete or type checking.
Solution: Always generate TypeScript declarations alongside JavaScript output. Use the typescript/es6-declarations format.
Pitfall: Over-Tokenization
Creating tokens for every possible value leads to token bloat and confusion.
Solution: Only tokenize values that need to be consistent or themed. One-off values can remain hardcoded.
Conclusion: Tokens Are Infrastructure
Design tokens are the infrastructure layer of your design system. Like any infrastructure, they require careful architecture, consistent maintenance, and clear documentation. Style Dictionary provides the tooling to manage this complexity at scale.
The three-tier architecture—global, semantic, and theme—gives you the flexibility to evolve your visual design without disrupting consuming applications. Multi-format output ensures every framework in your organization can consume tokens in the most natural way.
Invest in your token architecture early. The patterns you establish will shape your design system's maintainability for years to come.
Need help setting up your token architecture?
We've implemented Style Dictionary across dozens of enterprise organizations. Let's discuss how to structure tokens for your specific tech stack.
Get in Touch