What is a design token?
Instead of scattering #14b8a6 across 40 rules, you give that value a name —
--color-accent — and use the name. The name is the token. It becomes a
single source of truth: update the token once and everything that uses it changes.
Designers call these “variables” in Figma; in CSS they're the same idea.
Hard-coded values (the hard way)
.btn { background: #14b8a6; }
.link { color: #14b8a6; }
.badge { border: 2px solid #14b8a6; }
/* change the brand color? edit it in 3+ places... */
Tokens (the smart way)
:root { --color-accent: #14b8a6; } /* define once */
.btn { background: var(--color-accent); }
.link { color: var(--color-accent); }
.badge { border: 2px solid var(--color-accent); }
/* change the brand color? edit ONE line. */
Define & use your first tokens
Declare tokens on :root (the top of the document) with a --name, then read them
anywhere with var(--name). This button's color, padding, and corner radius are all tokens.
<button class="btn">I'm built from tokens</button>
<style>
:root {
--color-accent: #14b8a6;
--space-md: 14px;
--radius: 10px;
}
.btn {
background: var(--color-accent);
color: #fff;
padding: var(--space-md) calc(var(--space-md) * 1.8);
border: 0;
border-radius: var(--radius);
font-weight: 700;
font-size: .95rem;
font-family: inherit;
cursor: pointer;
}
</style>
-- then lowercase words with dashes. Group by purpose: --color-*, --space-*, --text-*, --radius-*.The usual token categories
Most design systems token-ize the same handful of things. Define a small, consistent set and reuse it everywhere.
Color
Spacing scale
--space-1: 4px--space-2: 8px--space-3: 16px--space-4: 32pxType scale
:root {
/* color */
--color-bg: #0f172a; --color-accent: #14b8a6;
--color-danger: #f43f5e; --color-muted: #e5e7eb;
/* spacing scale */
--space-1: 4px; --space-2: 8px; --space-3: 16px; --space-4: 32px;
/* type scale */
--text-sm: .875rem; --text-base: 1rem;
--text-lg: 1.5rem; --text-xl: 2.25rem;
/* radius & shadow */
--radius-sm: 6px; --radius-lg: 16px;
--shadow: 0 10px 30px rgba(0,0,0,.2);
}
Primitive vs. semantic tokens
Pro systems use two layers. Primitive tokens hold raw values (the palette). Semantic tokens describe a job and point at a primitive — this is called aliasing. Components use the semantic names, so swapping the palette doesn't touch them.
:root {
/* primitives — the raw palette (reference only) */
--teal-500: #14b8a6;
--slate-900: #0f172a;
/* semantic — what each value is FOR (alias a primitive) */
--color-accent: var(--teal-500);
--color-bg: var(--slate-900);
}
/* components only ever use the semantic names */
.btn { background: var(--color-accent); }
--color-accent to a different primitive — every button, link, and badge updates, and nothing breaks because they never referenced the raw color directly.Theming with modes (light / dark)
This is where tokens shine. Define the same token names with different values under a theme selector. Your components never change — only the tokens do. Click the button to flip this demo.
Account settings
Every color here comes from a token. The card, text, and button don't know the theme — they just read var(--…).
<div class="theme" id="themeDemo">
<div class="card">
<h4>Account settings</h4>
<p>Every color here comes from a token. The card, text, and button don't know the theme — they just read <code>var(--…)</code>.</p>
<button class="btn">Save changes</button>
</div>
<button class="theme-toggle" onclick="document.getElementById('themeDemo').classList.toggle('dark')">Toggle light / dark</button>
</div>
<style>
/* light is the default set of token values */
.theme {
--bg: #ffffff; --surface: #f5f5f7; --text: #1d1d1f;
--muted: #6b7280; --accent: #14b8a6; --line: #e5e5ea;
background: var(--bg); border: 1px solid var(--line);
border-radius: 14px; padding: 22px; transition: all .3s;
}
/* dark mode just redefines the SAME tokens */
.theme.dark {
--bg: #0f172a; --surface: #1e293b; --text: #f1f5f9;
--muted: #94a3b8; --accent: #2dd4bf; --line: #334155;
}
/* components read the tokens and never change */
.card { background: var(--surface); color: var(--text); border: 1px solid var(--line); padding: 18px; border-radius: 10px; }
.card h4 { color: var(--text); margin-bottom: 6px; }
.card p { color: var(--muted); font-size: .88rem; margin: 0 0 14px; }
.btn { background: var(--accent); color: #04201c; font-weight: 700; border: 0; padding: 9px 18px; border-radius: 8px; font-family: inherit; cursor: pointer; }
.theme-toggle { margin-top: 16px; background: rgba(20,184,166,.12); color: #5eead4; border: 1px solid rgba(20,184,166,.3); padding: 9px 16px; border-radius: 8px; font-weight: 600; font-family: inherit; cursor: pointer; }
</style>
<html data-theme="dark"> and toggle it with one line of JavaScript — document.documentElement.dataset.theme = 'dark'.Why tokens are worth it
Change once
Edit one token and it updates everywhere it's used — no find-and-replace.
Consistency
Everyone pulls from the same values, so spacing and color stay uniform.
Easy theming
Light/dark or whole re-brands are just a new set of token values.
Design ↔ code
Figma variables map straight to CSS custom properties — one shared language.