Design tokens get pitched as the solution to design-dev consistency, and they genuinely can be — but most implementations I've seen fail the same way: a flat list of color and spacing values that works fine for one product, then becomes a maintenance burden the moment a second theme, a dark mode, or a white-label requirement shows up.
Table of Contents
- What Tokens Actually Are (and Aren't)
- The Three-Tier Token Model
- A Practical Structure
- Handling Dark Mode Without Duplicating Everything
- Tooling: Figma Variables to Code
- Common Mistakes
- When Tokens Are Overkill
- FAQs
- Key Takeaways
What Tokens Actually Are (and Aren't)
A design token is a named, reusable value — a color, a spacing unit, a font size — that stands in for a hardcoded value so it can be changed in one place and propagate everywhere it's used. That's it. The mistake teams make is treating "we have tokens" as itself the accomplishment, without any structure to how those tokens relate to each other or to actual design decisions.
The Three-Tier Token Model
The structure that scales, used across most mature design systems (Material Design, Adobe Spectrum, and most well-run in-house systems converge on some version of this):
Tier 1 — Primitive tokens: raw values with no semantic meaning. blue-500: #3B82F6, space-4: 16px. These rarely change and represent your full available palette/scale.
Tier 2 — Semantic tokens: named for their purpose, referencing primitives. color-primary: {blue-500}, color-danger: {red-500}, space-component-padding: {space-4}. These are what components actually reference, and they're what changes when you rebrand or theme.
Tier 3 — Component tokens (optional, for larger systems): scoped to a specific component when it needs to deviate from the general semantic value. button-primary-background: {color-primary}, but overridable independently if a specific component genuinely needs to diverge.
The reason this three-tier structure matters: without it, teams either hardcode primitives directly into components (losing the "change once, propagate everywhere" benefit entirely) or make semantic tokens reference other semantic tokens in tangled ways that become impossible to trace.
A Practical Structure
{ "color": { "blue": { "500": { "value": "#3B82F6" } }, "red": { "500": { "value": "#EF4444" } } }, "semantic": { "color": { "primary": { "value": "{color.blue.500}" }, "danger": { "value": "{color.red.500}" } } } }
Using a tool like Style Dictionary or Tokens Studio, this JSON becomes the single source of truth, transformed into CSS variables, iOS/Android platform values, or Tailwind config — one definition, multiple platform outputs, instead of manually keeping a Figma palette and a Tailwind config and an iOS color asset catalog in sync by hand.
Handling Dark Mode Without Duplicating Everything
The common mistake: defining a completely separate token set for dark mode, doubling maintenance burden. The better pattern: semantic tokens reference mode-aware primitive values, so the semantic layer (what components use) stays identical between themes, and only the underlying value resolution changes:
:root { --color-primary: var(--blue-500); --color-surface: var(--white); } [data-theme="dark"] { --color-primary: var(--blue-400); --color-surface: var(--gray-900); }
Components reference --color-primary and --color-surface and never need to know which theme is active — the CSS variable resolution handles it. This is the actual payoff of the three-tier model: theming becomes a change at the semantic-to-primitive mapping layer, not a change scattered across every component.
Tooling: Figma Variables to Code
Figma's native Variables feature (not Styles, which are more limited) map cleanly onto this token model — primitive collections, semantic collections referencing them, and mode support built in for light/dark. The remaining gap is getting those variables into code consistently; Tokens Studio's Figma plugin exports to a JSON format Style Dictionary can consume directly, closing the design-to-code loop without manual re-entry, which is where most design-dev consistency actually breaks down in practice.
Common Mistakes
- No semantic layer — components reference primitives directly. This defeats the actual purpose of tokens; rebranding or theming requires touching every component instead of one mapping layer.
- Too many component-level token overrides too early. Component tokens should be the exception for genuine deviations, not the default pattern — overusing them recreates the "everything is a special case" problem tokens were meant to solve.
- Inconsistent naming conventions between design and code. If Figma calls it
primary-500and the codebase calls itbrand-blue, every handoff requires manual translation and drift creeps in immediately. - Treating token setup as a one-time project rather than an owned system. Tokens need an owner who reviews additions — otherwise the primitive tier balloons with one-off values added for a single use case, undermining the whole point.
When Tokens Are Overkill
For a single small product with one designer and one developer working closely together, a full three-tier token pipeline with automated Figma-to-code sync is more infrastructure than the problem warrants — a well-organized Tailwind config or CSS variable set, manually kept in sync, is entirely sufficient. Tokens earn their complexity when you have multiple products, multiple themes, or a design and engineering team large enough that manual sync becomes the actual bottleneck.
FAQs
Do I need a tool like Style Dictionary, or can I just use CSS variables directly? For a single web product, plain CSS variables following the semantic/primitive structure are often sufficient. Style Dictionary and similar tools earn their value when you need the same tokens to output to multiple platforms (web CSS, iOS, Android, React Native) consistently.
How many primitive color values should a design system have? Enough to cover your actual use cases without redundancy — a common starting point is a 9-11 step scale (100-900) per base hue, but the right number depends on how much visual range your product actually needs, not an arbitrary target.
Should spacing tokens use a fixed scale or arbitrary values? A fixed, consistent scale (often based on a 4px or 8px base unit) is strongly preferred — arbitrary spacing values are exactly the inconsistency tokens are meant to eliminate.
Can design tokens work with Tailwind CSS?
Yes — Tailwind's config file is essentially a token definition file already; the three-tier model maps onto Tailwind's theme configuration naturally, with semantic names as your theme.extend values referencing more primitive scale values.
Who should own the design token system, design or engineering? Ideally a shared responsibility with a clear point person, since tokens sit exactly at the design-engineering boundary — a system owned entirely by one side tends to drift out of sync with the other's actual needs.
Key Takeaways
- The three-tier model (primitive → semantic → component) is what makes tokens actually scale; a flat token list doesn't solve the consistency problem tokens are meant to address.
- Semantic tokens referencing mode-aware primitives is the clean way to handle dark mode without duplicating your entire token set.
- Tokens are genuinely valuable at multi-product or multi-theme scale, and often overkill for a single small product — match the tooling investment to actual need.
Related Reading
- Building Scalable Design Systems with React and Tailwind
- UI/UX Design Handbook: Principles That Actually Hold Up
- MVP Development Guide: What Actually Matters in Month One
Next Steps
Building or scaling a design system and want a second opinion on your token architecture? Let's talk.