Reference guide · html-css-js · Published 2026-08-16 · 3 min read
CSS color-mix and OKLCH
color-mix and OKLCH let you mix and define colors in a perceptually uniform space. Learn the syntax and wide-gamut handling.
- ·Mix colors with color-mix
- ·Choose OKLCH
- ·Go wide gamut safely
Why modern color functions matter
For years a brand color had to be hand-mixed: an author picked a hex or HSL value for a tint, and keeping every shade consistent meant maintaining a long list of hard-coded colors. CSS now ships two capabilities that remove that labour. The color-mix() function mixes two colors into one using any supported color space, and the oklch() (and oklab()) color space is designed to be perceptually uniform, which means equal numeric steps are close to equal visual steps. Both are baseline and widely available, so they are production-ready.
Together they let a theme derive all of its shades from a single base color instead of a hand-maintained palette, which is a natural fit for the custom properties pattern most design systems build on.
Mix colors with color-mix
:root {
--brand: oklch(0.62 0.18 265);
--surface: color-mix(in oklab, var(--brand) 6%, white);
--text: color-mix(in oklab, var(--brand) 30%, black);
}
color-mix(in oklab, A 40%, B) produces a blend of A and B at the given ratio. The color space you pick for the mix changes the result noticeably: mixing in oklab or oklch tends to stay perceptually even, while srgb mixing can darken or gray out the result. Specifying the space is not optional, because the two inputs may themselves be in different spaces and the browser needs a common one to mix in. This is the mechanism behind consistent hover states, border tints and surface colors that all derive from one brand value.
Choose OKLCH
oklch() expresses a color by lightness, chroma and hue, the same axes writers already think in for HSL but on a space tuned to human perception. Where HSL lightness is not perceptually uniform, equal oklch() lightness steps look evenly spaced. For interactive elements this matters: two colors a designer marks as equally light actually land that way, and a gradient built in oklch holds its perceived contrast across the whole ramp. See how that interacts with the cascade layers when you distribute the color tokens across a larger stylesheet.
The color model is also the scaffolding for relative color syntax, which lets you take an existing color and adjust one channel without knowing its other components:
--brand-hover: oklch(from var(--brand) l c calc(h + 12));
Go wide gamut safely
oklch can represent colors outside the classic sRGB gamut, which lets you target P3-capable displays. Browsers map out-of-gamut colors to their closest displayable shade automatically, so a display-p3 color degrades gracefully on a screen that cannot show it. To make use of the extra range only where the hardware supports it, gate with the color-gamut media query:
@media (color-gamut: p3) {
.accent { color: oklch(0.6 0.22 265); }
}
Keep the fallback value on the base rule so older or narrower-gamut screens see a safe sRGB color. Because these are small, purely visual declarations, they have no meaningful performance cost of their own, but they combine cleanly with the broader optimisation work in that article.