design systems

The art of naming tokens

A token name carries a promise: this is what I'm for, this is where I belong, this is what you should call on me to do. When that promise is unclear, everything downstream starts guessing, and guessing at scale has a cost.

0% read

There is a moment in working with a design system when you stop looking at components and start looking at names.

Not the values. Not the variants. The names.

And when you read them carefully, not as labels but as decisions, you start to see where the system is healthy and where it has been quietly patching itself. A token name carries a promise: this is what I'm for, this is where I belong, this is what you should call on me to do. When that promise is unclear, everything downstream starts guessing. And guessing, at scale, has a cost.

I've spent time inside systems where that cost was already accumulating. Spacing tokens named after their pixel values. Fill tokens borrowed for borders because the right one didn't exist. A default surface color that was the opposite of what every engineer expected. Nothing was visually broken. But every change took longer than it should, and nobody could explain exactly why.

This is about what naming tokens well actually looks like, and what happens when it doesn't.

It starts with a number

Imagine a spacing token called --8. Then another called --12. Then --16. Each one named after the value it holds.

It works. For a while.

Then someone decides the grid needs to shift. --8 should become 10. Now you have a choice that has no good answer: rename the token and break every reference in the system, leave the wrong name and mislead every person who reads it after you, or ignore it. And watch someone add a --10 next to the existing --8, because they genuinely couldn't tell which one they were supposed to use.

None of those are good endings.

The better version of that token is called spacing/sm. The value inside is 8. If the value needs to change, the name stays true. Nothing downstream breaks.

The rule is simple: a token name is not a value. It's a role. The value is what the variable holds. The name is the promise you're making to every component that will ever reference it.

Then someone needed a focus state

Now imagine a designer working on a checkbox. They need the focused border to be a certain grey. They look through the tokens. There's no border/focus. But there's a surface/strong that happens to be exactly the right shade.

So they use it.

This is where it gets quiet again. And quietly wrong. surface/strong is a fill token. It describes what goes behind content. It has nothing to do with borders. But now it does, because someone needed a grey and it was the closest thing available.

A month later, someone else reads that component. They see surface/strong applied to a border. They're confused. They wonder if that's intentional. They follow the pattern. Now surface/strong is being used for fills, borders, maybe an icon tint somewhere else. The token means everything, which means it means nothing.

The fix isn't complicated: add border/focus. Give people the token they actually need. When the right name exists, nobody has to borrow the wrong one.

The one that costs the most

This one is harder to catch, because catching it requires knowing what everyone else does.

Say your token called surface/default holds a light grey. And surface/subtle holds white.

It's internally consistent. The file works. Everything renders.

But it's backwards.

In every major system, on every platform, surface/default is the base. White, or close to it. The page background. The card surface. surface/subtle is the step off from there: a quiet tint, a barely-there grey.

When your naming is inverted, engineers integrating your tokens are working against what they expect. Every spec that says "use surface/default for the card" produces a grey card. They assume they've misunderstood something. They ask questions. They override things. The system creates more confusion than it saves.

And fixing it isn't a rename. It's a migration. Every component needs to be checked. You either rename the token (which breaks existing usage) or swap the values (which changes the visual output everywhere). Neither is a small job.

The cost of this decision doesn't stay flat. It grows.

When someone skips the layer entirely

The clearest sign a token system is under pressure: components that bypass the semantic layer and reach directly for a primitive.

Raw color stops. --blue-200. --purple-500. Values that belong inside token definitions. Not inside components.

It happens because a semantic token is missing. Someone needed a focus-state blue and there was no border/focus to use. So they went looking, found --blue-200, and it looked right. They used it. It shipped.

Then the brand color changes. Blue shifts. The semantic tokens update. But --blue-200 is hardcoded inside the component, so that component doesn't move with the rest of the system. You find it during the rebrand. You fix it. You wonder how many more there are.

One component doing this is a quick fix. Ten components, each grabbing a different stop for a different reason, is a different kind of problem.

What it actually costs

Token naming problems are patient. They sit there, invisible, while you ship. They surface during a rebrand, a dark mode launch, a new product that needs to adopt the system. That's when you find out how much of your system is actually a system, and how much is just components that agreed to be neighbors.

Every component that reaches past the semantic layer, borrows from the wrong category, or holds a name that lies about its value is a component that can't be updated by changing a token. It has to be hunted and changed manually. One by one.

That's the opposite of what a design system is for.

The fix

Before anyone touches token names, agree on two things.

First: what category does this token belong to? Colors, spacing, borders, typography, each one gets its own namespace, its own rules. Mixing them is how surface/strong ends up on a border.

Second: what role does this token fill? The name should answer it plainly. What is this for, and where does it go? If you can't answer in one line, the name isn't ready.

Get those two things clear, and the rest is just work.

Token naming isn't the exciting part. It never shows up in a design review. Nobody demos it. But it's the thing that determines whether your system actually behaves like a system, or just looks like one.