What is OKLCH? The perceptual CSS color format, explained

OKLCH is a CSS color format built on perceived lightness, chroma and hue. Learn how its three numbers work and why they make palettes easier to control.

OKLCH is a way to write colors in CSS using three perceptual values: lightness (L), chroma (C) and hue (H). It is the polar form of OKLab, a color space Björn Ottosson published in 2020, and it is designed so that equal changes in its numbers look like equal changes to the eye. In CSS you write it as oklch(62.8% 0.2577 29.23), which is pure red.

Where OKLCH comes from

Designers have long wanted a perceptually uniform color space, one where the distance between two sets of numbers matches how different the colors look. CIELAB, standardized in 1976, was the classic attempt, and its polar form is available in CSS as lch(). But CIELAB has known flaws, and the most visible one for UI work is that blues drift toward purple as you change their lightness or chroma. Ottosson built OKLab from a simple chain, a matrix, a cube root and a second matrix, fitted to perceptual data. The goal was better hue uniformity with math cheap enough for real-time graphics.

OKLab describes a color with a lightness value plus two opponent axes: a, from green to red, and b, from blue to yellow. OKLCH converts those two axes to polar coordinates. The distance from the neutral gray axis becomes chroma, and the angle around it becomes hue. Both oklab() and oklch() are defined in CSS Color Module Level 4.

The L, C and H components

  • L, lightness: 0 to 1, or 0% to 100%. 0 is black and 1 is white. Unlike HSL, where 50% means "pure hue", OKLCH lightness tracks how light a color actually looks, whatever its hue.
  • C, chroma: how far the color is from gray, starting at 0. There is no fixed maximum; in practice values run to about 0.37, and colors inside sRGB top out near 0.32. Pure blue #0000FF sits near that ceiling, at about 0.313.
  • H, hue: an angle from 0 to 360 degrees. Pure sRGB red sits near 29°, yellow near 110°, green near 142° and blue near 264°. These angles differ from HSL's 0°, 60°, 120° and 240°, so never copy hue numbers from one model to the other.

Read the red example with that in mind. In oklch(62.8% 0.2577 29.23), 62.8% is the lightness of #FF0000, 0.2577 its chroma and 29.23° its hue. For comparison, the indigo #6366F1 is about oklch(58.5% 0.204 277.1): slightly darker than red, less chromatic, and on the blue-violet side of the wheel.

CSS syntax and browser support

The function takes space-separated values, with an optional alpha after a slash:

  • oklch(62.8% 0.2577 29.23): lightness as a percentage, chroma as a number, hue in degrees.
  • oklch(0.628 0.2577 29.23deg): lightness as a number from 0 to 1; the unit on hue is optional.
  • oklch(62.8% 0.2577 29.23 / 50%): the same red at half opacity.

Every major browser supports oklch(): Chrome and Edge since version 111, Safari since 15.4 and Firefox since 113, all shipped by 2023. If you still support older browsers, declare a HEX fallback first and the OKLCH value second. A browser that does not understand the second declaration keeps the first.

Gamut: colors beyond sRGB

OKLCH can describe any visible color, not just the sRGB range that HEX and rgb() cover. Many phones and laptops now have Display P3 screens, which show more saturated reds, greens and oranges than sRGB allows. P3's pure red, color(display-p3 1 0 0), is about oklch(64.9% 0.30 29), beyond sRGB red's chroma of 0.258. On a P3 display the browser shows it as written; on an sRGB display it gamut-maps the color to one the screen can show.

So you can write one value that looks richer on capable screens and still degrades safely elsewhere. The catch is that the browser, not you, chooses the fallback. For brand-critical colors, pick an in-gamut value, or serve the wider value only inside @media (color-gamut: p3). Converters that output HEX, including the one on this site, have to clamp out-of-gamut OKLCH into sRGB.

The sRGB boundary is also lopsided, which matters when you pick chroma values. Pure blue #0000FF reaches a chroma of about 0.313 at a lightness of just 45.2%, while pure yellow #FFFF00 has a chroma of only 0.211 at 96.8%. The most colorful yellows are very light and the most colorful blues are fairly dark, so a single chroma value cannot work for every hue at every lightness.

Building palettes and tokens with OKLCH

OKLCH's main practical benefit is that lightness is consistent across hues. Four colors at oklch(70% 0.1 H), with hues of 30, 90, 150 and 250, all measure between 2.56:1 and 2.79:1 against white. The same experiment in HSL, with lightness fixed at 50%, ranges from 1.07:1 for yellow to 8.59:1 for blue. So if your blue-600 token passes contrast, a green-600 or red-600 at the same L will be close, and you only need to spot-check it in the contrast checker.

A typical recipe for a token scale:

  1. Take the hue from your brand color and hold it constant across the scale.
  2. Give each step a lightness target, for example about 97% at step 50 down to below 30% at step 950.
  3. Taper chroma at both ends, because very light and very dark colors cannot hold much chroma inside sRGB.
  4. Check that every step is in gamut, then test the text and background pairs you plan to use.

Tailwind CSS v4 defines its default palette in OKLCH and exposes it through an @theme block, with variables such as --color-blue-500 that generate utilities like bg-blue-500. You can browse every value on the Tailwind colors page.

Relative color syntax

CSS relative color syntax lets you derive new colors from a token instead of hard-coding each one. oklch(from var(--brand) calc(l - 0.1) c h) takes the brand color, lowers its lightness by 0.1 and keeps its chroma and hue, a clean way to make a hover state. It reached browsers later than oklch() itself, so confirm support for your audience before relying on it.

Try it: convert a color to OKLCH

Paste any color into the HEX to OKLCH converter to see its L, C and H next to HEX, RGB and HSL. The color picker has an editable OKLCH field that stays in sync with the other formats, and the Tailwind palette generator turns one base color into an 11-step OKLCH scale with @theme export. For how OKLCH compares with the older models, read RGB vs HSL vs OKLCH.

Frequently asked questions

What does OKLCH stand for?

OKLCH is the lightness, chroma and hue (LCH) form of OKLab, the perceptual color space Björn Ottosson published in 2020.

Is OKLCH supported in all browsers?

Yes, in all current major browsers: Chrome and Edge 111 and later, Safari 15.4 and later, and Firefox 113 and later. Add a HEX fallback only if you support older versions.

What is the maximum chroma in OKLCH?

There is no hard limit, but visible colors reach about 0.37 and colors inside sRGB top out near 0.32. Values beyond what a screen can show are gamut-mapped by the browser.

Is OKLCH the same as LCH?

No. CSS lch() is based on CIELAB, while oklch() is based on OKLab. They share the lightness, chroma and hue structure but use different math, and OKLCH keeps hue more stable, especially for blues.

Try it with these tools

More color guides