Component-Level Tokens
Component-Level Tokens
Component-level tokens provide design values scoped to specific components, adding a layer of customization between global semantic tokens and component implementations. While not every design system needs component tokens, understanding their purpose and tradeoffs helps teams make informed architectural decisions.
What Are Component-Level Tokens
Component-level tokens are tokens whose names and values specifically relate to individual components. Unlike semantic tokens that describe general purposes (primary background, secondary text), component tokens describe component-specific applications (button background, card border).
These tokens typically reference semantic tokens rather than containing raw values, creating an additional indirection layer:
Primitive: color.blue.500 = #3B82F6
↓
Semantic: color.action.primary = {color.blue.500}
↓
Component: button.background.default = {color.action.primary}
This layering allows component styling to diverge from semantic patterns when needed while maintaining a connection to the broader system.
How Component-Level Tokens Work
Component tokens exist as an optional layer in the token hierarchy. When present, components reference their dedicated tokens rather than semantic tokens directly.
A button component might use tokens like:
{
"button": {
"background": {
"default": { "value": "{color.action.primary}" },
"hover": { "value": "{color.action.primary.hover}" },
"disabled": { "value": "{color.background.disabled}" }
},
"text": {
"default": { "value": "{color.text.on-action}" },
"disabled": { "value": "{color.text.disabled}" }
},
"border": {
"radius": { "value": "{border.radius.md}" },
"width": { "value": "{border.width.default}" }
},
"padding": {
"horizontal": { "value": "{spacing.md}" },
"vertical": { "value": "{spacing.sm}" }
}
}
}
The component implementation references these component tokens:
.button {
background: var(--button-background-default);
color: var(--button-text-default);
border-radius: var(--button-border-radius);
padding: var(--button-padding-vertical) var(--button-padding-horizontal);
}
This indirection enables several capabilities. Component tokens can be overridden without changing semantic tokens. Themes can customize components individually. Product teams can adjust component styling without forking the component code.
Key Considerations
- Component tokens add complexity; add them only when the flexibility is needed
- Starting without component tokens and adding them later is viable
- Component tokens should reference semantic tokens, not primitives
- Naming should clearly indicate component scope
- Documentation should explain the relationship between token layers
- Build processes may need to handle component token generation differently
- Over-tokenization at component level creates maintenance burden
- Not all component style properties need dedicated tokens
Common Questions
When are component-level tokens necessary?
Component tokens become valuable when different contexts need different component styling without changing global semantics.
Multi-brand systems often need component tokens. Two brands might share semantic color definitions but need different button styling. Component tokens allow brand-specific button appearance while semantic tokens remain consistent.
Product customization scenarios benefit from component tokens. A platform allowing product teams to customize component appearance can expose component tokens for modification while protecting semantic layer integrity.
Theme variations beyond light/dark may warrant component tokens. A “compact” theme might use the same colors but different component spacing, achievable through component token overrides.
If all components uniformly apply semantic tokens without context-specific variation, component tokens add complexity without benefit. Start with semantic tokens directly and introduce component tokens when specific needs emerge.
How should component tokens relate to component variants?
Components often have variants (primary button, secondary button, ghost button) that require different styling. The relationship between variants and tokens requires deliberate modeling.
Variant tokens create separate token groups for each variant:
{
"button": {
"primary": {
"background": { "value": "{color.action.primary}" }
},
"secondary": {
"background": { "value": "{color.action.secondary}" }
},
"ghost": {
"background": { "value": "transparent" }
}
}
}
State tokens exist alongside variant tokens for interactive states:
{
"button": {
"primary": {
"background": {
"default": { "value": "{color.action.primary}" },
"hover": { "value": "{color.action.primary.hover}" },
"active": { "value": "{color.action.primary.active}" },
"disabled": { "value": "{color.background.disabled}" }
}
}
}
}
This creates a matrix of variant × state tokens. The matrix can become large for components with many variants and states. Evaluate whether full tokenization is necessary or whether some combinations can share values.
What about component tokens for third-party components?
Design systems using third-party component libraries face questions about how their tokens integrate with library theming systems.
Mapping approach defines component tokens that map to library theme APIs:
const theme = {
components: {
Button: {
defaultProps: {
bg: tokens.button.background.default,
color: tokens.button.text.default,
}
}
}
};
The design system’s component tokens feed into the library’s theming mechanism, creating a bridge between systems.
Wrapper approach creates design system components that wrap library components and apply tokens:
function DSButton(props) {
return (
<LibraryButton
style={{
background: `var(--button-background-default)`,
color: `var(--button-text-default)`,
}}
{...props}
/>
);
}
The appropriate approach depends on the library’s theming capabilities and how much control the design system needs.
Summary
Component-level tokens add customization capability between semantic tokens and component implementations. They enable context-specific component styling without modifying global semantics. While not universally necessary, component tokens prove valuable for multi-brand systems, product customization, and specialized themes. Starting without component tokens and adding them when specific needs emerge avoids premature complexity.
Buoy scans your codebase for design system inconsistencies before they ship
Detect Design Drift Free