What OKLCH is
oklch() describes a colour by three numbers people can reason about: L, perceived lightness from 0% (black) to 100% (white); C, chroma, which is how far from grey the colour is (0 is grey, and sRGB colours top out at about 0.32); and H, the hue angle in degrees. It is the polar form of OKLab, a colour space Björn Ottosson designed in 2020 so that equal steps in its numbers look like equal steps to the eye. Converting #ff0000 gives oklch(62.80% 0.2577 29.23), which matches the value MDN quotes for red.
Why OKLCH beats HSL for palettes and contrast
HSL's lightness is simply the average of the largest and smallest RGB channel. It ignores the fact that our eyes are far more sensitive to green and yellow than to blue, so two colours with the same HSL lightness can be wildly different in brightness:
| Colour | HSL lightness | OKLCH lightness | Contrast with white |
|---|---|---|---|
hsl(60 100% 50%) yellow | 50% | 96.80% | 1.07:1 |
hsl(240 100% 50%) blue | 50% | 45.20% | 8.59:1 |
White text on that blue passes WCAG AAA; on the yellow it is unreadable, yet HSL calls them equally light. OKLCH's L tracks what you actually see, so you can build a palette by holding L steady across hues and get rows of swatches that look equally light, or step L down evenly to get shades that darken evenly. Because L follows perceived brightness, it is also a far better predictor of contrast: if your text and background are far apart in OKLCH lightness, the WCAG ratio will usually be high. Always confirm with the actual ratio — WCAG 2.2 uses its own relative-luminance formula, which this tool applies to the 8-bit sRGB value (0.04045 threshold) — and use the contrast checker for real text-and-background pairs. Our WCAG colour contrast guide explains the thresholds, and the colour palette generator turns one brand colour into a full tint and shade scale.
Browser support for oklch()
According to MDN's compatibility data, oklch() and oklab() work in Chrome and Edge 111, Firefox 113 and Safari 15.4 (iOS included), and have been Baseline across browsers since May 2023. Two later additions are worth knowing about. Mixing value types — writing L as a plain number like 0.63 or C as a percentage — needs Chrome 116 or Safari 16.2, so this converter always writes L as a percentage and C as a number, the form every oklch()-capable browser accepts. Relative colour syntax (oklch(from var(--brand) calc(l - 0.1) c h)) arrived fully in Chrome 122, Firefox 128 and Safari 18.
Gamut: why some OKLCH colours can't be shown
Hex, rgb(), hsl() and hwb() can only describe sRGB, the colour range of a standard monitor. OKLCH has no such limit: raise the chroma far enough and you describe colours that only wide-gamut Display P3 screens can show, or that no screen can. Try oklch(70% 0.35 145) above: it is a vivid green beyond sRGB. On an sRGB display the browser has to bring it into range, and the converter shows the result it uses as the fallback, #00bf34.
The mapping here is deliberately simple: keep the OKLCH lightness and hue, binary-search for the largest chroma that fits inside sRGB, then clip the tiny remainder. The CSS Color 4 specification's algorithm is a refinement of the same idea that stops early once the clipped colour is within a just-noticeable difference (0.02 in OKLCH), so browsers may differ from this fallback by a hair. A colour counts as in gamut here if it sits within one 8-bit step of the sRGB edge, because clipping that little is invisible.
When to keep hex
Hex is still the right choice for colours that come from a brand guide, for email templates (many mail clients won't parse modern colour functions), for SVG attributes read by older tools, and anywhere you need byte-exact values to match a design file. Every hex colour converts to OKLCH and back exactly: the converter checks that the oklch() text it gives you rounds back to the same hex, and adds a decimal place in the rare cases (about one colour in a thousand, mostly with a channel near zero) where standard precision wouldn't. Reach for OKLCH when you are generating colours — hover states, palettes, themes — rather than storing ones somebody else chose.
How to write fallbacks
For an ordinary property, write the hex first and the oklch() second. Browsers that don't understand the second declaration discard it and keep the first. That trick fails with custom properties: --brand: oklch(…) is valid to every browser at parse time, so it overwrites the hex even where it can't be rendered. Wrap the modern value in @supports (color: oklch(0% 0 0)) instead — the snippet above includes both patterns. For colours outside sRGB, the hex in the snippet is the gamut-mapped version, so sRGB screens and older browsers get the closest colour they can show while P3 screens get the full value.