Semantic Tokens for Light and Dark UI Switching
Semantic tokens bridge the gap between design intent and implementation by abstracting raw style values into purpose-driven roles. By establishing an intermediary layer that defines what an element is rather than what it looks like, teams can automate the transition between light and dark user interfaces. This article explains the architectural shift from static colors to contextual tokens, demonstrates how CSS variables facilitate theme switching at runtime, and highlights the workflow efficiencies gained through semantic abstraction.
Understanding Semantic Tokens
Design tokens typically exist in a three-tier hierarchy: primitive, semantic, and component-specific.
- Primitive Tokens (Global): These represent raw
design decisions with no contextual meaning, such as
color-blue-500: #3b82f6orcolor-neutral-100: #f3f4f6. - Semantic Tokens (Alias): These map primitive values
to specific design intents or roles within an interface, such as
background-surface-default,text-primary, orborder-critical. - Component Tokens: These apply semantic tokens to
specific elements, such as
button-primary-background.
When interfaces rely directly on primitive tokens, switching themes
requires overriding individual values across every component stylesheet.
Semantic tokens eliminate this friction by acting as a universal
contract: the component requests a role (for example,
text-primary), and the design system resolves that role to
the appropriate primitive value based on the active theme.
Decoupling Roles from Visual Values
The core mechanism of theme automation is the decoupling of visual attributes from functional contexts. In a standard UI:
- Light Mode:
background-canvasresolves toneutral-50(#ffffff), whiletext-bodyresolves toneutral-900(#111827). - Dark Mode:
background-canvasmaps toneutral-950(#030712), whiletext-bodymaps toneutral-100(#f3f4f6).
Because the UI components only consume the semantic reference
(background-canvas), the component logic itself remains
completely agnostic to the theme being rendered. There is no need for
conditional CSS rules, duplicate classes, or JavaScript-driven class
toggling at the component level.
Implementing Dynamic Token Resolution
The most efficient way to automate theme switching in the browser is through CSS Custom Properties mapped to root or data attributes.
In this architecture, primitive values are declared at the base level, and semantic aliases are defined inside specific theme scopes:
:root {
/* Primitive Tokens */
--color-white: #ffffff;
--color-gray-900: #111827;
--color-gray-100: #f3f4f6;
--color-gray-800: #1f2937;
/* Semantic Mappings for Default (Light) Mode */
--surface-primary: var(--color-white);
--surface-secondary: var(--color-gray-100);
--text-primary: var(--color-gray-900);
}
/* Semantic Overrides for Dark Mode */
[data-theme="dark"],
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--surface-primary: var(--color-gray-900);
--surface-secondary: var(--color-gray-800);
--text-primary: var(--color-white);
}
}Components simply consume the semantic variable:
.card {
background-color: var(--surface-primary);
color: var(--text-primary);
}When a user toggles the UI mode, the application only updates a
single attribute—such as setting data-theme="dark" on the
<html> or <body> element—or relies
automatically on the system's prefers-color-scheme query.
The browser updates all instances instantaneously via the CSS
cascade.
Scaling Across Design and Development
Semantic tokens also streamline collaboration between designers and engineers. Modern design platforms (like Figma Variables) allow designers to define modes matching the exact naming conventions used in the codebase. When a design changes from light to dark in the design tool, it mirrors the exact variable swapping occurring in production.
This shared taxonomy reduces the overhead of maintaining theme parity, guarantees consistent contrast ratios across modes, and allows entire applications to support dark mode out of the box with zero component-level overrides.