On this page
Colour names become a product problem when developers cannot tell which value belongs on a button, a panel or its label. A name such as blue-600 describes a swatch; it does not explain permission to use it. Semantic colour tokens add that missing purpose without requiring every component to understand the palette.
Separate colour values from colour roles
Start by inventorying the values already in your interface. Use the colour converter to inspect equivalent HEX, RGB and HSL representations when comparing existing declarations. Normalising formats helps expose duplicate values, but it does not establish whether two identical colours should share the same role.
Think in two layers. Primitive tokens hold palette values such as neutral-950 or brand-600. Semantic tokens express intended use, such as surface-default or text-primary. A component usually consumes the second layer. The mapping between them can change without changing what the component means.
For example, a blue link and a blue selected navigation background may currently share a hex value. They should not automatically share a semantic token: one is text and the other is a surface. Their contrast partners and future theme requirements differ. Equality today is not evidence of identical purpose tomorrow.
The design tokens introduction covers the broader collaboration model. For colour specifically, the useful starting point is a list of real foreground-background relationships, not an elaborate naming hierarchy invented before anyone has inspected a screen.
Define primary and surface without ambiguity
The word primary is overloaded. It might mean the main brand colour, the highest-priority action, or the most prominent text. Use qualified names such as action-primary, text-primary and surface-primary when that distinction matters. Alternatively, reserve primary for action and explicitly document that convention.
Surface means a colour behind content, but its scope still needs definition. A minimal family could contain surface-canvas, surface-default and surface-raised. These names describe visual roles without insisting that the values always become lighter as elevation increases. A light theme and a dark theme can map them differently.
Avoid names such as background-two unless the number represents a documented sequence. A future developer should be able to decide between two tokens without reading their hex values. If that decision remains arbitrary, either the names need work or the roles are duplicates.
Write a one-sentence contract for every semantic colour. For action-primary, say that it fills the highest-priority enabled action. For surface-raised, say that it supports floating panels and dialogs. Include exclusions: action-primary is not automatically appropriate for body links, chart series or decorative illustrations.
Pair every important surface with its foreground
An on-surface token identifies a foreground intended for a particular surface. It does not mean a translucent layer placed above it. Common pairs include surface and on-surface, action-primary and on-action-primary, or status-danger-surface and on-status-danger.
Not every foreground needs its own new background token. Primary and secondary text may both appear on several neutral surfaces, provided each pairing has been checked. Document the supported combinations rather than implying that any text token works on every background. Inverse text especially needs an explicit home.
Use the contrast checker to record those approved pairs. Follow the distinction between ordinary and large text in W3C's contrast guidance. A token's friendly semantic name cannot guarantee readable output when a component uses an untested combination.
Keep borders and icons separate when their responsibilities differ from text. A decorative divider can be subtle, while an input boundary may need stronger visibility. Similarly, a status icon should not inherit the colour of a muted timestamp merely because both are small elements beside a label.
Map themes through aliases
Implement the roles as custom properties and change their values at a theme boundary. The following compact example deliberately keeps component selectors free from raw palette references. Its values are starting points to verify, not a universal palette.
:root {
--brand-700: #1749a3;
--neutral-950: #141922;
--neutral-50: #f4f6fa;
--surface: #ffffff;
--on-surface: var(--neutral-950);
--action-primary: var(--brand-700);
--on-action-primary: #ffffff;
}
[data-theme="dark"] {
color-scheme: dark;
--surface: var(--neutral-950);
--on-surface: var(--neutral-50);
}
.panel { background: var(--surface); color: var(--on-surface); }
.button-primary {
background: var(--action-primary);
color: var(--on-action-primary);
}The theme boundary can be the document root or a themed subtree. Be aware that custom properties inherit: a nested theme changes values for descendants, which may be desirable for previews but surprising for portalled overlays. Review MDN's custom property guide when deciding where values should live.
Do not derive every role from one primary value. Neutral surfaces, text, success and warning colours have independent constraints. Semantic mapping gives you room to meet those constraints instead of forcing all meaning through a single hue.
Keep the vocabulary small and testable
Begin with roles used by current screens. A practical first pass might include three surfaces, two text strengths, an action pair, a border and a few status pairs. There is no ideal count; the test is whether each role has a clear reason to exist and a predictable consumer.
Create a token specimen that shows each foreground on every supported background, with actual labels and body text. Include the token names in the specimen so reviewers can report a specific relationship. A visual mismatch then becomes an actionable mapping issue rather than a vague complaint about the theme.
When adding a token, ask three questions: does an existing role already express this purpose, does this role need to change independently, and can its permitted use be explained briefly? If independence is unnecessary, reuse the role. If the explanation requires a component's full implementation history, choose a clearer name.
For exported formats, distinguish your product vocabulary from the interchange structure described by the Design Tokens Community Group. A standard file format helps tools exchange data; it does not choose good semantic names for your product. Keep those decisions grounded in actual colour use.
Key takeaways
- Separate raw palette values from semantic intent.
- Qualify ambiguous names such as primary before they spread.
- Treat on-surface colours as foreground partners, not overlays.
- Change theme mappings rather than rewriting component colour choices.
- Document supported pairs and add new roles only for genuine independent needs.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor