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)
orlch(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:
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.
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.
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"
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.