OKLCH Color: Perceptual Lightness, Chroma and Hue
oklch() sets a colour by lightness, chroma (colourfulness) and hue in the OKLab perceptual space: oklch(0.72 0.15 230). Because the space matches human vision, equal numeric steps look like equal visual steps, unlike hsl().
Why it matters
OKLCH fixes the thing that makes HSL palettes look uneven. At 50% lightness in HSL, a yellow reads far brighter than a blue. In OKLCH, the lightness axis is perceptual: equal numeric steps actually look like equal visual steps.
That means you can generate accessible, consistent colour ramps with plain arithmetic. Shift the lightness from 0.3 to 0.9 while keeping hue constant, and every step looks equally different to the eye, whether the hue is yellow, blue, or anything in between.
OKLCH shipped in Chrome, Safari and Firefox in 2023. The only real caveat is old browsers, which you cover with a hex fallback declared just before the oklch() line.
How it works
OKLab is Björn Ottosson's perceptual colour space, fit to match human vision better than sRGB. OKLCH is the polar form: instead of Cartesian coordinates, it uses lightness (a brightness axis 0 to 1), chroma (colourfulness, how far from grey), and hue (the angle, 0 to 360).
- Lightness
0is black,1is white. - Chroma is unbounded but typically 0 to 0.3 for sRGB colours; higher values may fall outside the RGB gamut.
- Hue is 0–360, like HSL.
The key difference from HSL: the lightness axis is perceptually linear, so a change from 0.3 to 0.4 looks the same as 0.7 to 0.8, across any hue. Yellow at 0.5 looks as bright as blue at 0.5.
Syntax: oklch(lightness chroma hue), optionally with alpha: oklch(0.72 0.15 230 / 0.8).
What does not matter
OKLCH is not "experimental and unsupported." The Chromium browsers, Safari, and Firefox all shipped it in 2023. Yes, older browsers will not parse it, but that's what the fallback is for. Declare a hex or rgb colour right before the oklch(), and old browsers use the fallback; modern ones use the better one.
Chroma can exceed sRGB bounds. Some combinations of high chroma and certain hues produce colours that cannot be shown on an RGB screen. The extension flags these as "out of gamut"; modern CSS will clamp them at display time. It is not a failure; it just means you picked a very vivid colour that the screen will tone down slightly.
Code example
/* Perceptually even palette: walk the lightness while keeping hue and chroma constant */
:root {
/* Colour 1: light (lightness 0.8) */
--primary-100: oklch(0.8 0.1 230);
/* Colour 2: standard (lightness 0.6) */
--primary-500: oklch(0.6 0.15 230);
/* Colour 3: dark (lightness 0.35) */
--primary-900: oklch(0.35 0.15 230);
}
/* Fallback for older browsers */
.button {
background: #38bdf8; /* Hex fallback for browsers without oklch() */
background: oklch(0.72 0.15 230); /* Modern browsers use this */
}
/* Wrong: no fallback for old browsers */
.text {
color: oklch(0.5 0.2 45); /* Fails silently in Safari 16 and older */
}
How Scalpel Color shows it
Open the Picker tab. The OKLCH row shows the current colour's lightness, chroma and hue. You can adjust each with sliders. If the colour falls outside the sRGB gamut, the extension flags it and shows what the clamped result will look like. The format select lets you switch to hex, HSL, or another format at any time.