Why raw colors break as your product grows
Every design system eventually hits the same wall: a single brand color is referenced in 400 places, a rebrand happens, and now someone is doing a find-and-replace on #3B82F6 across the entire codebase. Design tokens exist to prevent exactly this. A token is a named decision—color.primary—that points to a value, so the value can change once and update everywhere.
The mistake most teams make is treating "tokens" as a fancy word for "variables." A flat list of variables (blue, blue2, blueDark) is barely better than hex codes. The power comes from layering tokens by intent.
The three layers of a color token system
1) Primitive (or "core") tokens
These are your raw palette: the full ramp of every color, named by what they are, not what they do.
--blue-50: #EFF6FF;
--blue-500: #3B82F6;
--blue-600: #2563EB;
--slate-50: #F8FAFC;
--slate-900: #0F172A;
Primitives never get used directly in components. They are the box of crayons, not the drawing.
2) Semantic tokens
This is the layer that actually matters. Semantic tokens describe intent: what a color means in your UI. They reference primitives.
--color-primary: var(--blue-600);
--color-surface: var(--slate-50);
--color-border: var(--slate-200);
--color-text-primary: var(--slate-900);
--color-text-muted: var(--slate-500);
--color-danger: var(--red-600);
Now your buttons use --color-primary. When the brand shifts from blue to indigo, you change one line and the whole product follows. This is also exactly how instant dark mode works—you swap the semantic layer, not the components.
3) Component tokens (optional)
For large systems, you can add a third layer that scopes semantics to a component: --button-primary-bg: var(--color-primary). This is overkill for small projects, but it lets you, say, restyle every button without touching the global primary.
Naming rules that save you pain
- Name by role, not appearance. Use
--color-text-muted, not--gray-text. In dark mode "gray text" might actually be light. - Be consistent with scales. Pick one numbering system (50–900) and stick to it across every hue.
- Reserve semantic colors. Once
dangermeans red, never reuse that red for decoration—it dilutes meaning for users.
A practical migration path
You do not need to boil the ocean. Start small:
- Extract the 8–12 colors you actually use into primitive tokens.
- Create a semantic layer for the essentials: primary, surface, border, text-primary, text-muted, danger, success.
- Refactor your most-used component (usually buttons) to consume semantic tokens.
- Expand outward, one component at a time.
Every palette on UI Colors Lab already ships with a semantic CSS-variable export and a Tailwind config that follows this exact structure—so you can copy a working token layer instead of building it from scratch. Browse a starting point in our Dashboard palettes or read our Tailwind color best practices guide for the framework-specific version.
The payoff
A layered token system is the difference between a rebrand being a one-day task or a one-month migration. It makes dark mode a configuration change instead of a rewrite, and it gives every developer on your team a shared vocabulary for color. Set it up early—your future self will thank you.