search⌘K

Design Tokens

Named decisions that keep color, type, and spacing consistent across products — primitives, theme aliases, and accents, browsable like Figma Variables.

What is a design token?

A design token is a named decision — a color, size, radius, or type style — stored as a variable instead of a raw value. Tokens turn one-off hex codes and pixel numbers into a shared vocabulary that design and code can both reference, so a change in one place updates every surface that depends on it.

Our system is built on a three-layer architecture: Primitives → Semantic (Theme) → Accent. Each layer has a distinct job, and tokens only ever reference the layer directly below them — never skip a layer, and never hardcode a raw value where a token should be used.

This structure exists to solve a specific problem: raw values tell you what a value is, but not why it was chosen or where it should be used. Layering tokens lets us update a decision in one place and have it propagate everywhere it’s used, while keeping the reasoning behind each decision visible and searchable.

Primitives

Semantic / Theme

Accent

blue-500color-actionaccent-primary
gray-100color-surfaceaccent-surface
red-600color-danger(theme override)

Theme (semantic) tokens pull from both the primitive palette and the accent ramp. A surface might alias neutral/0, while an action fill aliases accent/500 — so swapping the active accent scheme recolors branded UI without rewriting every component style.

Layer 1: Primitives

What they are: The raw material of the system — every color, spacing unit, font size, radius, and other base value we’ve decided to allow into the system. Primitives have no meaning attached to them; they’re just a name for a value.

Naming convention: {category}-{scale-step} Examples: blue-500 gray-100 space-4 radius-md

Rules for this layer:

  • Primitives never reference anything — they’re the floor of the system.
  • A primitive is a value, not a decision. blue-500 doesn’t know if it’s for buttons, links, or icons — that’s the semantic layer’s job.
  • New primitives should only be added when a value is genuinely missing from the ramp, not to solve a one-off design need. If you need a color that isn’t in the ramp, that’s usually a sign to either use an adjacent step or flag it in design system review — not to add a new primitive.
  • Each color ramp includes both solid scale steps (25–975) and alpha steps (a5, a10, a20, a40) of that same base color — see Transparency / opacity below.

Why this layer exists: It gives us one place to see the entire palette / scale at a glance, and one place to update a raw value (say, adjusting a color ramp for better contrast) without touching anything that depends on it directly.

Layer 2: Semantic / Theme

What they are: Tokens that carry meaning and intent. This is where a primitive gets assigned a job. Semantic tokens are what most components and designers should actually be reaching for day-to-day.

Naming convention: {property}-{role}[-{state}] Examples: color-action color-danger color-surface-raised color-action-hover

Rules for this layer:

  • Every semantic token must reference a primitive — never a raw hex/px value.
  • Semantic tokens are where theming happens: switching from light to dark theme, or between product surfaces, means swapping which primitives the semantic layer points to, without touching components at all.
  • A semantic token should describe purpose, not appearance. color-danger is correct; color-red is not — if we ever needed to make danger states orange for accessibility reasons, a name like color-red would become actively misleading.

Why this layer exists: It’s the translation layer between “here’s a value” and “here’s a decision.” It answers the question a primitive can’t: when should I use this?

Layer 3: Accent

What it is: Brand- and variant-level tokens that sit on top of the semantic layer. Where semantic tokens define system-wide roles (danger, surface, action), accent tokens define the specific personality applied across those roles for a given brand, product surface, or variant.

Naming convention: accent-{role} Examples: accent-primary accent-hover accent-surface

Rules for this layer:

  • Accent tokens reference semantic tokens, not primitives directly.
  • This is the layer to touch if we ever need to support multiple brand skins or product-line variants without duplicating the whole token set — one accent swap should be able to shift the “personality” of the product without breaking the underlying semantic logic.
  • Not every component needs an accent token. Reserve this layer for the handful of roles that genuinely vary by brand/variant (primary actions, key surfaces) rather than applying it broadly.

Why this layer exists: It isolates brand-specific decisions from system-wide decisions, so a re-brand or new product variant doesn’t require touching semantic logic or components.

How a token resolves, end to end

Example — a primary button’s background color:

