Note
Getting started: Refer to the README of the standalone package, PostCSS plugin, or Lightning CSS plugin, depending on your use case.
Color spaces like OkLCh and HCT aim to be perceptually uniform, and they can be incredibly useful if consistency of lightness across hues is important. However, it can be difficult to ensure consistency of chroma (saturation) across hues. This is because not all hues can deliver the same chroma at a given lightness.
For example, if you take the color oklch(50% 0.275 280)
, you get a very saturated
purple. However, if you shift the hue to 200, you'll find that an sRGB monitor
cannot truly display the color
oklch(50% 0.275 200)
, and instead will silently
fall back to oklch(50% 0.0848 200)
.
Now, how can we make colors of different hues but equal lightness appear equally saturated? One way, of course, is to 8000 set the chroma to zero, but this would just yield the same shade of gray independent of hue. Alternatively, one can try to find the largest chroma value that all the different hues can still deliver at the given lightness. That's precisely what égal (IPA /e.ɡal/ — from French, meaning "equal" or "indifferent") does. It maps the absolute scale of chroma embedded in OkLCh into a scale that is relative to the aforementioned maximal chroma.
At 50% lightness, the bottleneck for chroma is around hue 200 degrees.
The tradeoff here is that we lose consistency of chroma values across lightnesses:
What a chroma of 100% means for egal depends on the lightness.
However, given that the consistency of chroma across lightness was still vulnerable to being broken by fallbacks arising from the limitations of the sRGB (or P3, Rec2020, etc.) color gamut, this tradeoff might not be as bas as it seems, depending on the use case.
As mentioned before, the core idea behind égal is as follows:
For a given lightness, find the largest chroma such that any hue will be still able to deliver that chroma at the given lightness.
Now, this can be generalized into the following:
For a given lightness
$\ell$ , provide a linear mapping$f_\ell\colon[0,\infty)\to[0,\infty)$ such that$f_\ell(0) = 0$ and$f_\ell(100)$ equals the largest chroma such that, at the given lightness, any hue will still be able to deliver that chroma.
These functions
Note that these functions are sensitive to the color gamut that we are targeting, since the maximum chroma values directly depend on that gamut. This is because the gamut is what specifies when a color is no longer considered displayable. For example, P3 monitors are able to display more saturated colors than sRGB monitors, and therefore the maximum chroma values for P3 are larger than those for sRGB. Because of this, the target gamut can be specified as an option passed to the egal function.
Furthermore, if you prefer to use HCT instead of OkLCh behind the scenes, that's also supported.
- color.js: "Color conversion & manipulation library by the editors of the CSS Color specifications"
- ColorAide: Object-oriented color manipulation library written purely in Python.
- OKLCH Color Picker & Converter: Online OkLCh color picker and converter.
- HCT Color Converter: Online HCT color converter.
- HCT Color Picker: Figma plugin for picking colors in the HCT color space.
- Material Color Utilities: Google's color libraries for Material Design.
- Color Appearance Model: Wikipedia article on color appearance models.
- The Science of Color & Design: Google's blog post on the HCT color space.
- Exploring Tonal Palettes: A Jupyter notebook exploring tonal palettes using the ColorAide library, by the author of the library.
- HCT Color Space: Hacker News discussion on the HCT color space, involving its creator.
- Spot color systems: PANTONE, RAL, ANPA
- Widespread digital color systems: X11, web colors
- Specialized color systems: Adobe Spectrum, Apple HIG, Microsoft FluentUI, TailwindCSS, IBM Carbon, VMware Clarity, Google Material, Github Primer, etc.