Color Intermediate

Perceptually uniform colors with oklch

HSL looks like it should be perceptually uniform, but it isn't. Yellow at hsl(60 100% 50%) appears far brighter than blue at hsl(240 100% 50%) at the same lightness. oklch uses a model where L actually means the same perceived brightness across all hues.

Modern
5 lines
1/* oklch: L is perceptually uniform across hues */
2--brand: oklch(0.55 0.2 264);
3--brand-light: oklch(0.75 0.2 264);
4--brand-dark: oklch(0.35 0.2 264);
5/* only L changes */
Old 8 lines
1/* HSL: same L value, different perceived brightness */
2--yellow: hsl(60 100% 50%); /* blinding */
3--blue: hsl(240 100% 50%); /* dark */
4/* manually tweak each shade to look balanced */
5--brand-light: hsl(246 87% 68%);
Tailwind v4 uses oklch natively for all colors
change only L — chroma and hue stay constant
Light
L: 0.85
Mid
L: 0.65
Dark
L: 0.45
Darker
L: 0.25
HSL palette
uneven perceived brightness
oklch palette
perceptually uniform
Oklch
Widely available Since 2023 90% global usage
111+
113+
15.4+
111+

This feature is well established and works across many devices and browser versions. It has been available across browsers since 2023.

Predictable lightness

L: 0.5 looks the same perceived brightness in oklch regardless of the hue. That's not true in HSL.

Easy palette generation

Change only L for lighter or darker shades. Change only H to shift hue. Change only C for saturation. They're independent.

Wide gamut ready

oklch can express P3 colors that hex and sRGB can't. On wide-gamut displays the chroma can go higher than sRGB allows.

Old Approach
Hex / HSL
Guess-and-check each shade
Modern Approach
oklch()
Adjust L, C, H independently
Color model
L · C · H
Lightness · Chroma · Hue

How it works

HSL was supposed to be human-friendly but its lightness channel is not perceptually uniform. Yellow at hsl(60 100% 50%) looks far brighter than blue at hsl(240 100% 50%) even though both have L: 50%. Building a consistent color palette means manually adjusting each shade by eye until it looks balanced.

oklch(L C H) uses a perceptually uniform lightness model. L: 0.55 looks the same perceived brightness whether the hue is green, orange, or purple. To create a lighter shade, increase L. To shift hue, change H. Chroma C controls saturation. All three values are genuinely independent in a way HSL's S and L are not.

New CSS drops every month.

Get one old → modern comparison in your inbox every week.

ESC