accent-primary
color-actionsemantic
blue-500primitive
#3B82F6raw value

If we rebrand, we change what accent-primary points to. If we redesign our action color system-wide, we change what color-action points to. If we adjust the blue ramp itself, we change blue-500. Each change has a clear, contained blast radius.

Transparency / opacity

Opacity lives in the primitive layer, and shows up in two places:

1. Within each color ramp, as alpha steps alongside the solid scale steps. Each ramp (red, blue, neutral, etc.) carries its standard solid steps (25 through 975) plus a set of alpha variants (a5, a10, a20, a40) that apply a percentage of transparency to that ramp’s base color. For example, in the red ramp:

NameRaw valueOpacity
red-500#C01717100%
red-a5#C017175%
red-a10#C0171710%
red-a20#C0171720%
red-a40#C0171740%

This means transparency is color-aware at the primitive level — red-a20 is specifically “red at 20%,” not a generic overlay value. Every color ramp in the system follows this same pattern, so any ramp can supply its own alpha steps without reaching into a different group.

2. As a dedicated alpha group, for general-purpose, color-agnostic opacity — the “scrim/overlay” use case rather than a color-specific one. This group is split into light and dark subgroups (6 steps each), used for things like overlays and disabled states that need to sit correctly on either a light or dark surface rather than being tied to a specific hue.

Why both exist: the in-ramp alpha steps (red-a20, blue-a40, etc.) are for when a specific color needs to be shown at partial opacity — a tinted hover state, a colored badge background. The alpha group is for neutral, surface-relative transparency — a modal backdrop or disabled-state veil that isn’t tied to any particular hue and needs a light and a dark version to work on either surface.

Percentage-to-hex reference, for translating between the visible percentage and the underlying hex suffix when needed (e.g. exporting to a platform that expects 8-digit hex):

OpacityHex suffix
5%0D
10%1A
20%33
40%66
60%99
80%CC
100%FF

Semantic tokens compose a base color primitive with an alpha primitive where needed, e.g.:

overlay-scrim = neutral-900 @ alpha-dark-60

badge-danger-subtle = red-a20

Documentation for each alpha/transparent token should show the swatch against both a light and dark background, since transparency only reads correctly relative to what’s behind it — and should list the raw hex value alongside the plain-language opacity percentage, as shown in the Primitive collection view.

What every token’s documentation entry should include

In the reference browser below, each row shows:

  1. Name — the CSS custom property, with a live swatch when the token is a color
  2. Alias — the target and resolved value on the line below (use the copy button on hover to grab the CSS variable)

Use Collections and Groups to navigate the way you would in Figma Variables — start from All, or drill into a hierarchy path when you know the group you need.

Token reference

Browse collections and groups, inspect name and alias, and copy the CSS variable with the button on each row.

Collections

Groups

theme

search

Name

bg / solid

--color-bg-solid-1

neutral/25

#FAFAFA

--color-bg-solid-2

neutral/50

#F5F5F5

--color-bg-solid-3

neutral/100

#E5E5E5

--color-bg-solid-base

neutral/0

#FFFFFF

#

--color-bg-solid-tonal

{accent.25}

#

--color-bg-solid-accent

{accent.strong}

--color-bg-solid-inverse

neutral/975

#141414

#

--color-bg-solid-popover

{bg.solid.base}

When to create a new token

Before adding a new token, ask:

  • Does this pattern repeat across 3+ components? If yes, it’s a candidate for promotion to semantic (or accent, if brand-specific). If it’s a one-off, it probably doesn’t need a token yet.
  • Can an existing token, one step up or down in scale, do the job? Prefer reusing an adjacent step over introducing a new one.
  • Is this a system-wide decision or a brand-specific one? This determines whether it belongs in semantic or accent.

New tokens should be proposed and reviewed the same way a new component would be — not added ad hoc inside a single feature build.

Quick reference: layer summary

LayerAnswersReferencesChanges when…
PrimitiveWhat values exist?Nothing (raw values)The underlying scale/ramp changes
SemanticWhen do I use this?PrimitivesSystem-wide design decisions change
AccentWhat's this brand's personality?Semantic tokensBrand, theme, or product variant changes