Why colour tools give different answers
HSL says fifty per cent lightness for both a searing yellow and a near-black blue. Everything else follows from that.
The problem with the spaces we already had
sRGB addresses a screen. Its three numbers say how hard to drive the red, green and blue subpixels, and nothing at all about how the result looks. HSL rearranges those same numbers into a cylinder that is easier to talk about — hue, saturation, lightness — and inherits the same silence.
The consequence is easy to demonstrate. hsl(60 100% 50%) is a searing yellow. hsl(240 100% 50%) is a blue so dark it is nearly black. Both claim fifty per cent lightness. The number describes a height in a geometric solid; it is not a measurement of anything a person perceives.
That single fact is behind most of the failures designers recognise without naming: the ramp with a muddy middle, the palette where the yellow shouts and the blue disappears, the gradient with a grey band through it.
What OKLab and OKLCH change
OKLab, published by Björn Ottosson in 2020, is built for one purpose: equal numeric differences should look like equal differences. Its lightness axis tracks perceived lightness. Its two chroma axes are arranged so that a straight line between two colours passes through the colours a person would name in between.
OKLCH is the same space in polar form — lightness, chroma, hue — which is the shape design work actually wants. It gives you three things HSL cannot:
- A lightness you can compare. Two colours at L 0.62 look equally light, whatever their hue. A ramp with even lightness steps has even perceived steps.
- A hue that survives editing. Change lightness or chroma and the hue is exactly where you left it. In HSL, moving lightness on a saturated colour shifts the apparent hue noticeably.
- Interpolation that keeps its colour. Blend blue into yellow and the middle stays a real colour instead of collapsing toward grey.
Every generator on this site works in OKLCH for exactly these reasons. The ramp tool moves lightness along a fixed perceptual curve; the harmony tool rotates hue while holding lightness and chroma; the gradient tool interpolates in OKLab and shows you the sRGB version alongside so the difference is visible rather than asserted.
Gamut: naming a colour your screen cannot show
OKLCH can describe colours that do not exist in sRGB. So can Display P3. That is a feature — wide-gamut screens are common now — and it creates a problem at export: something has to happen when the colour has to be shown on a screen that cannot reproduce it.
The obvious answer is to clamp each channel into range. It is fast, it is one line, and it is wrong. Clamping the three channels by different amounts changes their ratios, and their ratios are the hue. A vivid cyan clipped that way comes back a different cyan, and nothing downstream can tell that it happened.
The approach used here is to reduce chroma at constant lightness and hue until the colour fits, by binary search, and to report how much was lost. The colour becomes less saturated — which is unavoidable, because that is what "outside the gamut" means — but it stays the same colour and the same weight.
Losing saturation is a visible, explicable compromise. Losing hue is a bug that looks like a design decision.
Progressive CSS, and why two declarations
Browsers that understand oklch() and the in oklab interpolation hint are now the majority, but a declaration containing an unrecognised function or keyword is invalid rather than merely ignored. Writing only the modern form means older browsers get no value at all.
So the exports here write a plain sRGB declaration first and the modern one second. Anything that understands the second overrides the first; anything that does not keeps the fallback. Removing the first line does not simplify the CSS — it removes the fallback.
Where CIE Lab fits, and its white point
CIE Lab predates OKLab by decades and is the space CSS lab() and lch() refer to. It is perceptual, standardised and widely supported in colour management, and it is less uniform than OKLab in the blues — which is precisely where design work notices.
One detail matters more than it appears to: Lab is defined relative to a white point, and CSS uses D50. A Lab value quoted without its white point is not a colour, it is three numbers. The converter states D50 explicitly rather than leaving you to assume.
CMYK is not in this picture
There is no device-independent CMYK. A conversion from sRGB without an ICC profile and a known press is a rough approximation and cannot be otherwise. Where CMYK appears on this site it is labelled not print-proof, because a printer's own conversion will differ and sometimes considerably.
Contrast is a separate question
WCAG contrast is computed from sRGB relative luminance, not from OKLCH lightness. The two correlate but they are not the same measurement, and a pair that looks like it should pass can fail.
Two things are worth knowing about how it is computed here. The ratio is judged before rounding — a pair at 4.4996 displays as 4.50 and does not pass, and a great many tools get this wrong. And alpha is composited against a stated backdrop rather than assumed to be white, because assuming white is how a tool reports a comfortable pass for text nobody can read.
One fact that surprises people: at AA for normal text there is always a passing on-colour. The worst possible background is the lightness where black and white tie, and they tie at 4.583:1 — above the threshold. A "neither black nor white works" warning at 4.5:1 would be a bug. The band only opens at AAA.
What none of this proves
A palette generated perceptually and checked for contrast is a better starting point than one that is not. It is still only a starting point. Meeting a ratio satisfies one success criterion out of many, and says nothing about focus order, labelling, motion, or whether the interface makes sense.
And a colour-vision simulation is a simulation. Colour vision varies between individuals with the same classification, and a design that survives every filter here can still fail in use. The simulator leads with the pairs that collide rather than a picture, because that is the part you can act on — but neither is a substitute for testing with people.