The Best Color Functions in CSS?

Avatar of Chris Coyier
Chris Coyier on

Take the pain out of building site search with the Algolia hosted API. Start free now!

I’ve said before that HSL is the best color format we have. Most of us aren’t like David DeSandro, who can read hex codes. HSL(a) is Hue, Saturation, Lightness, and alpha, if we need it.

hsl(120, 100%, 40%)

Hue isn’t intuitive, but it’s not that weird. You take a trip around the color wheel from 0 to 360. Saturation is more obvious where 0% has all the color sucked out, like grayscale, and 100% is fully rich color at that hue. Lightness is “normal” at 50% and adds white or black as you go toward 100% and 0%, respectively. I’m sure that’s not the correct scientific or technical way of explaining it, but that’s the brain logic.

There are still issues with using HSL, which Brian Kardell explains in depth. I’m far from a color expert, but I think I see what Brian (and Adam) are saying in that article. Say you have three different colors and they all have the exact same lightness in HSL. That doesn’t mean they are all actually the same lightness. That’s kinda weird, particularly when you’re using this color format as part of a system of colors.

The good news is that there are color features already specced as a CSS Level 4 module that help with this: Lab and LCH. Check out the example from Adam where the colors in Lab have values that reflect their actual lightness much more accurately to how we perceive it.


There are color spaces like Lab and LCH which deal with the full spectrum and have qualities like perceptual uniformness. Thus, if we want great color functions for use in real design systems everyone seems to agree that having support to do said math in the Lab/LCH color spaces is the ideal enabling feature.

In the bug ticket for Chrome, Tab thinks these would be almost trivial to implement.

Note that lab()/lch()/gray() can all be eagerly converted into our existing color infrastructure; they don’t introduce any fundamentally new concepts, they’re just a better way to specify colors, more closely associated with how our eyes actually function rather than being closely tied to how rgb pixels function.

The conversion functions to turn it into rgb are a little bit of code, but it’s just some exponentials and a bit of matrix multiplication, and it’s well-documented in the spec.

This should be a GoodFirstBug sort of thing, I think.