Articles /
OKLCH and modern color
Perceptually uniform colors and light-dark() for design systems that just work.
What's wrong with hex and RGB?
Hex and RGB work in the sRGB color space, which is not perceptually uniform. When you lighten a color by 20 in lightness, the perceived change is different depending on the hue. Blues look much brighter after a small lightness bump; yellows barely change. This makes building consistent palettes (e.g. 9-step scales) hard by eye.
OKLCH is a perceptually uniform color space. Equal steps in L (lightness) look equal to the human eye, regardless of hue. That makes it far better for building design tokens, color scales, and accessible color pairs.
The OKLCH syntax
/* oklch(Lightness Chroma Hue / Alpha) */
oklch(0.5 0.2 260) /* mid-light blue */
oklch(0.75 0.15 145) /* mid-light green */
oklch(0.9 0.08 75) /* light amber */
oklch(0.5 0.2 260 / 0.5) /* 50% opacity */
oklch(1 0 0 / 0.15) /* white at 15% — for glass overlays */
/* Ranges:
L: 0 (black) → 1 (white)
C: 0 (gray) → ~0.4 (most saturated, varies by hue)
H: 0–360 (hue angle, same as HSL) */
Hue 0/360 is red, 120 is green, 240 is blue — familiar from HSL. Chroma is roughly "how colorful" — 0 is gray, 0.2–0.3 is vivid, and the maximum varies by hue (e.g. yellows max out around 0.3, some blues go to 0.4).
A scale built at the same L and C, varying only hue:
Building a design token scale
:root {
/* Primary palette — same L steps, same hue (260 = blue) */
--blue-100: oklch(0.96 0.04 265);
--blue-200: oklch(0.88 0.08 265);
--blue-300: oklch(0.76 0.13 265);
--blue-400: oklch(0.63 0.18 265);
--blue-500: oklch(0.52 0.22 265); /* base accent */
--blue-600: oklch(0.43 0.22 265);
--blue-700: oklch(0.35 0.2 265);
--blue-800: oklch(0.26 0.16 265);
--blue-900: oklch(0.18 0.1 265);
/* Semantic tokens */
--color-primary: var(--blue-500);
--color-primary-soft: var(--blue-100);
--color-primary-text: var(--blue-800);
}
The equal L steps (0.96, 0.88, 0.76, 0.63, 0.52...) produce visually even steps in the scale — much harder to achieve in hex.
light-dark() — one line dark mode
light-dark(light-value, dark-value) is a function that returns the first value in light mode and the second in dark mode — based on color-scheme.
:root { color-scheme: light dark; }
.button {
/* One line instead of two @media blocks */
background: light-dark(oklch(0.52 0.22 265), oklch(0.68 0.19 265));
color: light-dark(white, oklch(0.14 0.02 260));
}
/* Works with any CSS color value — not just oklch */
.card {
border: 1px solid light-dark(oklch(0 0 0 / 0.08), oklch(1 0 0 / 0.1));
}
Browser support: Chrome 123+, Safari 17.5+, Firefox 120+. Widely usable in 2026. Add a @media (prefers-color-scheme: dark) fallback for older browsers if needed.
Practical tools
- oklch.com — interactive OKLCH color picker with conversion from hex.
- evilmartians.com/oklch — deep background on why OKLCH.
- Figma Variables — supports oklch values in 2026; define your scale once and reference it.