Architect themes on a token foundation
layered token architecture.
separate primitive tokens from semantic alias tokens that themes redefine, emit semantics as custom properties per theme scope, components use only semantics.
components reading primitives.
WHAT THIS TESTS Whether you can design a token hierarchy that makes adding themes cheap while keeping components blissfully unaware of which theme is active.
A GOOD ANSWER COVERS Use layers. Primitive tokens hold raw, context-free values like blue-500 or gray-900. Semantic tokens, also called alias tokens, give meaning: color-background, color-text, color-action. Each semantic token points at a primitive. A theme is simply a different mapping of semantic tokens to primitives. At build or runtime, emit the semantic tokens as CSS custom properties scoped per theme, for example under :root for light and under html[data-theme=dark] for dark, with each scope assigning the semantic properties to that theme's chosen primitives. Components reference only the semantic custom properties, never primitives. Switching themes is then just changing the active scope, the data-attribute or class, and the cascade resolves every component to the new values with no recompile and no component changes.
COMMON WRONG ANSWERS Letting components read primitive tokens directly couples them to specific colors and breaks theming. Duplicating component stylesheets per theme bloats CSS and invites drift. Skipping the semantic layer means a brand theme has to override dozens of scattered raw values instead of one mapping. Hardcoding values in components.
LIKELY FOLLOW-UPS Why two layers instead of one? Primitives are the palette; semantics are intent, so a new theme only redefines intent-to-palette mappings. How do you add a brand theme? Add another scope with its own semantic mappings. How do you avoid first-paint flash? Set the theme attribute before CSS loads.
ONE CONCRETE EXAMPLE Primitive gray-900 and white exist. The light scope sets color-background to white and color-text to gray-900; the dark scope sets color-background to gray-900 and color-text to white. A card uses background var(--color-background) and color var(--color-text). Toggling data-theme to dark on the html element repaints the card with no component edits, because only the semantic mappings changed.
Read the original → bradfrost.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.