Design Tokens: The Foundation of a Scalable Design System
Design tokens are the atoms of your design system — single source-of-truth values that make rebrandable, consistent, maintainable UI possible at scale.
Nextcraft Engineering Team
What Design Tokens Actually Are
A design token is a named design decision stored in a technology-agnostic format. Instead of hardcoding #3B82F6 as your primary blue in every component, you define:
{
"color": {
"brand": {
"primary": { "value": "#3B82F6" }
}
}
}
And reference color.brand.primary everywhere. When the primary blue changes — new brand refresh, white-label requirement, accessibility fix — you change it once, in one place.
This sounds simple. The implications are profound at scale.
The Three Tiers of Tokens
A well-structured token system has three tiers, each building on the previous:
Tier 1: Primitive Tokens (Raw Values)
{
"color": {
"blue-500": "#3B82F6",
"blue-600": "#2563EB",
"gray-100": "#F3F4F6",
"gray-900": "#111827"
},
"space": {
"4": "16px",
"8": "32px"
}
}
These are just named values with no semantic meaning. Never use these directly in components.
Tier 2: Semantic Tokens (Intent)
{
"color": {
"text": {
"primary": "{color.gray-900}",
"secondary": "{color.gray-600}",
"inverse": "{color.white}"
},
"surface": {
"brand": "{color.blue-500}",
"brand-hover": "{color.blue-600}"
}
}
}
Semantic tokens describe the intent, not the value. This is what your components reference.
Tier 3: Component Tokens (Specific Overrides)
{
"button": {
"primary": {
"background": "{color.surface.brand}",
"background-hover": "{color.surface.brand-hover}",
"text": "{color.text.inverse}"
}
}
}
Component tokens let specific components deviate from semantic defaults without hardcoding values.
Why This Structure Enables Dark Mode
Dark mode becomes trivial when your components only reference semantic tokens. You just swap the semantic token values:
{
"light": {
"color.text.primary": "{color.gray-900}",
"color.surface.default": "{color.white}"
},
"dark": {
"color.text.primary": "{color.gray-100}",
"color.surface.default": "{color.gray-900}"
}
}
Every component that uses color.text.primary automatically adapts. No component-level dark mode logic needed.
Implementing Tokens in a Next.js + Tailwind Project
With Tailwind, the most practical approach is to extend the theme with your semantic tokens:
// tailwind.config.ts
export default {
theme: {
extend: {
colors: {
brand: {
DEFAULT: 'hsl(var(--color-brand))',
hover: 'hsl(var(--color-brand-hover))',
},
surface: {
DEFAULT: 'hsl(var(--color-surface))',
elevated: 'hsl(var(--color-surface-elevated))',
},
text: {
primary: 'hsl(var(--color-text-primary))',
secondary: 'hsl(var(--color-text-secondary))',
},
},
},
},
};
Then define the actual values as CSS custom properties, swapped per theme:
/* globals.css */
:root {
--color-brand: 217 91% 60%;
--color-brand-hover: 221 83% 53%;
--color-surface: 0 0% 100%;
--color-surface-elevated: 210 40% 98%;
--color-text-primary: 222 47% 11%;
--color-text-secondary: 215 16% 47%;
}
.dark {
--color-surface: 222 47% 11%;
--color-surface-elevated: 217 33% 17%;
--color-text-primary: 210 40% 98%;
--color-text-secondary: 215 20% 65%;
}
Components use Tailwind classes like bg-surface, text-text-primary, bg-brand — and the theme switch is handled entirely at the CSS layer.
The White-Label Superpowe
Once you have a token system, white-labeling (providing your product under a client's brand) becomes a configuration problem, not an engineering problem.
For each client, you provide a different set of primitive and semantic token values. The component library doesn't change. Only the tokens do.
This is how enterprise SaaS companies like Intercom and Linear power-white-label offerings without maintaining parallel codebases.
Token Management at Scale
For larger teams, manage tokens with dedicated tooling:
- Style Dictionary (Amazon) — transforms tokens from JSON into CSS, SCSS, JS, Swift, and Android formats
- Tokens Studio (Figma plugin) — syncs tokens between Figma and code
- Theo (Salesforce) — an alternative to Style Dictionary with different transformation capabilities
The key workflow: designers own the token values in Figma, engineers consume them in code, and a CI step ensures they stay in sync. Any drift between design and code is caught before it ships.
Tokens Are a Team Protocol
The real value of design tokens is not technical — it's organizational. They create a shared vocabulary between designers and engineers.
When a designer says "this uses the surface-elevated token," engineers know exactly which CSS variable applies. When an engineer sees bg-surface-elevated in code, they can look up the token and understand the design intent.
This shared language reduces the back-and-forth, the "this doesn't match the mockup," and the "which blue is that?" conversations. At scale, that efficiency compounds significantly.
Start with 20 tokens covering your core colors, spacing scale, and typography. Add tokens when you find yourself hardcoding a value more than twice. Resist the urge to tokenize everything immediately — over-tokenized systems are as hard to maintain as no tokens at all.
Continue reading
Related articles
Why Next.js App Router Is Better for SEO Than Pages Router
The App Router isn't just a new file-system convention — it fundamentally changes how search engines crawl and index your Next.js application.
EngineeringServer Components vs Client Components: Making the Right Call
The boundary between Server and Client Components is the most consequential architectural decision you make in a Next.js application. Here's how to draw it correctly.
EngineeringBuilding High-Performance Next.js Applications for Scale
A deep dive into how we utilize App Router and React Server Components to scale our client builds effectively.
Stay Informed.
Join 1,200+ founders and engineers receiving our monthly deep dives on product engineering, design, and growth.