OKLCH vs HSL vs HEX: A practical guide to picking a color format

By LJK CreativeUpdated 10 min read
Stylized illustration of three geometric forms — a hexagon, a tilted color wheel, and a smooth gradient lens — representing HEX, HSL, and OKLCH

TL;DR

Use HEX as the storage and handoff format — every tool reads it. Use HSLonly for nudging a single color by hand; it's perceptually broken and produces inconsistent scales across hues. Use OKLCH for everything systemic: building scales, interpolating, generating dark-mode variants, tuning contrast. OKLCH is now natively supported in every major browser and has become the default working color space for modern design systems (Tailwind v4, Radix Colors, and most contemporary color tooling).

1. The short answer

You probably want all three, used for different things:

FormatUse forAvoid for
HEXStorage, handoff, tool interchangeDoing color math by hand
HSLTweaking a single color in codeBuilding scales, predicting contrast
OKLCHBuilding scales, gradients, dark mode, contrast tuningOlder-browser-only stylesheets

2. HEX: the universal default

Hexadecimal color notation (#1c749f) is a compact encoding of three 8-bit channels (red, green, blue) in the sRGB color space. It's the lowest common denominator of web color — supported since CSS1 (1996), universal across every design tool, and trivially parseable.

What HEX is good at: being a transport format. If you need to write a color down, paste it in Slack, send it to a contractor, or store it in a database, HEX is the right answer. Every conversion target speaks HEX.

What HEX is bad at:doing anything operational with the color. The channels don't correspond to anything meaningful — you can't tell if #a1b2c3 is light or dark, what hue it is, or how it compares to #b3c4d5 without converting first. HEX is a destination, not a workshop.

HEX also doesn't natively encode opacity or wide-gamut color. The 8-digit variant #1c749fffadds alpha, but most tools don't round-trip it cleanly, and colors outside sRGB can't be expressed at all.

3. HSL: intuitive, but mathematically broken

HSL — hue, saturation, lightness — was standardized as a CSS color function in CSS Color Module Level 3 (W3C Recommendation, June 2011) and quickly became the “readable” alternative to HEX. The appeal is obvious: hsl(212, 70%, 37%) tells you the color is a saturated blue at a moderate lightness. A designer can read that.

The problem is that HSL is a simple geometric transform of sRGB — it inherits sRGB's lack of perceptual uniformity. In HSL:

  • Lightness isn't perceived lightness. HSL yellow at L=50% looks near-white. HSL blue at L=50% looks mid-tone. The same lightness number doesn't produce equally light colors.
  • Saturation isn't perceived saturation. HSL red at S=100% feels much more saturated than HSL teal at S=100%, even though the number is the same.
  • Interpolation goes through ugly mid-tones. Interpolating between two HSL colors often produces a grey, brown, or muddy crossover in the middle because the math passes through the gamut center.

HSL is fine for one-off operations on a single color — nudging saturation up, shifting hue by 20°, lightening a specific shade. It's a poor foundation for systematic color work, which is why every modern design-system tool has moved off it.

4. OKLCH: perceptually uniform

OKLab, created by Björn Ottosson in 2020, is a modern perceptual color space designed to fix the problems with older perceptual spaces like CIELAB while staying simple to compute. OKLCH is its polar form: lightness, chroma (saturation-like), and hue angle.

A typical OKLCH color looks like:

oklch(45% 0.12 245)

— read as “45% lightness, 0.12 chroma, 245° hue”. Chroma in OKLCH runs from 0 (grey) up to roughly 0.4 for the most saturated sRGB colors; the practical upper bound depends on hue and lightness because the sRGB gamut has weird shapes in perceptual space.

Why OKLCH matters for design systems:

  • Same L = same perceived lightness across hues. Yellow at L=60% and blue at L=60% look equally bright. That makes it possible to build scales where step 600 (say) has a consistent perceived darkness across your entire palette.
  • Interpolation looks good. Gradients and mid-step generation pass through colors that look intermediate, not muddy.
  • Hue stays constant when you change lightness. Adjusting L without touching H doesn't drift the color toward a different hue — a common failure mode in HSL and raw sRGB.
  • Gamut is explicit. Colors outside sRGB just have chroma values higher than sRGB can render. You can detect and gamut-map predictably, which matters as P3 displays become standard.

Browser support: The CSS oklch() function is part of CSS Color Module Level 4. It shipped in Safari 15.4 (March 2022), Chrome 111 (March 2023), and Firefox 113 (May 2023). For modern-browser-only sites, it's safe to use natively. For broader support, generate OKLCH at design time and serialize to HEX or sRGB for the stylesheet.

Tooling has caught up. Tailwind CSS v4 (early 2025) replaced its entire default palette with OKLCH values. Radix Colors, Open Props, and most contemporary color tools now work in OKLCH or OKLab natively.

5. OKLab, LAB, and other formats

A few formats you'll encounter that live in the same neighborhood:

  • OKLab — the Cartesian form of the same color space as OKLCH. Components are L (lightness), a (green↔red axis), b (blue↔yellow axis). Better for linear math (averaging two colors, computing distances); OKLCH is better for design controls.
  • CIELAB / lab() — the older perceptual color space from the CIE. It mostly works but has known problems with blue and purple. OKLab was designed to fix these specifically.
  • LCH / lch() — the polar form of CIELAB, same relationship as OKLCH is to OKLab. Same caveats apply.
  • color(display-p3 ...) — not a color space per se, but a way to encode colors in the wider P3 display gamut. Useful for vibrant brand colors that go beyond sRGB, especially on Apple hardware.
  • HSV / HSB — close cousin of HSL with the same perceptual problems. Used in some design tools (notably Photoshop). Treat like HSL.

6. When to use which

A practical decision tree:

  • Writing a color down in design specs, tickets, or docs? HEX. It's the format every reader can paste anywhere.
  • Designing a single state and want to tweak it by hand? HSL is fine — nudging hue and lightness sliders on one color is exactly its strength.
  • Generating a scale, ramp, or sequence of related colors? OKLCH. The reason your hand-tuned scales feel inconsistent across hues is HSL — switching to OKLCH usually fixes it.
  • Animating or interpolating between two colors? OKLCH (or OKLab). The midpoint will look intermediate, not muddy.
  • Generating dark-mode variants? OKLCH. Inverting lightness while holding hue and chroma gives you a coherent dark theme; doing the same in HSL or sRGB drifts hues unpredictably.
  • Tuning for accessible contrast? Pair OKLCH with APCA (see our APCA vs WCAG guide). Adjusting OKLCH lightness alone usually lands you in the right contrast range across hues.

7. How Palette Daddy handles formats

Palette Daddy accepts HEX, OKLCH, or HSL as input. Internally, the generator works in a perceptual color space so that lightness, chroma, and hue can be manipulated independently when producing the 11-step scale (see our 11-step color scale guide for why that matters).

Outputs are shown in your preferred format (HEX by default; toggle to OKLCH or HSL in the input panel). The Token Studio JSON export serializes to HEX for maximum compatibility with downstream tools — pick OKLCH on the input side, get clean HEX out the other end.

8. Frequently asked questions

Can I just use HEX everywhere and ignore the rest?
For static colors, yes — HEX is universally supported, every design tool reads and writes it, and converting to anything else is a one-line operation. The reason to learn the other formats isn't to replace HEX but to do operations on color: building scales, interpolating gradients, generating dark mode variants. HEX is a great storage and handoff format and a bad math format.
What's wrong with HSL?
HSL's lightness axis isn't perceptually uniform. A yellow at HSL lightness 50% and a blue at HSL lightness 50% look wildly different in actual perceived brightness — the yellow reads near-white, the blue reads mid-dark. That makes HSL useless for generating consistent scales across hues, even though the format reads beautifully. HSL is fine for nudging a single color (tweaking saturation, shifting hue); it's the wrong tool for systematic operations.
Is OKLCH supported by browsers?
Yes, in every current major browser. The oklch() CSS function shipped in Safari 15.4 (March 2022), Chrome 111 (March 2023), and Firefox 113 (May 2023). It's part of CSS Color Module Level 4. If you're supporting only modern browsers (last two major versions of each), you can use OKLCH directly in stylesheets. For older support, generate OKLCH in your design tools and serialize to HEX at build time.
What's the difference between OKLCH and OKLab?
Same color space, different coordinates. OKLab uses Cartesian coordinates (L for lightness, a for green↔red, b for blue↔yellow). OKLCH uses cylindrical coordinates over the same Lab space (L for lightness, C for chroma, H for hue angle). OKLCH is easier to reason about as a designer — you have a hue dial — but they describe the same colors. Pick OKLCH for design tasks, OKLab when you need linear arithmetic across colors.
What about P3 and wide-gamut colors?
P3 (display-p3) is a wider color space than sRGB, available on Apple displays since 2015 and increasingly on PC monitors. It can show more saturated reds and greens than sRGB. OKLCH can describe P3 colors directly — values outside the sRGB gamut just have higher chroma. You can use color(display-p3 ...) for P3 colors with sRGB fallbacks via @supports or color-mix(). For most product UIs, sRGB-gamut OKLCH is plenty.

9. Further reading

Try OKLCH input in Palette Daddy

Paste an OKLCH value, generate an 11-step scale tuned in perceptual space, and export to Figma Token Studio in one click.

Open the generator