LazyTools

🔒 Every tool runs in your browser, the files and values you enter are never uploaded to any server. How it works

explainer

OKLCH: The Modern CSS Color Format (and How to Convert from HEX)

By Uttam Regmi · Published 2026-07-11 · Updated 2026-07-11 · 7 min read · Fact-checked, sources cited

OKLCH, the perceptually uniform CSS color format with lightness, chroma and hue

OKLCH is a way of writing colors, oklch(L% C H), that matches how your eyes actually work. L is lightness, C is chroma (colorfulness), H is hue. It is a CSS Color 4 function supported in about 95% of browsers and is Tailwind CSS 4’s default color space. The headline benefit: equal number changes look like equal visual changes, which HEX and HSL never guaranteed. #1d87f1 is oklch(62.3% 0.183 253.6).

The three parts

Infographic: OKLCH has three parts, L lightness 0 to 100 percent, C chroma 0 to about 0.37 from grey to vivid, and H hue 0 to 360 degrees. oklch(62.3% 0.183 253.6) equals #1d87f1. Unlike HSL, OKLCH lightness is perceptually even, so ramps are predictable. It is CSS Color 4, supported in about 93 to 95 percent of browsers and Tailwind 4's default, with a HEX fallback for out-of-gamut colors.
Lightness, chroma, hue, and a HEX fallback for colors too vivid for sRGB.
  • L, Lightness (0%100%): how light the color is, from black to white. Crucially, this is perceptual lightness, so 50% looks like a mid-grey regardless of hue.
  • C, Chroma: colorfulness, from 0 (grey) upward (about 0.37 at the most vivid). There is no fixed maximum, how much chroma is achievable depends on the lightness, the hue, and the display.
  • H, Hue (0360): the angle on the color wheel, roughly 0 red, 150 green, 250 blue, 300 violet.

Paste any color into the OKLCH color picker to see all three, alongside OKLAB, LAB, LCH, HWB and the HEX/RGB forms.

Reading a value at a glance

Take oklch(62.3% 0.183 253.6). The 62.3% tells you it is a touch lighter than mid-grey. The 0.183 chroma says it is moderately vivid, not neon, not muted. The 253.6 hue lands in the blue arc. Change just the first number to 40% and you get the same blue, only darker; change only the hue to 30 and you get an orange of the same lightness and vividness. That independence is the whole point: each channel moves one visual property and leaves the others alone, which HEX and RGB never let you do.

Why not just use HSL?

HSL has an L too, so why switch? Because HSL’s lightness is a lie. A hsl(60, 100%, 50%) yellow is blindingly bright, while hsl(240, 100%, 50%) blue is dark, same “50%”, wildly different perceived lightness. Build a UI’s shade scale in HSL and the steps look uneven; rotate the hue and some colors jump in brightness.

OKLCH is built on the OKLab color space, published by Björn Ottosson in 2020 and designed so that equal numeric distances match equal perceived distances. Hold L and C constant, sweep the hue, and every color stays the same lightness. That is exactly what you want for design tokens: a 500 shade that reads as the same weight across your entire palette.

Here is how the three common ways of writing a color compare on the things that matter when you are building a UI:

PropertyHEX / RGBHSLOKLCH
Perceptually uniform lightnessNoNoYes
Change one visual property in isolationNoPartlyYes
Predictable shade ramps (50-950)NoUnevenEven
Hue rotation keeps brightnessNoNoYes
Human-readableNot reallySomewhatYes
Can describe wide-gamut (P3) colorsNoNoYes
Universal fallback / legacy supportYesYesNeeds a fallback

The takeaway: HEX is the safe lowest common denominator, HSL feels intuitive but its lightness lies, and OKLCH is the one that behaves the way a designer expects.

How to convert HEX to OKLCH

The conversion is a fixed sequence of matrix operations, deterministic and exact:

  1. Decode the HEX to sRGB channels (0-255).
  2. Linearise each channel (undo the sRGB gamma curve).
  3. Apply the OKLab matrices (Björn Ottosson’s published constants): linear-sRGB → LMS → OKLab.
  4. Convert OKLab’s rectangular a/b to polar chroma and hue: C = √(a² + b²), H = atan2(b, a).

The result for #1d87f1 is oklch(62.3% 0.183 253.6). The HEX to OKLCH converter runs exactly this and shows OKLAB and LAB too, the same math the browser uses, so what you copy is what renders. (A chatbot, by contrast, will confidently produce a plausible OKLCH that is subtly wrong, because it can’t run the matrices.)

The one gotcha: gamut

OKLCH is more expressive than an sRGB screen. You can write an oklch() value whose chroma is higher than any #rrggbb can show. It is out of gamut. Converting back to HEX then has no exact answer, so a good tool reduces chroma (holding lightness and hue) until the color fits, and tells you it did. This is why you still ship a HEX fallback: for the ~5% of browsers without oklch(), and for design or email tools that only speak HEX. The OKLCH to HEX converter flags out-of-gamut input and always returns a displayable fallback.

