· Working ·
What are design tokens? Raw, alias and semantic
A design token is a named design decision that Figma and code share. This guide covers the three layers (raw, alias, semantic), how tokens differ from CSS variables, and how to set them up in Figma Variables, Tailwind v4 and the W3C token format, using this site's real tokens.
A design token is a named design decision, such as --muted-foreground set to #595959. Components reference the name and never the raw value. Organise tokens into three layers (raw, alias, semantic) and you can change the brand colour or add dark mode in one place, without editing a single component.
I'm Hai Le, a UI/UX designer who writes code. This site keeps more than 160 colour variables in app/globals.css, split into exactly those three layers. Every example below is copied from that file.
What is a design token, in plain terms?
The W3C Design Tokens Community Group defines a token as "information associated with a human readable name, at minimum a name/value pair". The spec's own example is color-text-primary: #000000.
That definition is accurate, and it leaves out the useful part. The name carries the value of a token. #595959 is a hex code. muted-foreground tells you this is secondary text, one step below body text, and that every place needing secondary text should use it. When I change the hex code, the name keeps that promise.
Most people picture a palette of hex codes when they hear "design tokens". The palette is only the visible part. Whether your system survives its first rebrand depends on how the names are layered.
How are design tokens different from CSS variables?
A CSS variable is a storage mechanism. A token is a convention about which layer a value belongs to and who may reference it. Two projects can both use CSS variables while only one has a token system; the other has nicely named constants.
| CSS variable | Design token | Figma Variable | |
|---|---|---|---|
| What it is | A CSS feature: --name: value | A naming and layering convention for design decisions | A Figma feature for storing reusable values |
| Where it lives | CSS files, read by the browser | Anywhere: CSS, JSON, Figma | The Figma file |
| How it references others | var(--other) | Depends on format, e.g. {brand.500} in JSON | Aliasing to a variable of the same type |
| How it handles themes | Overrides in a selector such as .dark | Depends on the tool | Modes within a collection |
CSS variables are one place tokens can live. On this site, tokens live in CSS variables and are explained in prose in DESIGN.md.
What are raw, alias and semantic tokens?
Raw tokens hold values and nothing else. --gray-600 is #717171. It has no idea whether it will end up as text or as a background.
Alias tokens give a whole ramp a role. --neutral-* is the neutral ramp, --primary-* is the brand ramp, --error-* is the error ramp. Each is still an 11-step scale, now with intent.
Semantic tokens are what components touch: --background, --foreground, --muted-foreground, --border, --primary. The name says where the token is used. The layers below decide the actual colour.
Three real lines from app/globals.css, forming one chain from value to component:
/* LAYER 0 — raw: value only */
--gray-700: #595959;
/* LAYER 1 — alias: a ramp with a role */
--neutral-700: var(--gray-700);
/* LAYER 2 — semantic: components touch this */
--muted-foreground: var(--neutral-700);How the layers split responsibility on this site:
| Layer | Examples | Who may reference it | When it changes |
|---|---|---|---|
| Raw | --brand-500: #ff009d, --gray-700: #595959 | The alias layer only | Rebrands, contrast fixes |
| Alias | --primary-500, --neutral-700, --error-700 | Semantic tokens, occasionally components | Rarely |
| Semantic | --primary, --muted-foreground, --destructive | Components | When a role changes, e.g. a darker error button |
The semantic layer only contains var() references to the alias layer, never a hex code. That rule sits in a comment at the top of the CSS file, where both people and AI agents editing it will see it.
Why keep the alias layer in the middle?
Skipping the middle layer, with semantic tokens pointing straight at raw values, looks like a time saver. The bill arrives at the first rebrand. Someone finds half the codebase typing raw values where it should have used semantic ones, and the only way back is a manual find-and-replace.
The alias layer prevents that with one promise: a step number means the same thing everywhere. I number this site's neutral ramp the Radix way. neutral-50 is always the background and neutral-950 is always the strongest text, in light and dark mode alike. The step measures distance from the background. In dark mode, neutral-950 is the lightest colour on the page.
That means dark mode only overrides the raw layer:
.dark {
--gray-50: #111111; /* dark background */
--gray-700: #a8a8a8; /* secondary text on dark */
--gray-950: #ededed; /* primary text on dark */
}--muted-foreground still points at --neutral-700, which still points at --gray-700. No component needs to know dark mode exists. How to pick the steps for a ramp like this is covered in the Radix colour scale, and the harder dark mode questions are in dark mode FAQ.
Never let a component reference the raw layer (bg-gray-600). Always go through a semantic token (bg-muted), even when both point at the same value today. On this site the raw layer is deliberately left out of the Tailwind theme, so bg-gray-600 does not exist to be typed by mistake.
How do you set up design tokens in Figma Variables and in code?
In Figma
Figma stores tokens as variables. Its help centre lists six variable types: color, number, string and boolean, plus the newer timing and easing types for motion. Variables sit in collections, and each collection can have several modes such as Light and Dark, with one value per variable per mode. How many modes you get per collection depends on your Figma plan.
To build the three layers in Figma:
- Create a
Rawcollection holding the raw ramps. Hide it from library consumers where you can, so nobody paints with it directly. - Create a
Semanticcollection with Light and Dark modes. Each variable aliases a variable inRaw. Figma only allows aliasing to a variable of the same type. - Name semantic variables exactly as they are named in code:
muted-foreground,border,primary. - In each variable's Code syntax section, add the code name for Web, Android or iOS. For Web, include the
var()wrapper, e.g.var(--muted-foreground), so Dev Mode shows the variable instead of a hex value.
Step 4 is the one teams skip, and it is what lets a developer or an AI agent read the right token name out of a Figma file. If you work with Claude Code, Figma MCP with Claude Code shows how to pull variables into CSS tokens without creating duplicates.
In code, with Tailwind v4
Tailwind v4 is configured in CSS. You declare theme variables inside @theme, and Tailwind generates matching utilities. A variable in the --color-* namespace produces bg-*, text-*, border-* and the other colour utilities.
This site uses @theme inline and exposes only the alias and semantic layers:
@theme inline {
--color-background: var(--background);
--color-muted-foreground: var(--muted-foreground);
--color-primary: var(--primary);
--color-neutral-700: var(--neutral-700);
}According to the Tailwind docs, the inline option makes utilities use the variable's value instead of referencing the theme variable. That matters when your variables point at other variables, as the three-layer chain does. Inside components I only ever write classes like text-muted-foreground or hover:bg-muted.
In a JSON token file
If tokens need to reach iOS, Android or a build tool, the standard format is the W3C Design Tokens Format Module (version 2025.10). This site has no JSON token file, so here is how my brand token would look under the spec:
{
"brand": {
"$type": "color",
"500": {
"$value": {
"colorSpace": "srgb",
"components": [1, 0, 0.616],
"hex": "#ff009d"
}
}
},
"primary": {
"$type": "color",
"$value": "{brand.500}",
"$description": "Primary button fill. Not for small text."
}
}A colour $value is an object with a colour space and numeric components, and hex is optional. References use curly braces, as in {brand.500}. Style Dictionary has supported this format since version 4.
Tokens give exact values, and AI agents also need the reasoning behind them. What is DESIGN.md explains how I write that layer.
What are the most common design token mistakes?
Naming by value. --pink and --gray-dark start lying the day you change the colour. Semantic names describe a role: --primary, --muted-foreground.
Letting components reach the raw layer. Every hand-typed #717171 in a component is one spot dark mode will miss.
One token doing two jobs. The brand magenta #ff009d reaches only 3.66:1 contrast on white. That works as a button fill under white text and fails WCAG AA for small text. I split it in two: --primary for fills, and --primary-text (#be0074, 6.09:1) for magenta text at 20px and below.
Adding a token every time a design asks for one. This site has three surface values (#ffffff, #f8f8f8, #efefef) and four text tiers. When I catch myself wanting a fourth surface, the layout is usually what needs fixing.
Different names in Figma and code. Design says Text/Secondary, code says muted-foreground, and every handoff needs a lookup table. Use one name from the start and record the code name under Code syntax.
Overriding semantic tokens for dark mode. If the ramp is numbered by distance from the background, dark mode only touches the raw layer. Overriding semantic tokens one by one is a sign the ramp was designed for one mode.
Where should you start?
If your Figma file has a loose pile of styles, do not migrate everything at once. Start with five semantic tokens: background, text, secondary text, border, primary. Point each at a step in a raw ramp. Once all five work in light and dark, add error, success and warning. New to Figma? Learn Figma from zero covers the basics first.
I built a system of more than 50 components for Joyme, and the hardest part was always agreeing on token names between Figma and code. You can see how that went in the Joyme case study. If your team needs someone to build a design system that holds up in both Figma and code, here is more about me.
Sources
- Design Tokens Format Module 2025.10, W3C Design Tokens Community Group: token definition, file extensions,
$value,$type, references. Final Community Group Report, 28 October 2025. Accessed 2026-10-04. - W3C Design Tokens Community Group.
- Figma: Overview of variables, collections, and modes: the six variable types, collections, modes, aliasing. Accessed 2026-10-04.
- Figma Plugin API: setVariableCodeSyntax: Web, Android and iOS code syntax platforms.
- Tailwind CSS: Theme variables:
@theme, the--color-*namespace, theinlineoption. Accessed 2026-10-04. - Style Dictionary: DTCG support.
app/globals.cssandDESIGN.mdin this site's repository.
Frequently asked questions
- Are design tokens only for colour?
- No. The W3C Design Tokens Format Module defines types such as color, dimension, font family, font weight, duration, cubic Bézier and number, plus composite types like border, shadow and typography. Colour is where most teams start because it is the most visible.
- Are Figma Variables the same thing as design tokens?
- Figma Variables are how Figma stores tokens inside a design file, with collections, modes such as light and dark, and aliasing between variables of the same type. A token is the broader idea. The same token can live in Figma, in CSS and in a JSON file.
- Is there a standard file format for design tokens?
- Yes. The W3C Design Tokens Community Group published the Design Tokens Format Module 2025.10 on 28 October 2025. Files use the .tokens or .tokens.json extension, each token has a $value and usually a $type, and references use curly braces such as {brand.500}.
- Does a small project need all three layers?
- I would set them up from day one because the cost is low: the alias layer is a few dozen lines pointing at raw values. What can wait is build tooling such as Style Dictionary, which only pays off once tokens ship to several platforms.
You might also like
What is Claude Code? Install it and run your first session, no coding needed
A designer's walkthrough for people who have never opened a terminal: what Claude Code is, what you need, installing on macOS and Windows, a safe first session, and fixes for the usual errors. Every command comes from Anthropic's docs.
Working· 2026-10-04
Claude Code for designers: turning a Figma design into a working page
How I use Claude Code as a designer to get from Figma frames to a real, maintained website: what to set up, how to write rules the agent follows, a six-step workflow, and the mistakes I made building this site.
Working· 2026-10-04
What is Claude? A guide for people who have never used AI
Claude is Anthropic's AI assistant. This guide covers what it can do, how the free and paid plans differ, how to run your first conversation, and where Claude tends to get things wrong. Every feature and price was checked against Anthropic's own pages.
Working· 2026-10-04