semantic token adoption
situation
component kit with semantic theme variables in some places, raw palette utilities in others, local inline overrides scattered throughout. a theme explorer made a new palette easy to preview, but applying it directly would create two incompatible authorities with no way to change a role consistently. changing "primary action" color meant finding and editing five places using five different names or mechanisms — the source of truth was distributed.
why
the protected invariant is single ownership of visual meaning. when one role can be spelled many ways, no visual decision can be made once and trusted; every refinement becomes an audit of the whole interface. routing accepted palette values through a bounded role-token system keeps the fast preview loop while ensuring each value lands under an owner the next change can address in one place.
invariants
- tokens name roles, never individual component instances.
- components consume role tokens through a bounded variant contract.
- a refinement preserves the incumbent visual identity outside its approved scope; a redesign is named explicitly and replaces the old system fully.
- local layout utilities do not become hidden theme values.
decision process
| candidate | reasoning | decision |
|---|---|---|
| paste explorer output into component files | each component becomes its own authority | rejected |
| add a raw palette utility whenever a surface differs | similar values, no shared audit path | rejected |
| retain every old alias for safety | two vocabularies, ambiguous adjustments | rejected |
| map accepted values to a small role-token system, then update named variants | token-to-component trace, coherent theme | selected |
resulting shape
shared theme defines canvas, surface, text, border, action, destructive, and data roles. components expose intent and state variants mapping to those roles. a route controls only local layout and its final content inset. a role correction changes the token once; a component-state correction changes its variant once.
verification
- classify the work as refinement or redesign before choosing values.
- render supported theme modes and real interaction/error/loading states.
- trace each changed element to a semantic role and component variant.
- search for superseded raw values and token aliases after adoption.
- inspect nested surfaces for duplicate padding or decorative-wrapper insets.
good and bad
bad
// status badge<span className="text-emerald-500 bg-emerald-100">active</span>// elsewhere, a different "active" state<div className="border-2 border-green-500">running</div>// in another component, yet another shade<Button className="bg-[#10b981]">proceed</Button>
emerald-500, green-500, and #10b981 are the same or similar greens, each named differently and maintained independently. changing "active status" color means auditing every file and guessing which hex values were intentional. a designer's correction ("make positive calmer") lands on some surfaces and not others; every fix needs a manual sweep.
good
// status badge<StatusBadge tone="positive">active</StatusBadge>// elsewhere, a different "active" state<Badge tone="positive" variant="outlined">running</Badge>// another component using the same role<Button variant="primary">proceed</Button>
each component declares its semantic role (positive, primary, destructive). one
token system owns all "positive" surfaces: changing the positive color updates
every surface using the positive role. new surfaces inherit the role's colors
without inventing new spellings.