Skip to main content
Back to module
Design Systems & Developer Handoff · Design Systems

Design tokens

Reading44mLesson 2 of 3 in this module

What you'll learn

  • Define colour, type and spacing tokens
  • Distinguish primitive tokens from semantic tokens and use semantic ones in components
  • Declare tokens as CSS custom properties on :root and read them with var()
  • Name tokens by purpose so themes and rebrands change only the values

Define primitive and semantic tokens for colour, type and spacing, and apply them with CSS custom properties.

Why it matters

Design tokens are the smallest, most reused decisions in a product: the exact blue of the primary button, the body text size, the gap between a label and its input. When those decisions live as hard-coded values scattered across hundreds of files and Figma layers, changing one (a rebrand, a dark mode, an accessibility fix) means hunting down every copy. Design tokens give each decision a name and a single source, so designers and developers use the same value and change it in one place.

The core idea: name the decision, not the value

A token is a name-value pair: color-primary = #2563eb. The name is what people use. The value can change without anyone editing the places that use it.

Most systems use two (sometimes three) tiers:

TierPurposeExamples
Primitive (core, global)The palette and raw scales, named by what they areblue-600: #2563eb, space-3: 16px
Semantic (alias)What a value is for, pointing at a primitivecolor-primary: blue-600, color-text: gray-900
Component (optional)One component's specific usebutton-padding-x: space-4

Designers and developers should mostly use semantic tokens. A button uses color-primary, not blue-600. Then a dark theme or a rebrand only changes which primitive each semantic token points at.

Tokens in Figma and in code

In Figma (free plan included), variables store tokens: colour, number and string variables, organised into collections, with modes for themes such as light and dark. Styles (colour, text, effect) are an older, still common way to share values. Figma's free plan has some limits on modes, so check the current plan details if you need many themes.

In code, the simplest form of tokens is CSS custom properties (CSS variables):

CSS
:root {
  --blue-600: #2563eb;          /* primitive */
  --color-primary: var(--blue-600);  /* semantic points at primitive */
  --space-3: 16px;
}

.button {
  background: var(--color-primary);
  padding: var(--space-2) var(--space-4);
}

A custom property is declared with two dashes, usually on :root (the html element) so it is available everywhere, and read with var(). Custom properties cascade and can be overridden, which is how themes work:

CSS
:root[data-theme="dark"] {
  --color-text: #f9fafb;
  --color-surface: #111827;
}

Many teams keep tokens in a JSON file and use a build tool (Style Dictionary is a popular free one) to generate CSS, iOS and Android formats from the same source. The W3C Design Tokens Community Group is working on a standard JSON format so tools can share tokens.

Worked example: a small token set

Colour primitives   blue-600 #2563eb   blue-700 #1d4ed8   gray-900 #111827   gray-100 #f3f4f6   white #ffffff
Colour semantic     color-primary -> blue-600        color-primary-hover -> blue-700
                    color-on-primary -> white        color-text -> gray-900
                    color-surface -> white           color-background -> gray-100
Type                font-size-body 16px   font-size-heading 28px
Spacing (4/8 base)  space-1 4px   space-2 8px   space-3 16px   space-4 24px
Radius              radius-md 12px

Note the spacing tokens are named by step, not by pixel value (space-3, not space-16), so the scale can be tuned later without names becoming lies. Check that each semantic colour pairing meets contrast requirements: white on #2563eb passes WCAG AA for normal text (about 5.2:1).

Naming tips

  • Use a consistent pattern: category-role-variant-state, such as color-primary-hover.
  • Name by purpose for semantic tokens (color-danger), never by appearance (color-red), so a theme can change the appearance.
  • Keep the set small. Every token is a decision someone must understand.
  • Document which pairs are safe together (text on surface, on-primary on primary).

Common mistakes

  • Using primitives directly in components. Themes then need every component changed.
  • Appearance names for semantic tokens. color-blue-button breaks the moment the button turns green.
  • Tokens only in Figma or only in code. They must match; one source should generate the other, or be checked regularly.
  • Hundreds of one-off tokens. If a value is used once, it probably does not need a token.
  • Ignoring contrast when defining colour pairs.

Quick recap

  • Tokens name design decisions so they are used and changed from one source.
  • Primitive tokens hold raw values; semantic tokens say what a value is for and point at primitives.
  • In Figma, use variables (with modes for themes); in code, CSS custom properties on :root, read with var().
  • Name by purpose, keep the set small, and keep design and code in sync.

Try it

On the Do tab you will replace hard-coded values in a small page with colour, type and spacing tokens written as CSS custom properties.

Key terms (5)

design tokens
Named values for design decisions such as colour, type, spacing and radius, kept in one source and used by both design and code.
primitive
A token tier that holds raw values named by what they are, such as blue-600 or space-3.
semantic
A token tier that says what a value is for, such as color-primary, and points at a primitive.
CSS custom properties
CSS variables declared with two leading dashes, usually on :root, and read with var().
modes
Figma variable settings that let one collection hold different values per theme, such as light and dark.

Recap

  • Tokens name design decisions so they are used and changed from one source.
  • Primitive tokens hold raw values; semantic tokens say what a value is for and point at primitives.
  • Components should use semantic tokens, so a theme or rebrand only remaps them.
  • In Figma use variables with modes; in code use CSS custom properties on :root, read with var().
  • Name by purpose, keep the set small, check contrast and keep design and code in sync.
Also in this lesson: