OKLCH Color Space Explained

May 28, 20266 min read

The Problem OKLCH Solves

Traditional color spaces like HSL and RGB have a critical flaw: they don't match human perception. In HSL, a lightness value of 50% doesn't look equally bright across different hues. A yellow at HSL lightness 50% appears much brighter to the eye than a blue at the same value. This makes HSL unreliable for design systems.

RGB is worse — it's tied to hardware (red, green, blue phosphors) and tells you nothing about how a color actually looks to a human.

OKLCH (Okabe-Lab Chroma-Hue) fixes this by being perceptually uniform. Equal numeric changes produce equal visual changes, regardless of hue. This makes it the first color space that works the way designers actually think.

How OKLCH Works

OKLCH uses three components:

ComponentWhat It ControlsRangeAnalogy
L (Lightness)Perceived brightness0–1HSL lightness, but accurate
C (Chroma)Color intensity/saturation0–0.4+HSL saturation, but uniform
H (Hue)Color angle on the wheel0–360Same as HSL hue
oklch(L C H)
oklch(0.7 0.15 150)  /* A medium-bright green */

Why Perceptual Uniformity Matters

Consider two colors at the same HSL lightness (50%):

ColorHSLPerceived Brightness
Yellowhsl(50, 100%, 50%)Very bright
Bluehsl(230, 100%, 50%)Much darker

In OKLCH, you'd express these with different lightness values to match their actual perceived brightness:

ColorOKLCHPerceived Brightness
Yellowoklch(0.82 0.18 90)0.82 (bright)
Blueoklch(0.45 0.15 260)0.45 (dark)

This is the key difference: OKLCH lightness matches what your eyes see.

OKLCH vs Other Color Spaces

FeatureOKLCHHSLRGBLCH
Perceptually uniformYesNoNoMostly
Hue-independent lightnessYesNoNoMostly
CSS supportModern browsersAllAllModern browsers
Intuitive for designYesYes (misleadingly)NoYes
No hue shift in gradientsYesNoN/ASome

OKLCH vs LCH

LCH (CIE LCH) was an earlier attempt at perceptual uniformity. It's better than HSL but still has a known issue: the blue-purple shift. Adjusting lightness in LCH can unintentionally shift the hue, particularly in the blue range. OKLCH corrects this with a newer underlying model (OKLAB), making it the most perceptually accurate option available.

Practical Uses in CSS

Consistent Shade Scales

The biggest win: generate shade scales by changing only lightness while keeping chroma and hue constant. The result is visually even steps — no surprises:

:root {
  --brand-50:  oklch(0.97 0.03 260);
  --brand-100: oklch(0.93 0.06 260);
  --brand-200: oklch(0.85 0.10 260);
  --brand-300: oklch(0.75 0.14 260);
  --brand-400: oklch(0.65 0.17 260);
  --brand-500: oklch(0.55 0.20 260);
  --brand-600: oklch(0.45 0.18 260);
  --brand-700: oklch(0.37 0.15 260);
  --brand-800: oklch(0.28 0.12 260);
  --brand-900: oklch(0.22 0.10 260);
  --brand-950: oklch(0.15 0.08 260);
}

Every step looks equally different from its neighbor. Try that in HSL and you'll get inconsistent visual gaps.

Better Gradients

HSL gradients often pass through muddy middle tones because hue interpolation isn't perceptually uniform. OKLCH gradients stay vivid:

.gradient-hsl {
  background: linear-gradient(to right, hsl(0, 80%, 55%), hsl(240, 80%, 55%));
  /* Passes through dull gray in the middle */
}

.gradient-oklch {
  background: linear-gradient(to right, oklch(0.6 0.2 25), oklch(0.5 0.2 280));
  /* Stays vivid and perceptually even throughout */
}

Accessible Color Manipulation

Need a lighter version with the same perceived hue? In HSL, you'd adjust lightness and get unexpected hue shifts. In OKLCH, bump the L value and the color stays consistent:

/* HSL: making lighter shifts the apparent hue */
.button       { background: hsl(230, 80%, 45%); }  /* blue-ish */
.button-hover { background: hsl(230, 80%, 55%); }  /* slightly purple shift */

/* OKLCH: making lighter preserves the hue */
.button       { background: oklch(0.45 0.18 260); }
.button-hover { background: oklch(0.55 0.18 260); }  /* same hue, brighter */

Browser Support

BrowserVersionCSS Support
Chrome111+oklch() in color values
Firefox113+Full support
Safari15.4+Full support
Edge111+Full support

For older browsers, provide hex/hsl fallbacks:

:root {
  --brand-500: #3366ff;              /* Fallback */
  --brand-500: oklch(0.55 0.2 260); /* Modern override */
}

Browsers that don't understand oklch() will use the first declaration; those that do will use the second.

Converting Between Formats

FromTo OKLCHMethod
HexOKLCHUse a color converter tool or library (culori, colorjs.io)
RGBOKLCHSame — the math requires intermediate LAB conversion
HSLOKLCHDirect conversion not possible; go HSL → RGB → OKLCH

For build-time conversion, culori works well:

import { converter } from 'culori'

const toOklch = converter('oklch')
toOklch('#3366ff') // { mode: 'oklch', l: 0.55, c: 0.20, h: 260 }

Try It Yourself

Convert any color to OKLCH and see the perceptually uniform difference. Our color picker supports hex, RGB, HSL, and OKLCH with live preview.

👉 Free Color Picker