Typical design systems carry fifteen hand-mixed shades of one brand color, made in a design tool and pasted by hand. color-mix(in oklab, ...) derives those shades in the browser from the same two custom properties, so 'the brand, 12 percent washed' is a function instead of a screenshot.
:root {
--ink: #111111;
--brand: #bb0000;
}
.card { background: color-mix(in oklab, var(--brand) 8%, white); }
.card:hover { background: color-mix(in oklab, var(--brand) 16%, white); }
.muted { color: color-mix(in oklab, var(--ink) 55%, white); }Dark mode is a block, not a redesign
@media (prefers-color-scheme: dark) {
:root { --ink: #eeeeee; --brand: #ff6659; }
}
/* every mix above re-resolves; nothing else changes */Rules that keep it honest
- Mix perceptually: oklab keeps tints looking consistent across hues; srgb is fine for grays.
- Keep a contrast floor: a 5-percent brand mix is a background color, never a text color.
- Name the recipes, not the results: --surface-quiet beats --red-04.
This very site runs on two custom properties plus mixes; the whole warm-zine to white-broadsheet redesign was one block of token edits. That is the payoff when colors are data.
