On this page
The problem OKLCH solves
HSL looks like it should make lightness and saturation predictable, but it doesn't — because HSL is a simple geometric transformation of RGB, not a model of how the human eye actually perceives brightness. Two colors with identical HSL lightness values can look dramatically different in actual perceived brightness: pure yellow (hsl(60, 100%, 50%)) looks far lighter than pure blue (hsl(240, 100%, 50%)) at the "same" 50% lightness. That mismatch is why programmatically generating shades or tints from HSL often produces uneven, sometimes muddy results.
OKLab and OKLCH, briefly
OKLab is a color space (published by Björn Ottosson in 2020) explicitly designed so that equal numeric distances correspond to roughly equal perceived differences — a property HSL and even the older, more established LAB space don't fully deliver. OKLCH is simply OKLab expressed in polar coordinates: Lightness, Chroma (roughly "how far from gray"), and Hue in degrees — the same L/C/H structure as the older LCH space, but built on the more accurate OKLab foundation.
Where this actually matters in practice
Generating a set of shades (darker) or tints (lighter) of a base color is far more even in OKLCH than in HSL, because "lightness" in OKLCH tracks perceived brightness rather than a raw geometric value. The same is true for blending two colors: averaging in OKLab space avoids the muddy, grayish middle step that naive RGB averaging produces when mixing, say, a saturated red and a saturated green.
Why Tailwind CSS moved its whole palette to OKLCH
As of Tailwind CSS v4, every default color — from slate-50 to rose-950 — is defined in OKLCH rather than hex. This lets the framework guarantee more perceptually consistent lightness steps across all 22+ color families and every shade from 50 to 950, something that was harder to keep consistent when each family's hex values were tuned by hand.
Browser support
oklch() and oklab() are supported natively in CSS in all major browsers as of 2023–2024, meaning you can write color: oklch(70% 0.15 250) directly — no preprocessing or JavaScript required, with the browser converting to display-appropriate RGB automatically.
How to read an oklch() value
oklch(70% 0.15 250) breaks down as: L = 70%, perceived lightness — unlike HSL's L, equal steps here genuinely look like equal steps, and the number matches intuition across every hue. C = 0.15 is chroma, the distance from gray — 0 is achromatic, and the practical ceiling varies by hue (sRGB yellows top out near 0.21 while some blues exceed 0.3). H = 250 is hue in degrees — but on OKLab's hue circle, not HSL's: the angles don't line up (HSL's 240° blue lands near 264° in OKLCH), so never copy a hue number between the two notations and expect the same color.
The gotchas before you adopt it
- Chroma clips. Not every L/C/H combination exists in sRGB. Ask for
oklch(90% 0.3 30)and the browser must gamut-map to the nearest displayable color — you silently get less chroma than you wrote. Keep chroma modest at extreme lightness values. - Wide-gamut screens change the answer. On a Display-P3 monitor some previously clipped values become real — the same physics our gamut guide covers. Check on an sRGB screen if that's what most of your users have.
- Tooling lag. Every modern browser renders
oklch(), but plenty of design tools and older pipelines still speak only hex and HSL, so storing tokens in OKLCH can mean converting at the boundaries.
The scale-building payoff
The clearest demonstration is a lightness ramp. Take one hue and generate ten steps from light to dark in HSL, then in OKLCH. The HSL ramp bunches — steps near yellow blow out bright while blues plunge dark, and mid-steps drift in apparent saturation. The OKLCH ramp simply descends evenly, and every hue behaves identically — which is precisely what a 50–950 design-system scale needs, and why Tailwind rebuilt its entire default palette on it. If you're building your own ladder, our design-system guide walks the method step by step.
OKLCH and contrast checking
Perceptual lightness makes contrast intuition better — two colors far apart in OKLCH lightness will usually contrast well — but WCAG's ratio is computed from its own luminance formula, so the contrast checker remains the authority. A useful habit: fix your text and surface colors' OKLCH lightness values (say, text at L 25% on surfaces at L 98%) and contrast stays consistent as you rotate hue — something HSL could never promise.
Do you need to migrate?
Not necessarily. HSL remains fine for hand-tweaks, hex remains the universal interchange, and nothing about an existing site breaks by staying put. OKLCH earns its keep when colors are computed: generated scales, dark-mode variants, hover states, and data-viz palettes where "equally spaced" must mean equally spaced to the eye. A pragmatic setup: author and manipulate in OKLCH, let build tooling emit hex fallbacks, and reach for @supports (color: oklch(0% 0 0)) only if you must support pre-2023 browsers.