Design Token Naming Conventions: A Practical Guide
On This Page:
If you have ever stared at a long token list and wondered whether a name should be button-primary-bg, primary-button-background, or color-button-primary, you are not alone.
Naming design tokens can look and feel simple right up until you have to do it for real. Choose a weak pattern and things get inconsistent fast. Choose a clear pattern and you get a shared language that helps both design and engineering move quicker.
There is no single correct convention. A two-person team shipping one product has very different needs from a large organisation running multiple brands across multiple platforms.
What you can do is pick a convention that is clear, consistent, and scalable. This article walks through the core token tiers, common naming models, and practical rules that help avoid naming drift.
Why naming gets hard faster than expected
Most teams don't struggle creating tokens. They struggle to keep token meaning stable as more people and components work on the Design System.
You can often see multiple names emerge for the same idea:
["spacing.small", "space-2", "gap-md", "paddingStandard"]Each name might make sense in isolation. Together, they signal a semantic drift. The naming model is no longer acting as a shared language.
This is why naming quality can affect delivery speed. If token intent is unclear, every usage becomes a local judgement call, and those judgement calls will accumulate into inconsistency.
The examples in this article follow the Design Tokens Community Group specification using $value, $type, and dot-notation aliases like {color.brand.primary}.
What are design tokens, a quick recap
Design tokens are a tooling and platform agnostic way to store design decisions as named values: colour, spacing, typography, radius, shadows, motion, and (much) more.
The key difference is where and how many times you define those values.
Without tokens, you might use #BADA55 in dozens of places across components, stylesheets, and design files. When that green needs to change, you might have to hunt down every instance to make sure you're updating everything that needs to be updated.
With tokens, you create a design token file, a JSON object following the Design Tokens Community Group specification:
{
"green": {
"400": {
"$value": "#BADA55",
"$type": "color"
}
}
}You define #BADA55 once as green.400, then you can reference that name everywhere. The value is still defined somewhere, but it is defined in one place with a meaningful name and consumed by both design tools and code by reference via tooling like Tokens Studio or Style Dictionary.
This centralisation helps to give you three major benefits:
- Better consistency across UI
- Easier theming and re-branding
- Safer changes at scale because you update once and propagate everywhere
Tokens are not just a technical detail. They are a shared language between design and development (and the whole team) that ends up in the product(s).
The three token tiers
Most modern token workflows follow a three-tier model. You will see different names for each tier, but the same underlying structure keeps showing up.
Tier 1 - Primitive tokens
These are your raw values.
They describe what something is, not why it exists.
They are also referred to as reference, base, options, or global tokens.
{
"blue": {
"500": {
"$value": "#0066CC",
"$type": "color"
}
},
"spacing": {
"8": {
"$value": "8px",
"$type": "dimension"
}
}
}Primitives should not (imho) and are usually not consumed directly by components. They are source values for more specific design decisions and token tiers.
A common failure is skipping straight to component naming because it feels productive in the short term. In practice, that locks in naming decisions before your core scales are stable.
When possible, stabilise primitives first, attach intent later.
{
"core": {
"dimension": {
"100": { "$value": "8px", "$type": "dimension" },
"200": { "$value": "16px", "$type": "dimension" },
"300": { "$value": "24px", "$type": "dimension" }
}
}
}Tier 2 - Semantic tokens
Also known as alias, decision, theme, or system tokens.
Semantic tokens can express intent. They describe why a value is used or what it is for.
{
"color": {
"brand": {
"primary": {
"$value": "{blue.500}",
"$type": "color"
}
}
}
}This is where tokens become resilient.
If brand blue changes, the semantic name can stay the same while the underlying primitive reference changes.
You can update once and it will change everywhere.
{
"layout": {
"spacing": {
"formStack": { "$value": "{core.dimension.300}", "$type": "dimension" },
"contentToButton": {
"$value": "{core.dimension.200}",
"$type": "dimension"
}
}
}
}Tier 3 - Component tokens
Also known as "component specific" or contextual tokens.
This layer maps semantic intent to specific elements, components, and parts thereof.
Component tokens should always reference semantic tokens, never primitives directly. That indirection is what can keep the system flexible.
It is optional, but very useful in larger systems.
{
"button": {
"primary": {
"background": {
"$value": "{color.brand.primary}",
"$type": "color"
}
}
}
}Component tokens give you precision. You can tune one component without creating side effects and explosions elsewhere.
Common naming conventions
There is no universal winner ... "It depends". Different teams will always optimise for different makeup, needs, and constraints.
These diagrams show the core layers for each convention. Real-world usage may add optional layers like state or namespace for more specificity.
1. Category-Property-Modifier (CPM)
This starts broad and gets more specific: category, property, role (and optionally state for interactions). It is easy to scan and keeps naming fairly lightweight.
{
"color": {
"button": {
"primary": {
"hover": {
"$value": "{color.brand.primary-hover}",
"$type": "color"
}
}
}
}
}2. Tier-based naming
Here, the naming pattern changes depending on the tier. Primitives hold values, semantic tokens hold intent, and component tokens hold local UI decisions, which keeps each layer focused. The diagram shows the token tiers; actual nesting depth varies (e.g., primitives are often 2 layers, while component tokens may be 3+).
{
"blue": {
"500": {
"$value": "#2196F3",
"$type": "color"
}
},
"color": {
"brand": {
"primary": {
"$value": "{blue.500}",
"$type": "color"
}
}
},
"button": {
"primary": {
"background": {
"$value": "{color.brand.primary}",
"$type": "color"
}
}
}
}3. Object-Property-Modifier (OPM)
This one starts with the UI object, then narrows to property and variant. It tends to feel natural in component-led workflows because it mirrors how teams talk about UI day to day.
{
"button": {
"background": {
"primary": {
"hover": {
"$value": "{color.brand.primary-hover}",
"$type": "color"
}
}
}
}
}4. Context-first naming
Context (e.g., 'brand' or 'theme') comes first, followed by category, property, and modifier. This ensures you know the layer before the specific decision. That works well when the same semantic token needs to exist across several themes or brands.
{
"brand": {
"color": {
"primary": {
"default": {
"$value": "{primitive.color.blue.500}",
"$type": "color"
}
}
}
}
}5. A Comprehensive Structure
Each segment has a specific job, from namespace through to state. This comprehensive structure allows for up to 6 layers, but start with the core 5 and add as needed for complexity. Names are longer, but they are very explicit, which helps when multiple teams and brands share the same system. Nathan Curtis's article Naming Tokens in Design Systems goes much deeper on this taxonomy, it's well worth a reading (and bookmarking).
{
"acme": {
"color": {
"button": {
"background": {
"primary": {
"hover": {
"$value": "{acme.color.brand.primary}",
"$type": "color"
}
}
}
}
}
}
}Handling variants, states, and modes
Whatever base naming model you choose, you still need clear, predictable state and variant placement patterns.
One practical way to stay coherent is to define where each concept lives in the name:
- Variant: what version of the same thing it is (for example
primary,secondary,danger) - State: how that version is currently behaving (for example
hover,active,disabled) - Mode: which environment it belongs to (for example
light,dark,highContrast)
If those three ideas float around in different positions, naming gets noisy fast.
The same decision in different conventions
These examples all express the same meaning: primary button background on hover in dark mode.
[
"button.primary.background.hover.dark",
"button-background-primary-hover-dark",
"dark.button.primary.background.hover",
"acme.color.button.background.primary.hover.dark",
"theme.dark.component.button.primary.background.hover"
]Pick one ordering model and enforce it consistently. Mixing these patterns inside one system is where discoverability starts to break down.
Variants
Variants describe alternatives at the same hierarchy level, not interaction changes.
[
"button.primary.background",
"button.secondary.background",
"button.danger.background"
]Equivalent variant naming in other conventions:
[
"button-background-primary",
"button-background-secondary",
"button-background-danger",
"component.button.background.primary",
"component.button.background.secondary",
"component.button.background.danger"
]States
States represent interaction or status changes for the same UI element. Keep state terms predictable (hover, active, focus, disabled) so engineers can find related tokens quickly.
[
"button.primary.background.hover",
"button.primary.background.active",
"button.primary.background.disabled",
"button.primary.background.focus"
]The same state grouping works across naming styles:
[
"button-background-primary-hover",
"button-background-primary-active",
"button-background-primary-focus",
"button-background-primary-disabled"
][
"component.button.background.primary.hover",
"component.button.background.primary.active",
"component.button.background.primary.focus",
"component.button.background.primary.disabled"
]Scale sizes
Scale tokens create consistent step-based sizing. Whether you use xs-xl labels or numeric steps, use one scale pattern and apply it everywhere to avoid drift.
{
"spacing.sm",
"spacing.md",
"spacing.lg",
}For type scales, the same rule applies: keep naming sequential so size relationships are obvious at a glance.
["font.size.sm", "font.size.md", "font.size.lg"]Modes and themes
Modes are contextual variants of the same decision (for example light and dark). The token purpose stays the same, only the value changes per mode.
["color.background.default.light", "color.background.default.dark"]Other mode-placement patterns you will see in real systems:
[
"light.color.background.default",
"dark.color.background.default",
"theme.light.color.background.default",
"theme.dark.color.background.default"
]Whichever pattern you choose, keep mode placement fixed. A stable suffix or a stable prefix both work; switching between them does not.
Numbered scales
Numbered scales are useful when you need many fine-grained steps, especially for colour ramps and heading systems. The key is to keep the numbering direction and increments consistent.
[
"font.size.heading.1",
"font.size.heading.2",
"font.size.heading.3",
"color.gray.50",
"color.gray.100",
"color.gray.200"
]Numbered scales also appear in alternative patterns:
[
"size-font-heading-100",
"size-font-heading-200",
"size-font-heading-300",
"scale.type.heading.100",
"scale.type.heading.200",
"scale.type.heading.300"
]Rules that keep token naming healthy
Most strong naming systems follow a predictable anatomy: an optional prefix (namespace or context), a core meaning (category and property), optional modifiers (intent, variant, scale), and an optional suffix (state or mode).
You do not need every part in every token, but when a part is present it should always appear in the same position.
1. Consistency beats perfection
The best naming system is the one your whole team actually follows.
Drifting tends to start not where the convention is broken outright, but where it is ambiguous enough to invite interpretation. A token set that mixes naming styles makes it impossible to predict what a new token should be called.
Avoid: mixing conventions across the same token tier
[
"color.button.primary.default",
"button.background.primary.hover",
"theme.dark.component.button.primary.background.default"
]Prefer: one convention applied throughout
[
"component.button.primary.background.default",
"component.button.primary.background.hover",
"component.button.secondary.background.default"
]2. Optimise for clarity over brevity
Autocompletion means typing long names is rarely a problem, but reading a token list at a glance still relies on human pattern recognition. Abbreviating too aggressively trades a small typing convenience for an ongoing cognitive overhead every time someone reads or audits the system.
Avoid abbreviations that require decoding
["btn-bg-pri-hvr", "c-txt-err", "sp-md"]Prefer names that read on first glance
["button-background-primary-hover", "color-text-error", "spacing-md"]3. Keep semantics semantic
A name like color-text-red describes a visual value, which means the name breaks as soon as the colour changes. A name like color-text-error describes intent, which stays stable even when the underlying value does not. At semantic and component levels, name for why, not what.
Avoid: naming for the visual value
["color.text.red", "color.background.grey", "color.border.green"]Prefer: naming for the intent
["color.text.error", "color.background.subtle", "color.border.success"]4. Order from broad to specific
Names are easier to scan when they move from the widest concept toward specificity. This also means that alphabetically sorted token lists naturally group related tokens together, which makes auditing and searching much faster.
Avoid: specificity before category
["hover.primary.button.color", "disabled.background.input", "lg.font.heading"]Prefer: broad category first, narrowing toward specificity
[
"color.button.primary.hover",
"color.input.background.disabled",
"font.heading.lg"
]5. Make names self-explanatory
A developer who has never seen your design tokens before should be able to make a reasonable guesstimate about what it does. If a name requires specific knowledge or a colleague to interpret, it is a candidate for renaming.
Avoid: names that only make sense in context
["token-1", "c-bd-x", "primary-a"]Prefer: names that explain themselves
[
"button.border.radius",
"color.border.focus",
"color.button.background.primary"
]6. Design for growth
Your naming model should be able to absorb more components, more themes, and potentially more brands without a heavy structural rewrite. That does not mean you need to "over engineer" from the start, but it does mean avoiding patterns that only work at small scale, like implicit positional meaning or naming steps after their current count.
Avoid: patterns that hit a ceiling
["spacing-small", "spacing-smaller", "spacing-new", "spacing-new-2"]Prefer: a scale that can always grow in either direction
["spacing.100", "spacing.200", "spacing.300", "spacing.420"]Choosing your convention
A practical way to decide is to consider your team size, system scope, tooling, and existing patterns together.
Larger teams will need a stronger structure, single product systems can probably stay simpler, and tools like Figma, Tokens Studio, and your build pipeline may naturally favour one style.
It is also usually safer to evolve existing patterns than replace everything at once, and the best outcome comes when designers and developers both buy into the same model.
Start simple, document it clearly, add examples, and revisit as the system grows.
Token names do not need to be academically perfect. They need to be clear enough that your team can apply them without hesitation.
Keep naming alive with lightweight governance
Naming is not a one time decision.
It is an ongoing Design System practice.
If you want conventions to hold up over time, define lightweight governance:
- One canonical naming guide with approved examples
- A short review checklist for new tokens
- Periodic cleanup of duplicate or ambiguous names
- Shared ownership across design and engineering
The goal is not "process for process' sake".
A token system in a Design System that people trust is one they will actually use, and consistent naming is how that trust gets built.