• Home

  • Engineering

  • Engineering insights

Engineering insights

3 min read

LCH: Easier accessibility and prettier colors

The lightness, chroma, hue color space is easy to work with and can enhance accessibility.

By Sage Fennel · July 6, 2023

The lightness, chroma, hue color space was created to be perceptually uniform and easy for humans to work with. We used LCH to enhance the accessibility for accent colors within our design system.

Color spaces

Let's start with some terms:

  • RGB (red, green, blue): RGB colors look like rgb(255, 128, 0) or #ff8000

  • HSL (hue, saturation, lightness): HSL colors look like hsl(180, 50%, 60%)

  • LCH (lightness, chroma, hue): LCH colors look like lch(50, 150, 180) or lch(50%, 100%, 220)

  • HSV (sometimes called HSB) is similar to HSL. We will not be covering HSV in this post

What's wrong with RGB?

RGB colors are hard for humans to understand. We don't think of colors as being a mix of red, green, and blue light. Worse yet is manipulating RGB colors. There is no easy way to lighten/darken or saturate/desaturate an RGB color.

Look at these sliders and try to make any sense of them:

An RGB color picker with four sliders, the first is R, then G, B, and A. As the triangle indicator is moved left to right, the numbers and color identifier change. For a pale orange color, the R slider shows 255, the G slider shows 153, the B slider shows 0, and the A slider shows 255.
An RGB color picker from Aseprite with sliders that relate to the colors you’ll get but in a confusing way.

What's wrong with HSL?

HSL is much easier to use than RGB. However, it has a critical flaw: It's not perceptually uniform. Different hues will drastically change the perceived brightness for a given saturation and lightness.

The HSL spectrum, going from red on the left, through orange, yellow, green, blue, purple, and back to red.
The HSL rainbow has dark areas (blue) and bright areas (green).

A bar showing the color spectum for LCH starting with a bright pink on the left, going through red, orange, yellow, green, blue, purple, and returning to red.
The LCH rainbow has a uniform brightness for different hue values.

Small rectangles of different colors in HSL and LCH color models. White text is on top of each showing the color value of the underlying rectangle.
Rainbow color ramp with white text on top showing that for the same Saturation and Lightness values differing hues in HSL result in different contrast values.

For accessibility compliance, WCAG AA requires a 4.5 contrast ratio for text. You must carefully adjust the saturation and lightness of each color (hue) in your palette. We did some code exploration of how to make our accent colors accessible. We initially used the HSL color space to darken app colors until they met the 4.5 ratio.

A grid of cards with white text and small app icons in a field of either green, blue or red, generated using different color spaces.
LCH vs. HSL card colors demonstrating that the LCH contrast adjustment algorithm generates richer colors while still meeting accessibility requirements.

HSL does an ok job of darkening colors for accessibility, but you can see that when we used LCH, I retained much more of the original saturation of the color. The HSL cards look dark and dull.

How do I use LCH?

As of May 2023, lch() is supported in all browsers. To support older browsers you'll need to use a CSS processor or convert colors manually.

An example CSS LCH color looks like lch(40 68.8 34.5). This color uses the new CSS Colors 4 syntax without commas and supports the new slash syntax for specifying the alpha channel: lch(40 68.8 34.5 / 50%).

You can explore LCH colors and convert them to RGB using the LCH Colour Picker. Using colord, you can convert LCH colors to RGB colors:

// This should be in a file called "colord.js" or similar
import { extend } from "colord";
import a11yPlugin from "colord/plugins/a11y";
import lchPlugin from "colord/plugins/lch";
extend([lchPlugin, a11yPlugin]);
export { colord } from "colord";

And then in a different file:

import { colord } from "./colord";
const lch = colord({ l: 55, c: 85, h: 25 }).toLch();
colord(lch).toHex(); // "#fa234c"
lch.l -= 10;
colord(lch).toHex(); // "#da0036"

You can even generate more accessible versions of existing colors:

import { colord } from "./colord";
// Contrast is a number in the range 1-21 using the WCAG AA algorithm
function darkenColorForContrast(baseColor, contrast = 4.5) {
  const lch = colord(baseColor).toLch();
  while (lch.l > 0 && colord(lch).contrast("#fff") < contrast) {
    lch.l -= 1;
  return colord(lch).toHex();
darkenColorForContrast("#f60"); // "#d54100"

Before and after contrast correction: 2.9 to 4.6 (WCAG AA requires 4.5+ for most text)
Before and after contrast correction: 2.9 to 4.6 (WCAG AA requires 4.5+ for most text).

Lossy conversion

LCH can represent colors outside the sRGB color space used by RGB and HSL colors in the browser. Many colors in LCH can't be translated accurately to RGB. It's important to keep colors in LCH format as long as possible. And LCH must be implemented in CSS before non-SRGB colors in the browser.

LCH tradeoffs

You might be surprised to learn that with LCH colors, the chroma and lightness maximum values depend on the hue. In my experience, this isn't a huge issue, but it can be weird to get used to. If you exceed a parameter's range in LCH, its value is adjusted to the maximum.

Also, the hue in LCH is offset from the hue in HSL. HSL's hue starts at pure red, while LCH's hue starts at pinkish red. So if you were attempting to convert HSL to LCH manually, your hues would get mixed up.

Further reading

  • Color Combos: A color contrast and combination website that supports LCH colors if your browser supports LCH colors. Note that this site uses the WCAG color contrast algorithm, which isn't perceptually correct. The WCAG is considering switching to a perceptually correct contrast algorithm in a future release, enabling designers to pick colors that look better and are more accessible without automatic contrast-checking tools complaining about them. Note: If your browser doesn't support LCH, you can use this URL to approximate the colors via hex codes.

  • Designing accessible color systems by Stripe: A great read on how Stripe used perceptually uniform color spaces to redesign their color palette for accessibility! This is where I originally learned about the CIELAB color space.

  • Perceptually uniform color spaces by Programming Design Systems: This article contains excellent information on color spaces with many great images.

Get productivity tips delivered straight to your inbox

We’ll email you 1-3 times per week—and never share your information.


Related articles

Improve your productivity automatically. Use Zapier to get your apps working together.

Sign up
A Zap with the trigger 'When I get a new lead from Facebook,' and the action 'Notify my team in Slack'