A team adopts design tokens specifically to make a future rebrand cheap, and then the rebrand still turns into a multi-week scramble anyway. The tokens didn't fail because tokens are a bad idea. They failed because the token system was flat, a list of names pointing at raw values, instead of layered by meaning. Here's why that distinction is the whole ballgame.
The trap: tokens that describe appearance, not role
--brand-blue: #1a73e8 is a design token in the loosest technical sense, a named CSS variable, but it doesn't separate the color's identity from its purpose. When the brand color changes to green, you're stuck either keeping a token called "blue" that's now green, which confuses everyone who reads it, or renaming it everywhere it's used, which is the exact manual find-and-replace tokens were supposed to eliminate.
This trap is easy to fall into because a flat list of named colors genuinely feels like progress compared to inline hex codes scattered through a codebase. It is progress, just not enough of it to survive the specific stress test a rebrand puts on it.
The tell is usually visible in the pull request history if you go looking. A codebase with only flat tokens tends to accumulate a slow trickle of "quick fix" commits that add a new near-duplicate token, color-blue-alt, color-blue2, rather than reusing an existing one, because nothing in the naming scheme signals which token is the canonical choice for a given role.
What actually breaks during a rebrand
The failure mode is almost always the same: some components reference the semantic-ish token, and some reference the raw value directly, because a developer under deadline pressure grabbed the hex code from a design file instead of looking up the "right" token to use. Nothing in a flat system flags that shortcut as wrong, because there's no structural distinction between a token meant to be referenced and a raw value that happens to have a name.
A design system built on a semantic layer catches this differently. If every component is required to reference a semantic token rather than a primitive value directly, a linter can enforce that boundary automatically, flagging any raw hex code in component code as a violation rather than relying on developers to remember the convention every time.
That automation matters more than it sounds like it should, because the deadline pressure that causes a developer to grab a raw hex code instead of looking up the right token doesn't go away just because a style guide says not to. A linter that fails CI on a hardcoded value removes the temptation entirely rather than relying on a rule everyone agrees with in principle but occasionally skips in practice.
The fix: separate what a color is from what it's for
The structural fix is a semantic layer between the raw palette and the components that use it. Primitives hold the actual values: blue-500, red-600. Semantic tokens map a role to a primitive: action-primary points at blue-500 today, brand-green-500 after the rebrand, without any component needing to know that mapping changed.
{
"primitive": { "blue-500": "#1a73e8", "green-500": "#1a9e6e" },
"semantic": { "action-primary": "{primitive.green-500}" }
}
Every component references action-primary, never blue-500 or green-500 directly. The rebrand becomes a one-line change to the semantic mapping, and every component that follows the convention picks it up automatically, with zero component-level edits required. Tools built around this pattern, including Figma's variables feature, let designers make the same swap on the design side without touching individual component files either.
Why this matters more as a codebase grows
In a five-component prototype, the difference between flat tokens and layered ones barely matters, because you can manually check every usage in a few minutes. In a two-hundred-component production app maintained by a rotating team over several years, manual verification isn't realistic, and the structural guarantee is the only thing standing between "the rebrand is a config change" and "the rebrand is a company-wide six-week project."
The MDN documentation on CSS custom properties covers the mechanics of how custom properties cascade and inherit, which is useful background, but it won't tell you anything about the semantic layer, because that's a naming and process discipline your team has to build on top of the underlying CSS feature.
This is worth stating plainly because it's a common misconception: adopting CSS custom properties, or any platform's equivalent, doesn't automatically give you a semantic token layer. The platform feature is the mechanism; the semantic layer is a decision your team makes about how to use it. Plenty of codebases use custom properties extensively while still keeping them named after appearance rather than role, which reproduces the exact rebrand fragility this article is about, just implemented with a slightly more modern CSS feature.
The migration path if you're already in the trap
If your existing tokens are flat, you don't need a rewrite to fix this. Audit which raw hex values actually appear across the codebase, map each to a semantic role based on how it's actually used (not how it was originally named), and introduce the semantic layer as a new set of tokens that reference the existing primitives. Migrate components to the semantic layer incrementally, behind visual regression tests, rather than attempting a single sweeping change.
Expect the mapping step to surface disagreements about what a given color's role actually is. Two components using the same hex value might be serving genuinely different purposes, one a brand accent and one a coincidentally identical warning color, and conflating them into a single semantic token because they happen to share a value today will just recreate the same fragility a few years down the line when only one of those uses is supposed to change.
A quick way to check where your own system stands
If you're not sure whether your current tokens are flat or properly layered, there's a fast check: pick any component and ask whether it references a token by what the value looks like or by what role it plays. If the answer involves the word "blue," "green," or a pixel value anywhere in the token's name, you're looking at a primitive being used where a semantic token should be.
A second check is to grep for raw hex codes directly in component files rather than in a dedicated tokens file. Any hits mean at least some of your styling bypasses the token system entirely, regardless of how well-designed the semantic layer itself is, since a bypass anywhere undermines the guarantee the whole structure is meant to provide.
Run both checks before committing to a rebrand timeline, not after the rebrand is already underway. Finding out mid-rebrand that a third of your components bypass the token system turns a scoped color update into an unplanned audit project, at exactly the moment stakeholders are expecting a quick turnaround.
The takeaway
Tokens don't fail rebrands because the tooling is wrong. They fail because a flat naming scheme never separated a value's identity from its purpose, so there's nothing structural stopping a shortcut from reintroducing the exact coupling tokens were meant to eliminate. 137Foundry's guide to building a design token system that survives a rebrand walks through the full three-tier structure, including a working token file and a governance process for who's allowed to add new tokens.
Top comments (0)