Short answer
Because its lightness number tracks perceived lightness far better than HSL's, so colours with the same L look about equally light across hues, and you can build tints, shades and palette ramps by changing one number without the hue drifting. The catch is chroma: the most saturated colour available differs a lot by hue and lightness, so an OKLCH palette needs its chroma chosen per hue and checked against the sRGB boundary.
oklch(L C H) has three components. L is perceived lightness from 0 (black) to 1 or 100% (white). C is chroma, how far from grey the colour is; it starts at 0 and in practice rarely exceeds about 0.37 for real display colours, and CSS treats 100% as 0.4. H is the hue angle in degrees. The hue angles are not the same as HSL's: in OKLCH, red sits around 25–30°, yellow around 110°, green around 140–150°, blue around 265°. Converting a palette from HSL therefore changes every hue number, and design tokens should say which model their numbers belong to.
HSL's lightness is a property of the RGB numbers, not of vision. Fully saturated HSL colours at 50% lightness range from a yellow almost as light as white to a blue barely lighter than navy, so text on a 'same lightness' HSL palette swings between readable and illegible as the hue changes. In OKLCH, colours at the same L have much more similar luminance. The table on this page makes the point with contrast against white: an HSL row at fixed saturation and lightness varies by a large factor, the OKLCH row at fixed L and C stays within a narrow band. This is what makes OKLCH useful for systems that generate many hues at once, such as status colours, charts and theming.
The sRGB gamut is lopsided in OKLCH. Blues and magentas can reach high chroma only at low-to-middle lightness; yellows only at high lightness; cyans have little chroma anywhere in sRGB. A single rule such as 'every accent at C 0.2' produces colours some hues cannot reach, which the browser then gamut-maps — each to a different lower chroma. Workable approaches are to pick one chroma low enough that every hue in the palette can meet it at the chosen lightness, or to scale chroma per hue towards each hue's maximum. Tools that plot the gamut boundary for each hue make this visible instead of a surprise.
Dark-mode palettes suffer most: at low lightness only blue, violet and red keep much chroma, so dark-mode accents often need slightly higher L than their light-mode counterparts.
A ramp of shades for one hue — 50 to 950 in many design systems — can be defined as a sequence of L values at constant H, with C tapering towards the ends where the gamut narrows. Hover and pressed states become simple offsets, for example relative colour syntax with calc(l - 0.05). Because lightness is perceptual, equal L steps give visually even steps. OKLCH is not a contrast checker, though: WCAG contrast uses its own luminance formula, and OKLCH L and WCAG luminance disagree enough that a palette built on L still needs checking. Nor does OKLCH model context — the same swatch still looks different on a dark or a light background.
| Hue angle (each model's own) | hsl(h 70% 45%) — contrast with white | oklch(0.6 0.09 h) — contrast with white |
|---|---|---|
| 30° | #c37322 — 3.62:1 | #b06b60 — 4.10:1 |
| 90° | #73c322 — 2.20:1 | #957e3c — 3.94:1 |
| 145° | #22c365 — 2.32:1 | #5d8f5e — 3.78:1 |
| 200° | #228ec3 — 3.67:1 | #2b9095 — 3.80:1 |
| 265° | #6522c3 — 8.17:1 | #667fb6 — 3.98:1 |
| 330° | #c32273 — 5.53:1 | #9f6c9a — 4.13:1 |
Why: Chroma was fixed while each hue's available maximum differs; some were gamut-mapped.
Fix: Choose chroma per hue relative to that hue's maximum at the chosen lightness.
Why: HSL hue angles were copied into OKLCH as if they were the same scale.
Fix: Convert colours, not numbers; use a converter and re-check each token.
Each statement is labelled by kind — established fact, a standard’s requirement, observed market data, a convention, or Colourwise’s own interpretation or analysis — with the strength of the evidence behind it.
StandardStrong evidence
In CSS oklch(), lightness runs from 0 to 1 (0% to 100%), chroma from 0 with 100% equal to 0.4, and hue is an angle in degrees whose values differ from HSL hue angles.
Source: CSS Color Module Level 4; MDN Web Docs: oklch() and the color-gamut media feature
FactModerate evidence
OKLab was designed to predict perceived lightness, chroma and hue well, fitted to CAM16-derived data for lightness and chroma and to the Ebner–Fairchild data for hue.
Caveat: Self-published definition; its performance claims are the author's own comparisons, though widely replicated in practice.
Source: A perceptual color space for image processing (Oklab)
Colourwise analysisStrong evidence
Colours with fixed HSL saturation and lightness vary several-fold in contrast against white across hues, while colours with fixed OKLCH lightness and chroma vary much less.
Based on: Computed by Colourwise with WCAG 2.2 contrast for the sample colours in the table on this page.
Source: CSS Color Module Level 4; Web Content Accessibility Guidelines (WCAG) 2.2; A perceptual color space for image processing (Oklab)
ConventionModerate evidence
Building design-system colour ramps as steps of OKLCH lightness at constant hue is a design convention, not a standard; it still requires contrast checking against WCAG.
Source: CSS Color Module Level 4; Web Content Accessibility Guidelines (WCAG) 2.2
Reviewed 29 September 2026. Colourwise summarises its sources in its own words and does not reproduce standards text or proprietary colour data. Spotted an error? Tell us.