RGB vs HSL vs OKLCH: which CSS color model to use and when

RGB describes light, HSL makes it editable, OKLCH makes it perceptually even. Here is what each model is good at and when to reach for it in CSS.

RGB, HSL and OKLCH are three ways to write a color in CSS. RGB lists how much red, green and blue light the screen emits, HSL rearranges those values into hue, saturation and lightness, and OKLCH uses lightness, chroma and hue scaled to match human vision. Use RGB or HEX to exchange exact values, HSL for quick tweaks, and OKLCH for palettes and themes where equal steps should look equal.

RGB: the screen's native model

RGB is additive: each pixel mixes red, green and blue light, with each channel running from 0 to 255 in 8-bit sRGB. rgb(30 144 255) and its hex form #1E90FF tell the browser exactly what to send to the display. That makes RGB the lossless format for design handoff, tokens and anything that must match byte for byte. For how the hex form encodes those numbers, see hex color codes explained.

The weakness is editing. To make #1E90FF a little darker, you have to lower all three channels in proportion; to make it less saturated, you have to move them toward each other. Neither intent maps to a single number, which is why almost nobody tunes colors by typing RGB values.

HSL: RGB rearranged for people

HSL, introduced in CSS Color Level 3, reshapes the RGB cube into a cylinder. Hue is an angle on a 360° wheel (0° red, 120° green, 240° blue), saturation runs from gray at 0% to full color at 100%, and lightness runs from black at 0% through the pure hue at 50% to white at 100%. Dodgerblue becomes hsl(210 100% 56%), and a darker version is simply hsl(210 100% 40%). Harmonies are easy too: add 180 to the hue for the complement.

HSV, also called HSB, is a close sibling that most color pickers use for their square canvas. It shares HSL's hue, but its third value is brightness, where 100% means the pure hue rather than white. It has the same perceptual blind spots as HSL, and CSS has no hsv() function, so treat it as a picker convenience rather than a format to store.

HSL's problem is that its lightness is a formula, not a measurement of what you see. L is the average of the largest and smallest RGB channels, so it ignores the fact that the eye sees green and yellow light as far brighter than blue.

The yellow vs blue lightness problem

Compare hsl(60 100% 50%) with hsl(240 100% 50%). HSL says their lightness is identical. On screen they are pure yellow #FFFF00 and pure blue #0000FF, and they are nowhere near equal:

  • WCAG relative luminance: 0.93 for the yellow, 0.07 for the blue.
  • Contrast against white: 1.07:1 for the yellow, 8.59:1 for the blue.
  • OKLCH lightness: 96.8% for the yellow, 45.2% for the blue.

This is why an HSL palette with a fixed L of 50% gives you a yellow button that fails every contrast check beside a blue one that passes easily. The distortion affects hue as well. Lighten #0000FF to hsl(240 100% 80%) and you get #9999FF; measured in OKLCH, the hue moves from about 264° to about 282°, which is why pale HSL blues read as lavender.

OKLCH: lightness you can trust

OKLCH is the polar form of OKLab, a color space Björn Ottosson published in 2020 to predict perceived lightness, colorfulness and hue more accurately than older models. It has three channels: L for perceived lightness (0% to 100%), C for chroma (0 for gray, up to about 0.37, with sRGB colors topping out near 0.32) and H for hue in degrees. Pure red is oklch(62.8% 0.2577 29.23).

The payoff is predictability. Colors with the same L look about equally light whatever their hue. Four swatches 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, compared with the 1.07:1 to 8.59:1 spread HSL produced at "equal" lightness. Changing L also leaves the hue alone, so a scale from light to dark stays in one color family. The guide what is OKLCH covers the syntax and gamut in detail.

OKLCH has two costs. Its numbers are less familiar than HSL's, and not every combination of L, C and H exists on an sRGB screen: push chroma too high and the browser has to map the color back into range. It can also describe Display P3 colors beyond sRGB, which is useful on wide-gamut screens but means you should check what users on standard displays will see.

Strengths and weaknesses at a glance

  • RGB and HEX: exact, universal and compact; hard to adjust by intent; limited to sRGB.
  • HSL: intuitive hue wheel, easy lightening, darkening and harmonies; lightness and saturation are not perceptual, so equal numbers do not look equal across hues.
  • OKLCH: perceptually even lightness, stable hue, access to wide-gamut color and reliable generated scales; less familiar, and high chroma values can fall outside sRGB.

Which color model to use in CSS in 2026

All three are safe to ship. rgb() and hsl() have worked everywhere for well over a decade, and oklch() has been in every major browser since 2023 (Chrome 111, Safari 15.4, Firefox 113). The choice is about workflow, not support. A practical split:

  1. Store brand colors and handoff values as HEX. Every tool and platform reads it, and it round-trips exactly.
  2. Define design-token scales in OKLCH. Tailwind CSS v4 defines its default palette in OKLCH, and the same approach works for your own tokens: fix H, step L, and taper C at the extremes.
  3. Do theme math in OKLCH. Dark-mode surfaces, hover states and disabled states behave more predictably when you adjust L than when you adjust HSL lightness.
  4. Use HSL for quick local tweaks where perceptual accuracy does not matter, such as a one-off overlay or a prototype.
  5. Mix in OKLab or OKLCH. color-mix(in oklch, …) avoids the gray, muddy midpoint that sRGB mixing gives complementary pairs.

Whatever you author in, test the final text pairs. OKLCH narrows the gap between "looks equally light" and "measures equally light", but WCAG compliance is still measured with its own luminance formula, not OKLCH lightness.

Try it: see one color in every model

Type a color in any notation into the color converter and it shows the HEX, RGB, HSL, HSV, OKLCH and CMYK equivalents as you type. The HEX to OKLCH converter is the fastest way to move an existing brand palette into OKLCH, and the Tailwind palette generator builds an 11-step OKLCH scale from one base color. Then confirm your text and background pairs in the contrast checker.

Frequently asked questions

Is OKLCH better than HSL?

For palettes and scales, yes. Equal OKLCH lightness values look about equally light across hues, while HSL at 50% lightness ranges from pale yellow to deep blue. HSL is still fine for quick edits where perceptual accuracy does not matter.

Can I use oklch() in production CSS?

Yes. oklch() is supported in Chrome 111, Safari 15.4, Firefox 113 and later versions, which covers all current major browsers.

Why do HSL colors with the same lightness look different?

HSL lightness is the average of the highest and lowest RGB channels, which ignores that the eye sees green and yellow light as much brighter than blue. hsl(60 100% 50%) and hsl(240 100% 50%) share L = 50% but have relative luminances of about 0.93 and 0.07.

Should I store colors as HEX or OKLCH?

Store exact handoff values such as brand colors as HEX, which every tool reads. Author generated scales and theme adjustments in OKLCH, and convert when a platform needs HEX.

Try it with these tools

More color guides