---
name: applying-design-tokens
description: Applies design tokens correctly instead of hardcoding colours, spacing, radii or type sizes. Covers semantic-over-primitive selection, dark mode safety, and the token resolution order. Use when styling any component, choosing a colour or spacing value, writing CSS or Tailwind classes, converting a design to code, or when the user mentions tokens, theming, dark mode, or a rebrand. Applies before writing any literal hex, px, or rem value in UI code.
license: CC0-1.0
---

# Applying design tokens

Hardcoded values look correct in light mode, wrong in dark mode, and wrong
again after the next rebrand. This is the most common defect in agent-written
UI and the easiest to prevent, because it is decidable: a literal is either
there or it is not.

## Fill this in first

- Token source of truth: `{{tokens/tokens.json}}` (W3C DTCG)
- CSS custom properties: `{{app/globals.css}}`
- Token lookup: `{{npm run tokens:list}}` or the MCP tool `{{list_tokens}}`
- Lint: `{{pnpm lint}}`

## The rule

Never write a literal colour, spacing value, radius, font size or shadow.

| Wrong | Right |
|---|---|
| `color: #FF3B30` | `color: var(--color-text-danger)` |
| `padding: 16px` | `padding: var(--space-4)` |
| `border-radius: 8px` | `border-radius: var(--radius-md)` |
| `bg-blue-500` | `bg-primary` |
| `dark:text-white` | a semantic token that already flips |

A manual `dark:` override is a token failure in disguise. If you need one, the
token layer is missing a role — say so rather than patching it at the call
site.

## Choose the semantic layer, not the primitive

Tokens come in layers. Reach for the one that names intent.

1. **Semantic** — `color.action.primary`, `space.inset.md`. Always prefer these.
2. **Component** — `button.background`. Use only when one surface genuinely
   needs an override.
3. **Primitive** — `blue.600`, `size.4`. Never consume directly in a
   component. They exist so semantics have something to point at.

An agent shown `blue.500` will use `blue.500`. If your docs list primitives
first, they will dominate. List semantics first, and say what each one means
rather than what colour it currently is.

## Resolution order

References resolve in one direction: component to semantic to primitive.
Never the reverse. If you find yourself pointing a primitive at a semantic,
the model is inverted and the theme will not flip cleanly.

## Four failure modes to check yourself against

1. **Fabrication** — inventing a token name that does not exist. Look it up;
   do not infer it from a pattern.
2. **Within-session drift** — using a different token for the same role three
   components later.
3. **Between-session amnesia** — a different choice tomorrow for the same job.
4. **Silent resolution** — writing the resolved value instead of the
   reference. `#2563EB` where `var(--color-action-primary)` was meant. Six
   months later the rebrand misses it.

Colour tokens survive better than spacing tokens in practice, because hex
values are dense in training data while semantic spacing aliases are not.
Check spacing especially carefully.

## Verification

Before reporting done:

1. Grep your own diff for literals: `{{rg "#[0-9a-fA-F]{3,8}\b|\b\d+px" -- your-changed-files}}`.
2. Confirm every token name you used exists in the source of truth.
3. Render in both themes if the change is visual. A token that reads in light
   must read in dark; that is the whole reason it is a token.
