HSL was a gift when it arrived in CSS: finally, dials that made sense — hue, saturation, lightness — instead of inscrutable hex codes. But HSL has a flaw baked into its math, and once you see it you can’t unsee it: HSL’s “lightness” lies.
The lie, in one comparison
Ask HSL for yellow and blue at identical 50% lightness: hsl(60 100% 50%) and hsl(240 100% 50%).
#FFFF00background#0000FFbackgroundSame number, wildly different brightness: the yellow’s relative luminance is about 0.93, the blue’s about 0.07.
HSL is a geometric remapping of the RGB cube — its lightness is an average of channel values, not a model of human vision. Our eyes are far more sensitive to green-yellow light than to blue, and HSL simply ignores that. The practical damage: any system built on “same L means same prominence” — theme palettes, chart scales, auto-generated shades — quietly produces colors with completely different visual weight and completely different contrast ratios.
What OKLCH fixes
oklch(L C H) is built on OKLab, a color space designed so that numeric distance matches perceived difference. Same three friendly dials, but now they tell the truth:
- Equal L looks equally bright across hues — an L=0.7 red, green, and blue all carry similar visual weight, and similar contrast against a given background.
- Hue stays put. Darkening a blue in HSL drifts it toward purple; in OKLCH, H is perceptual, so shades of a brand color stay on-brand. This is what makes the keep-the-hue contrast fix work.
- Access to more colors. OKLCH can address the wider P3 gamut modern screens ship with; HSL is fenced into sRGB.
Where you see it most: gradients
Blend two saturated, hue-distant colors in plain sRGB and the middle of the gradient sags into gray — the notorious dead zone. Interpolating in OKLCH keeps the midpoints vivid:
#1392E6start stop#E40078end stop#1392E6start stop#E40078end stopBlue to magenta, same stops. The sRGB blend dulls in the middle; the OKLCH blend stays saturated. (Rendered live by your browser with linear-gradient(in oklch, …).)
Support and syntax
oklch() has shipped in every major browser since 2023 (Chrome 111, Safari 16.4, Firefox 113) — as has gradient interpolation syntax. Usage looks like:
.button {
background: oklch(0.55 0.17 250); /* L C H */
background: oklch(0.55 0.17 250 / 80%); /* with alpha */
}
.hero {
background: linear-gradient(in oklch, #1392e6, #e40078);
}HSL isn’t going anywhere and stays fine for quick one-off tweaks. But for anything systematic — palettes, shade scales, gradient ramps, contrast math — reach for OKLCH first.
Try it in the tool
Everything on PaletteSense runs on OKLCH: the palette generator rotates hues at constant perceptual lightness, the gradient editor blends in OKLCH with live contrast analysis, and every page in the color library lists the OKLCH values.