Token naming conventions
April 3, 2026
The name you give a token in week one becomes the CSS variable name every engineer on every consumer app writes in their code. Not an internal label. Not configuration. The actual string in production stylesheets, for the life of the system. color.brand.primary becomes --color-brand-primary. That is an API surface. Treat it like one from the first token you create.
Three tiers, one clear job each
The model that scales is three tiers: category, group, role. Each answers a different question about what the token is.
Three tiers, three different questions. A well-named token answers all three at a glance.
Category is the broad type. Keep it a closed vocabulary: color, spacing, typography, radius, shadow, motion. Resist creating new categories because a token feels unusual. If it involves color in any form, it belongs in color. New categories only when there is genuinely no reasonable fit.
Group is the semantic cluster within the category. For colors: brand, neutral, feedback, interactive, surface. Groups express purpose, not value. color.brand means "related to brand identity," not "the blue ones." If a group name reads like a hex value or a Pantone code, it belongs in the base layer.
Role is the specific usage slot. primary, secondary, subtle, disabled, hover. Roles express state or emphasis, not raw values.
Put it together: color.feedback.error. A consumer writes var(--color-feedback-error) and knows exactly what it is for without opening any documentation. That readability is the goal, and it is achievable with the three-tier model applied consistently.
Names become CSS variables directly
When a consumer runs reframe pull, every published token path is converted to a CSS variable. Dots become dashes, -- is prepended. There is no intermediate mapping. color.brand.primary becomes --color-brand-primary. The token name you choose is the variable name every downstream engineer writes in their code.
--color-feedback-error: #ef4444;
--color-brand-primary: #2563eb;
--spacing-layout-gutter: 24px;Rename a token later and the CSS variable changes. Every var(--color-brand-primary) reference in every consumer app breaks silently. No build error. Wrong values at runtime. This is why naming is architectural, not cosmetic. The post on token versioning covers what renames actually cost.
Base tokens vs semantic tokens
Base tokens hold raw values. color.palette.red-500 = #ef4444. They name a value, not a use. They are internal. Consumer apps should never reference them directly.
Semantic tokens alias base tokens and encode intent. color.feedback.error → color.palette.red-500. These are the public surface. When your error color changes from red to orange in a rebrand, you update one alias. Every var(--color-feedback-error) reference continues to work without touching consumer code.
A simple test: should a consumer app ever write var(--[this-token]) directly? If yes, it is semantic. If no, keep it in the base layer and alias from there.
Mistakes that are cheap to avoid and expensive to fix
- Values in semantic token names.
color.blue-500as a semantic token means you will rename it the moment the brand updates. Name by intent:color.action.primary. - Overloading "default" across groups. When
color.brand.default,color.interactive.default, andcolor.surface.defaultall coexist, "default" communicates nothing. Be specific:primary,base,resting. - Skipping the group tier to save time.
color.primaryworks for five tokens. At thirty, with no group tier, the namespace is flat and unsearchable. Add the middle tier from the first token. - camelCase or underscores within segments. Within a segment, use lowercase kebab:
brand-primary, notbrandPrimaryorbrand_primary. The CSS variable mirrors this convention exactly.
The first three tokens you name set the convention every token after them follows. Nobody audits naming patterns at token 200. They inherit whatever the first few established. Spending an afternoon on this before you build is the cheapest architectural decision in the entire system — and the most consequential one you will never get to revisit.