oklch() color format is coming to CSS
Artikel uit Frontmania magazine 2023
Nowadays, designers don’t choose each color manually. They generate a palette by a system. And they need some solid math as the foundation for this system. Also, we now have new screens which shows +30% colors. rgb(), hsl(), and hex can’t handle this new world order. This is why CSS Colors 4 specification brought a new color format oklch().
Like hsl(), oklch() encodes colors much closer to the way of how humans think about colors. Instead of using amount of red, green, blue, it uses lightness, saturation and hue.
Image 1: OKLCH axes
oklch() color functions has 3 or 4 numbers to define color: oklch(L C H) or oklch(L C H / α).
- L is perceived lightness. It ranges from 0% (black) to 100% (white). The % is mandatory, even for zero values.
- C is chroma, the saturation of color. It goes from 0 (gray) to infinity. In practice there is actually a limit, but it depends on a screen’s color gamut and each hue has a different maximum chroma. For current screens, the value will be always below 0.37.
- H is the hue It goes from 0 to 360, through red 0, yellow 70, green 120, blue 200, purple 300 and then back to red. Since it is an angle, 0 and 360 encode the same hue.
- α is opacity (0-1 or 0-100%).
Image 2: OKLCH color examples
Native Color Manipulations
Why do we need oklch() if we already have similar hsl()? The main problem with hsl() is that L component of the colors is not very useful. It has no uniformity, the same number will have different lightness for our eyes depending on the hue.
L component in oklch() is much more uniform and closer to how our eyes see (see image 3).
Image 3: Hue-Lightness slice of HSL and OKLCH spaces with the same chroma/saturation and black-and-white versions below. HSL lightness is not consistent across hue axes.
It happens because HSL is a cylindrical color space. Every hue has the same amount of saturation (0—100%). But in reality, our displays and eyes have different max saturations for different hues. HSL hides this complexity by deforming the color space and extending colors to have the same max values.
Here are a few real use case examples to demonstrate this problem:
- Adding 10% lightness will have different results for blue and purple colors. SASS users may remember how darken() generates unexpected results.
- Hue changes (for instance, producing an error-like red color using a company’s accent color) could also change lightness, thus making text unreadable.
Image 4: In HSL, hue changes could lead to accessibility issues from low contrast
Because of L issue, hsl() can’t be used to generate palette in design systems. For instance, Stripe in “Designing accessible color systems” post ask community not to use HSL for palette generation. OKLCH color space is a much better way for color transformation. Every color with the same L will look similar. This is why palette generators like Huetone already use OKLCH.
This is why upcoming native color transformation in CSS will allow you to choose color space to do transformation.
Image 5
In this syntax, you define a new color from another color (so the feature is called relative colors). You are using any color function, write `from` and then color in any format (including CSS Custom Properties).
Each component (l, c, h for oklch()) after from X can be:
- A letter (l, c, h), which indicates to keep the component the same as it was in the origin color.
- A calc() You can use a letter (l, c, h) instead of a number to reference the value in the origin color.
- A new value, which will replace the component.
With CSS Custom Properties, we can define the :hover logic just once and then create many variants simply by changing the source color (see image 6).
Image 6
Wide-gamut colors
New screens (like OLED or in all new Apple devices) can show +30% more colors. With this new wide-gamut you can make a more vivid landing page. Or you can be more flexible in your design system’s palette.
Image 7: Newly available P3 colors for green on the left. Real-world icon comparison with sRGB vs. P3 on the right.
You can’t use rgb() or hsl() to encode these new colors. And oklch() can help you here as well (see image 8)
Image 8
Moving to oklch()
Right now all major browsers support oklch(): Chrome since 111, Safari since 15.4, and Firefox since 113.
You can convert colors in your source code by oklch.com converter or by convert-to-oklch script.
Image 9
By moving to OKLCH right now, you will have a few improvements in code maintenance:
You will be able to find some contrast issues just by comparing L numbers. But contrast math is really tricky, and it is better to use special tools (see image 10).
Image 10
You will be able to apply simple color modifications right in the code and get predictable results as shown in image 11.
Image 11
Since OKLCH is much closer to real-life color, using oklch() in CSS will educate developers and lead the community to an overall better understanding of color.
By moving to OKLCH today, we’re preparing ourselves for the not-so-distant future where native color modification will be available in CSS. OKLCH is the best color space for color transformations, and it’s better to become familiar with its axes by using them in color definitions now.
Biography
Andrey Sitnik is principal front-end in Evil Martians. He is the creator of PostCSS, Autoprefixer, and Browserslist.