Name design tokens so nobody has to guess

Put the parts of every token name in one fixed order and people can guess names instead of looking them up. Renames go through an alias, never a break.

Names outlive values

Token names are the public API of your design system. Designers pick from them in Figma, engineers type them hundreds of times a week, and AI coding tools pattern-match on them. Values change freely. Names stick around until someone pays for a migration.

A good name tells you what the token is for and roughly how it relates to its neighbours, without opening the docs. A bad one makes people guess, and people who guess pick the wrong token or give up and paste a hex.

One order for every name

Pick an order for the parts of a name and never break it. This one works for most systems:

category - property - variant - state
PartAnswersExamples
CategoryWhat kind of value?color, space, radius, font-size, shadow, duration
PropertyWhat role or element?surface, text, border, primary, inset, control
VariantWhich one?raised, muted, danger, on-primary, sm, lg
StateWhen?hover, pressed, focus, disabled, selected

Category and property are required. Variant and state appear only when you need them:

--color-surface
--color-surface-raised
--color-text-muted
--color-text-on-primary
--color-border-focus
--color-primary-hover
--space-inset-md
--radius-control

Read color-text-on-primary left to right and it's a sentence: the colour of text that sits on primary. If a name reads as a description of its job, the grammar is working.

Keep state last, always. --color-primary-hover sorts next to --color-primary in every token list, and searching for -hover finds every hover token in the system. Put state in the middle once and both of those stop being true.

[!TIP] Add a system prefix if your tokens end up in other people's pages. --acme-color-surface won't collide with a third-party widget that also defines --color-surface.

Good names, bad names

BadWhat goes wrongBetter
--blueThe hue is the meaning, so a rebrand breaks it--color-primary
--color-light-grey-bgWrong the moment dark mode ships--color-surface-sunken
--text-color-2A number with no order anyone remembers--color-text-muted
--red-textHue again, and the parts are backwards--color-text-danger
--spacing-16pxThe value is in the name; it lies after the first change--space-4 or --space-md
--surface-v2There will be a v3--color-surface-raised
--primaryColorDifferent case style from everything else--color-primary
--header-paddingA component name in the shared tier--space-inset-lg, or a component token
--color-button-primary-background-default-restSeven parts to say one thing--button-bg

Most bad names fall into two groups. Some describe the value (blue, light grey, 16px), which is the primitive tier's job. Others describe one place it's used (header, v2), which is a component's job. Semantic names describe a role, and nothing else.

T-shirt sizes or numbers

There are two ways to name the steps of a scale, and they suit different jobs.

T-shirt sizes (xs, sm, md, lg, xl) are easy to say out loud and hard to misuse. They fit short scales where the relationship matters more than the value: radius, elevation, the handful of text sizes in a small product. The weakness is insertion. When you need something between sm and md there's no good name for it, and by 3xl people are counting x's.

Numbers come in two flavours:

  • Multipliers tie the name to a base unit. On a 4px grid, space-1 is 4px, space-4 is 16px and space-6 is 24px. You can work out the value from the name, which engineers like.
  • Ordinal steps (100 to 900, or 0 to 1000) say nothing about the value, only the order, and leave gaps. 550 fits between 500 and 600 without renaming anything. That's why colour ramps almost always use them.
ScaleBest fitWhy
Colour rampsOrdinal numbersMany steps, and you'll need to insert one
SpacingMultipliersThe name maps to the grid
RadiusT-shirtFew steps, sm and lg say enough
ShadowT-shirt or 1 to 5Elevation is short and ordered
Font sizeEitherT-shirt for a small product, numbers for a long type scale
DurationNamed (fast, base, slow)People choose by feel, not by milliseconds

You can mix styles across tiers. Primitives use numbers and semantics put t-shirt sizes on top: --space-inset-md: var(--space-4). What you can't do is mix inside one scale. space-sm, space-4 and space-large in the same list means nobody knows which is bigger.

Renaming without breaking people

You will rename tokens. The job is to never make it a single breaking change.

  1. Add the new name with the same value.
  2. Turn the old name into an alias of the new one, so both resolve identically.
  3. Mark the old name deprecated where tools can see it. The W3C DTCG format has a $deprecated property that takes true or a string explaining what to use instead.
  4. Warn in code. A lint rule or build step flags the old name and prints the replacement.
  5. Migrate usage. Token names are plain text, so a codemod or a careful find-and-replace does most of it.
  6. Delete the alias in a major version, after consumers have had at least one release to move.

In DTCG JSON, steps 2 and 3 look like this:

{
  "color": {
    "bg-subtle": {
      "$type": "color",
      "$value": "{color.surface-sunken}",
      "$deprecated": "Use color.surface-sunken. Removed in 4.0."
    }
  }
}

And in CSS the alias is one line:

/* Deprecated: use --color-surface-sunken. Removed in 4.0. */
--color-bg-subtle: var(--color-surface-sunken);

Old code keeps working, new code uses the new name, and a search for the alias tells you who hasn't moved yet.

[!WARNING] Never rename a token and change its value in the same release. If something looks off afterwards, nobody can tell whether the rename or the value caused it.

The checklist

Keep this next to your token file:

  • Every name follows one grammar: category, property, variant, state, in that order.
  • One case style everywhere. Kebab-case in CSS, with JSON groups that mirror it.
  • No hues in semantic names. No hex, px or rem values in any name.
  • No theme names like -dark or -light. Themes change values, not names.
  • State is always the last part.
  • A new token has to do a job that no existing token does in any theme.
  • Every fill colour has an on- partner for the text and icons on top of it.
  • Each scale uses one naming style, with room to insert a step.
  • Component names appear only in component tokens.
  • Deprecated names alias to their replacement and say when they'll be removed.
  • Someone new can read the name aloud and guess where it goes.

Flash generates names in this shape: semantic roles like surface, text, primary and border on top of ramps numbered 0 to 1000. The Documentation view puts a copy chip on every token, so people grab the real name instead of retyping it from memory.

Every on- pair in that checklist exists to pass a contrast threshold, and WCAG AA contrast in practice has the numbers behind them.