.button {
  background: #1d87f1;                 /* fallback first */
  background: oklch(62.3% 0.183 253.6); /* modern browsers override */
}

The two-declaration pattern works because CSS ignores any property value it does not understand: a browser without oklch() keeps the HEX, a browser with it takes the second line. No @supports query needed.

Browser support in one line

The oklch() function shipped across the major engines a while ago: Chrome and Edge from version 111, Safari from 15.4, and Firefox from 113. In practice that covers the large majority of users, commonly cited figures put it in the low-to-mid 90s percent range, and the share keeps climbing as older browsers age out. The remaining slice is exactly what the HEX fallback above is for, so you lose nothing by adopting OKLCH today.

Worked example: a shade ramp

The perceptual evenness pays off most when you build a 50950 scale. In OKLCH you keep the hue fixed, keep chroma roughly constant (dialing it down slightly at the extremes so pale and dark ends do not clip), and step the lightness in even amounts. Because lightness is perceptual, even steps look even:

:root {
  --blue-100: oklch(92% 0.05 253);
  --blue-300: oklch(78% 0.12 253);
  --blue-500: oklch(62% 0.18 253); /* the base */
  --blue-700: oklch(46% 0.15 253);
  --blue-900: oklch(30% 0.09 253);
}

Every swatch shares hue 253, so the family reads as one color; only the lightness marches down in regular increments. Try the same thing by hand in HEX and you will spend an afternoon nudging channels and still end up with a 700 that looks muddy next to its neighbours.

Where it pays off

Beyond ramps, the even lightness makes hue rotation reliable. Rotate the hue for a set of color harmonies, complementary, triadic, analogous, and every result stays the same visual weight, so no single accent shouts louder than the rest. This is the reason Tailwind CSS 4 moved its default palette to OKLCH, and why design-system teams increasingly define tokens in it and export HEX only for the legacy layer.

A few practical habits keep OKLCH work smooth:

  • Anchor on lightness first. Decide how light each step should be, then add chroma and hue. Lightness is the channel your eye judges hardest.
  • Ease chroma at the ends. Very light and very dark colors cannot hold much chroma before they clip, so taper it toward 50 and 950.
  • Keep the HEX fallback generated, not hand-typed. Let the converter produce it so the two values always match.

Quick summary

OKLCH writes a color as lightness, chroma and hue in a perceptually uniform space, so equal steps look equal, fixing HSL’s uneven lightness. It is a CSS Color 4 function with ~95% browser support and is Tailwind 4’s default. Convert HEX to OKLCH by linearising sRGB and applying the OKLab matrices (e.g. #1d87f1oklch(62.3% 0.183 253.6)), and keep a HEX fallback for out-of-gamut colors and older browsers. Try it on the OKLCH color picker.

Sources: CSS Color Module Level 4 (W3C); Björn Ottosson, “A perceptual color space for image processing” (OKLab); Tailwind CSS 4 color documentation; MDN oklch(). Educational information.

Frequently asked questions

What is OKLCH?

OKLCH is a perceptually uniform color format written oklch(L% C H): L is lightness (0-100%), C is chroma (colorfulness, 0 to about 0.37), and H is hue in degrees (0-360). Equal numeric steps look like equal visual steps, which HSL and HEX do not guarantee. It is a CSS Color 4 function supported in roughly 95% of browsers.

How do I convert HEX to OKLCH?

Decode the HEX to sRGB, linearise it, and apply the OKLab matrices, then express the result in polar form. For example #1d87f1 becomes oklch(62.3% 0.183 253.6). A converter does this exactly, the same math the browser uses for the oklch() function.

Is OKLCH better than HSL?

For perceptual work, yes. In HSL, a 50%-lightness yellow looks far brighter than a 50%-lightness blue, so lightness ramps are uneven. OKLCH fixes this: lightness is perceptually uniform, so shade scales and hue rotations behave predictably. That is why design systems and Tailwind CSS 4 adopted it.

Do all browsers support oklch()?

The oklch() CSS function works in all current browsers, Chrome/Edge 111+, Safari 15.4+ and Firefox 113+, which is about 93-95% of users. For the small remainder and for tools that expect HEX, provide a HEX fallback alongside the oklch() value.

Why is my OKLCH color 'out of gamut'?

OKLCH can describe colors more saturated than an sRGB screen can display. When that happens, the exact HEX does not exist, so a good converter reduces chroma (keeping lightness and hue) until the color fits and flags it. Wide-gamut (P3) displays can show more of these colors.

What is chroma in OKLCH?

Chroma (C) is colorfulness, how far the color is from grey. 0 is a neutral grey; higher values are more vivid. Unlike HSL saturation, OKLCH chroma is not capped at a fixed maximum, because the achievable chroma depends on the lightness and hue and on the display's gamut.