OKLCH: Why CSS Color Is About to Change Forever
For decades, web developers have used HSL to describe colors in a "human-friendly" way. But HSL has a dirty secret: its lightness axis is a lie. Two colors with the same HSL lightness can look completely different. OKLCH fixes this, and it is already in your browser.
The Problem with HSL
HSL (Hue, Saturation, Lightness) was introduced as a more intuitive alternative to RGB. Instead of thinking in terms of red, green, and blue channel values, you think in terms of what color (hue), how vivid (saturation), and how light or dark (lightness). This sounds perfect — until you actually try to use it for consistent design.
Try this: create two colors with the same saturation and lightness but different hues.
hsl(60, 100%, 50%)Yellowhsl(240, 100%, 50%)BlueBoth have lightness: 50%, but the yellow looks dramatically lighter than the blue. That is because HSL's lightness is a mathematical abstraction, not a perceptual one. It is calculated from the max and min RGB channel values, which bears no relation to how bright a color appears to human eyes.
This creates real problems. If you try to build a consistent color system in HSL — say, a set of buttons where every color feels equally prominent — you will find that yellows look washed out while blues look dark. You end up hand-tuning every color, defeating the purpose of having a systematic approach.
Enter OKLAB: Perceptual Uniformity for the Web
In December 2020, Björn Ottosson, a Swedish color scientist and software engineer, published a paper introducing OKLAB — a new perceptually uniform color space designed specifically for the needs of computer graphics and web design. Ottosson designed OKLAB to improve upon CIE L*a*b* (which has known issues with hue linearity, especially in the blue region) while remaining computationally lightweight enough for real-time applications.
OKLAB uses the same conceptual axes as L*a*b*:
L = Perceived lightness (0 = black, 1 = white)
a = Green–Red axis (negative = green, positive = red)
b = Blue–Yellow axis (negative = blue, positive = yellow)
But the transformation matrices were optimized using modern perceptual data (CAM16 color appearance model and the Ebner-Fairchild dataset) to achieve much better hue linearity. In OKLAB, rotating the hue while keeping lightness and chroma constant produces colors that genuinely look equally light and equally vivid — something CIE L*a*b* does not fully achieve.
OKLCH: The Cylindrical Form
OKLCH is the cylindrical (polar) form of OKLAB, just as HSL is the cylindrical form of RGB. It converts the rectangular a/b coordinates to polar Chroma/Hue coordinates:
L = Lightness (0 to 1, or 0% to 100%)
C = Chroma (0 to ~0.4, how vivid/saturated)
H = Hue angle (0 to 360 degrees)
Conversion from OKLAB:
C = √(a² + b²)
H = atan2(b, a) × 180 / π
OKLCH is designed to be the replacement for HSL in CSS. The key advantage: when you change the hue in OKLCH while keeping L and C constant, the colors actually look equally light and equally vivid. This is revolutionary for building design systems.
CSS Syntax: Using OKLCH Today
OKLCH is part of the CSS Color Level 4 specification (W3C Candidate Recommendation). It is supported in all major browsers: Chrome 111+, Firefox 113+, Safari 15.4+, and Edge 111+. The syntax:
/* oklch(lightness chroma hue) */
color: oklch(70% 0.15 250); /* A vivid blue */
color: oklch(70% 0.15 150); /* A vivid green - same perceived brightness */
color: oklch(70% 0.15 60); /* A vivid yellow - same perceived brightness */
/* With alpha transparency */
color: oklch(70% 0.15 250 / 0.5); /* 50% transparent */
/* Using relative color syntax */
color: oklch(from var(--brand) calc(l - 0.1) c h); /* Darker variant */
The relative color syntax (the from keyword) is particularly powerful. It lets you derive new colors from existing ones by modifying individual channels. Want a darker version of your brand color? Subtract from L. Want a muted version? Reduce C. The hue stays consistent because OKLCH is perceptually uniform.
HSL vs. OKLCH: A Direct Comparison
Here is what happens when you set "50% lightness" in HSL versus OKLCH across different hues:
HSL(0, 100%, 50%) = #FF0000 (Red) Perceived luminance: 0.213
HSL(60, 100%, 50%) = #FFFF00 (Yellow) Perceived luminance: 0.927
HSL(120, 100%, 50%) = #00FF00 (Green) Perceived luminance: 0.715
HSL(240, 100%, 50%) = #0000FF (Blue) Perceived luminance: 0.072
Same "lightness" in HSL, but perceived luminance ranges from 0.072 to 0.927!
That is a 13x difference.
In OKLCH, oklch(65% 0.25 H) produces colors with genuinely
similar perceived lightness regardless of hue angle H.
This 13x difference in HSL is not a minor inconvenience — it is a fundamental flaw that makes HSL unreliable for systematic color work. OKLCH eliminates this problem.
Practical Benefits for Designers
1. Consistent Color Palettes
Need 8 status colors that all feel equally prominent? In OKLCH, set the same L and C values and rotate the hue. Done. In HSL, you would need to manually adjust each color's lightness and saturation to compensate for perceptual differences.
2. Better Gradients
Gradients in OKLCH avoid the "muddy middle" problem. When you interpolate between blue and yellow in sRGB, the midpoint is a dull gray-green. In OKLCH, the midpoint is a vibrant, properly saturated intermediate color. CSS now supports color interpolation in OKLCH:
background: linear-gradient(in oklch, oklch(60% 0.2 250), oklch(85% 0.2 90));
3. Accessible Contrast
Because OKLCH's L channel corresponds to real perceived lightness, you can predict WCAG contrast ratios more accurately. Two colors with L values far apart will have good contrast; two with similar L values will not, regardless of their hue or chroma.
4. Design Token Systems
OKLCH is ideal for design tokens in systems like Tailwind CSS, Radix Colors, and Open Props. You can define a semantic scale (e.g., blue-50 through blue-900) by varying L in even steps, and the resulting colors will have genuinely even visual spacing.
The Gamut Problem
One complexity of OKLCH: not all L/C/H combinations correspond to displayable sRGB colors. Some OKLCH values are "out of gamut" — they describe colors that are more vivid than your monitor can produce. When this happens, the browser clips the color to the nearest displayable value.
This is actually a feature, not a bug. As display technology improves (P3 gamut is standard on modern Macs and iPhones, Rec. 2020 is coming), OKLCH will automatically take advantage of wider gamuts. CSS Color Level 4 also introduces color(display-p3 ...)for targeting specific device gamuts.
Migration Path: HSL to OKLCH
You do not need to rewrite your entire codebase. A practical migration approach:
- Start using OKLCH for new color definitions and design tokens
- Use
@supports (color: oklch(0% 0 0))for progressive enhancement - Convert critical palettes (brand colors, accessibility-sensitive colors) first
- Use tools like our Color Converter to translate between formats
- Keep HSL/hex fallbacks for older browsers where needed
Conclusion
OKLCH represents the biggest improvement in how web developers work with color since HSL was introduced. By grounding its lightness and chroma axes in human perception rather than mathematical convenience, it makes it possible to build truly consistent, accessible, and beautiful color systems without manual per-color tweaking.
The future of CSS color is perceptually uniform. Start exploring OKLCH values with our Color Wheel, build gradients that stay vibrant across the full spectrum, and browse our palette collection with a new appreciation for the perceptual science behind every color.
Sources & References
This article is based on research from leading color authorities:
- CSS Color Module - Official CSS color specification
- OKLCH Explanation - Perceptually uniform color space
- Web.dev Color Guide - Modern web color best practices