Skip to content
ColorArchive
Design Systems
2028-07-15

Color Naming in Design Systems: Semantic, Functional, and Why Your Naming Breaks at Scale

Color naming in design systems is a solved problem that teams keep resolving incorrectly. The tension between descriptive names (blue-500), semantic names (color-interactive), and functional names (button-primary-background) has a correct resolution depending on design system maturity and team size — but most teams use an approach that works at one stage and fails as the system grows. Understanding the three-tier model prevents the most common naming collapses.

Highlights
The three-tier color naming model separates colors into: (1) Primitive tokens — the raw color values with descriptive names (blue-500, gray-100). These are fixed values that never change meaning. (2) Semantic tokens — purpose-driven names that reference primitives (color-background, color-interactive, color-error). These have stable meaning but can change their underlying primitive across themes. (3) Component tokens — component-specific names that reference semantic tokens (button-background, card-border, nav-text). These are the highest-level abstraction and exist only for components that need fine-grained control. The most common mistake: systems that skip tier 2 (semantic tokens) and map primitive names directly to component names. This collapses when the team needs theming, dark mode, or brand refreshes — without a semantic layer, every theme requires a new primitive-to-component mapping, and the work scales with (primitives x themes).
Primitive token naming should be value-descriptive, never semantic. `blue-500` is correct; `brand-blue` is incorrect for this tier because it encodes meaning (brand association) into a structural name (the 500 step in the blue scale). When the brand color changes from blue to green, `brand-blue` must be renamed everywhere, whereas `blue-500` simply becomes a less frequently referenced value. The numeric scale convention (50-950 or 100-900 in 100-step increments) has become the de facto standard because it is extensible and value-descriptive — `blue-450` clearly represents a value between blue-400 and blue-500. Avoid naming primitives with lightness words (blue-light, blue-dark) because these become ambiguous at the edges and when the scale grows — is `blue-lighter` lighter than `blue-light`? The numeric convention avoids this entirely.
The dark mode collapse is the most common symptom of a broken semantic layer. Teams building dark mode for the first time often discover that they have no semantic tokens — their components reference primitive tokens directly (color: blue-900; background: gray-50). Dark mode requires a different color assignment in a different context, which requires a semantic layer to express: `color-text: blue-900 (light) / blue-100 (dark)`. Without this layer, every component needs manual dark mode overrides, which is unsustainable. The fix requires retroactively adding a semantic layer between primitives and components — auditing which primitives appear in which semantic roles, creating semantic tokens that cover those roles, updating components to reference semantic tokens, and then defining the dark mode theme as a mapping of semantic tokens to different primitives. This is correct but painful to do after the fact; the cost of doing it correctly at the start is low.

Semantic naming patterns that age well

The semantic naming patterns that remain stable as systems grow use role + prominence + state, not descriptive color language. Pattern: `color-{role}-{prominence}-{state}`. Examples: `color-background` (default background), `color-background-subtle` (secondary background surface), `color-interactive` (default interactive element), `color-interactive-hover` (hover state of interactive), `color-feedback-error` (error state signal), `color-feedback-success` (success state signal). Avoid naming patterns that embed color descriptions (`color-blue-interactive`) or product names (`color-cta`) in semantic tokens — both become inaccurate when the primitive value changes. The role-based pattern remains accurate regardless of what primitive value backs it, and reads as a design decision (this surface is 'subtle') rather than a color description.

When to add component tokens

Component tokens — the third tier — should be added selectively, not comprehensively. The trigger for adding component tokens: a component needs a color that deviates from the semantic meaning it would normally use. A button with a background of `color-interactive` is correct and does not need a component token. A card that uses a slightly different surface color than the default `color-background-subtle` — to create a specific layering effect — benefits from a `card-surface` component token, because this deviation needs to be explicit and themeable. The anti-pattern: creating component tokens for every color used in every component 'for completeness.' The result is a system with hundreds of component tokens that most designers ignore, and where changes to shared semantic values must be propagated manually to every component token that redundantly stores the same value. The criterion: add a component token only when the component needs a deviation from semantic color, not for every color used.

Newer issue
Color in Data Visualization: Encoding, Perception, and the Rules That Are Actually Followed
2028-07-08
Older issue
Film Color Grading for Designers: How Cinematographic Color Applies to Brand Work
2028-07-22