Why OKLCH, and what HSL gets wrong
HSL lightness is a geometric convenience, not a perceptual one. Here is what that costs you when building a colour system, and what OKLCH fixes.
Almost every colour picker you have used speaks HSL: hue, saturation, lightness. It is easy to reason about and it maps neatly onto a wheel. It is also, in one specific and expensive way, wrong.
The problem in one line
In HSL, hsl(60 100% 50%) and hsl(240 100% 50%) both claim 50% lightness. The first is yellow, which is nearly as bright as white. The second is pure blue, which is nearly as dark as black. Their WCAG contrast against white differs by roughly a factor of ten. The number is the same; the experience is not.
This matters the moment you try to build a scale. A design system usually wants --blue-500 and --green-500 to be interchangeable in a layout — same apparent weight, same legibility over the same background. In HSL they simply are not, so teams end up hand-tuning every ramp and re-tuning it whenever a hue changes.
What OKLCH does instead
OKLCH is the polar form of OKLab, a colour space fitted to how people actually perceive difference. It has the same three ideas with honest values:
- L — perceptual lightness, 0 to 1. Equal numbers look equally light across every hue.
- C — chroma, how far from grey. Unbounded in principle; sRGB tops out around 0.37 and much lower at the extremes of lightness.
- H — hue angle in degrees, and it behaves: stepping 30° looks like a consistent step everywhere on the wheel, which is not true of HSL hue.
The practical consequence is that a lightness ramp defined once works for every hue, and a harmony built on hue angles produces evenly spaced colours rather than a cluster in the greens and a gap in the blues.
The two complications worth knowing
Not every OKLCH colour exists
OKLCH can describe colours sRGB cannot display — a very light, very saturated green, for instance. Something has to give. Colorstruct offers three documented behaviours: reduce chroma while holding lightness and hue exactly (the default, and what CSS Color 4 specifies), shift lightness toward where the hue can carry the chroma, or clip the channels. Clipping is fastest and worst: it moves hue and lightness in ways you did not ask for.
Hue can be meaningless
When chroma is zero the colour is grey, and its hue angle carries no information at all. CSS calls this a powerless hue. Colorstruct pins it to 0 and says so, rather than showing a number that changes nothing.
Using it today
Every current browser supports oklch() in CSS, and gradients can interpolate in it. Colorstruct exports both the OKLCH values and a hex fallback for every palette, so adopting the space does not mean dropping support for anything.