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.

Flat editorial illustration showing wireframe bricks and translucent layer panels assembling into a small browser window outline with dual panes.
Illustration: this article at a glance.

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.

Need a website built, fixed, optimised, migrated or replaced?

This technical resource is written by CSMBAC, a small design and development studio. If you would rather hand the problem to a professional, the website service page explains how we build enquiry-ready websites.

Explore website services