What L, C and H Mean
An oklch() value has three numbers where a hex code has three pairs, but they measure different things. A hex code says how much red, green and blue light to mix; OKLCH says how the result looks.
- L, lightness — from 0% (black) to 100% (white), scaled so that equal steps look like equal steps.
- C, chroma — how far the colour is from grey. 0 is a neutral grey; the most vivid colours a standard screen can show sit between about 0.15 and 0.32, depending on hue.
- H, hue — an angle around the colour wheel in degrees: reds near 20–30, yellows near 110, greens near 140, blues near 265.
So #C8102E becomes oklch(53.04% 0.2074 22.32): a red a little above mid-lightness and strongly coloured. The part-by-part box in the converter does the same reading for whatever code you enter.
OKLCH vs HEX: When Each Is Useful
Hex stays the most portable way to write a colour. Every browser, email client, design tool and brand guideline understands it, it maps exactly to 8-bit screen values, and it is what you paste into a Pantone lookup. For a fixed brand colour that only ever appears as itself, hex is enough.
OKLCH earns its place when you start changing colours rather than storing them:
- Perceptual lightness. Two colours with the same L look about equally light. Hex hides this completely: pure yellow
#FFFF00has an OKLCH lightness of 96.8% and pure blue#0000FFonly 45.2%, although both have a channel at FF. - Predictable palettes. Hold C and H and step L, and you get a tint-and-shade scale whose steps look even and whose hue does not drift, which is hard to do by nudging hex digits.
- Wide gamut. Hex can only describe sRGB. An oklch() value can describe the more saturated colours of Display P3 screens, and a browser on an ordinary screen brings them back into range for you.
A common split is to keep the hex code as the reference value in the brand guide and to generate hover states, tints and dark-mode variants from its oklch() form in the stylesheet. The guide to colour spaces compares RGB, HSL, HSV and LAB if you are choosing between models.
Using oklch() in CSS
oklch() works anywhere a colour does. The usual starting point is to store the converted brand colour in a custom property:
:root {
--brand: oklch(53.04% 0.2074 22.32); /* #C8102E */
}
.button { background: var(--brand); }Opacity goes after a slash, as a number or a percentage, so the 8-digit code #C8102E80 becomes:
.overlay {
background: oklch(53.04% 0.2074 22.32 / 0.502);
}To build lighter and darker steps, change only L and leave C and H as they are. Watch the ends of the scale, though: darker and lighter colours hold less chroma in sRGB. A pressed state at oklch(43% 0.2074 22.32) is already outside sRGB, so a browser has to pull it back into range; lowering C to oklch(43% 0.17 22.32) keeps it inside, as the OKLCH to HEX gamut check confirms.
Browser Support
oklch() is supported in Chrome and Edge 111 and later, Safari 15.4 and later, and Firefox 113 and later. Older browsers ignore a declaration they cannot parse, so writing the hex code first and the oklch() value second gives everyone a colour:
.button {
background: #C8102E;
background: oklch(53.04% 0.2074 22.32);
}For a colour converted from hex the two lines show the same thing, so the fallback costs nothing. Keep the hex line for HTML email too, where support for newer CSS colour syntax varies from one mail client to the next.
HEX to OKLCH Reference Values
The extremes, the three sRGB primaries and this site's brand purple, converted when this page was built. Green here is the primary #00FF00, which CSS calls lime; the CSS keyword green is the darker #008000.
| Colour | HEX | oklch() | L | C | H |
|---|---|---|---|---|---|
| White | #FFFFFF | oklch(100% 0 0) | 100% | 0 | 0 |
| Black | #000000 | oklch(0% 0 0) | 0% | 0 | 0 |
| Red | #FF0000 | oklch(62.8% 0.25768 29.23) | 62.8% | 0.25768 | 29.23 |
| Green | #00FF00 | oklch(86.64% 0.29483 142.5) | 86.64% | 0.29483 | 142.5 |
| Blue | #0000FF | oklch(45.2% 0.31321 264.05) | 45.2% | 0.31321 | 264.05 |
| Brand purple | #C44EED | oklch(64.57% 0.23988 316.07) | 64.57% | 0.23988 | 316.07 |
The primaries show how unevenly hex spreads lightness: red is 62.8%, green 86.64% and blue 45.2%, although each has one channel at FF and the others at 00.
How We Calculate
The hex code is read as an sRGB colour with the D65 white point. Each channel is decoded from its gamma curve to linear light, then converted to OKLab with the matrices Björn Ottosson published in A perceptual color space for image processing (2020). Chroma is the distance from the grey axis in OKLab and hue is its angle, which is the conversion the CSS Color 4 specification uses for oklch().
Lightness is printed as a percentage with 2 decimal places, chroma with 5 and hue with 2, trailing zeros removed. That is the least precision at which all 16,777,216 six-digit hex codes come back unchanged after a round trip through the printed oklch() string; we checked every one. Alpha is printed to 3 decimals, enough to tell all 256 alpha bytes apart. A hex code is always inside sRGB, so no gamut mapping is involved in this direction.
The Pantone panel uses the same weighted RGB ranking as our Pantone converters. Its matches are approximate screen comparisons, so confirm a final ink against a printed swatch book.