The New CSS Color Format You Didn't Know You Needed; OKLCH()

Nathan G Bornstein - Jan 24 - - Dev Community

The other day, I was on uicolors trying to find a suitable color scheme to base a current project's theme off of. The client requested a muted green as the primary color and so I derived that using a dropper tool to extract a hexadecimal code. I plugged that hex code into uicolors (srsly, that site is great) and got my palette. I then went to export the necessary code to plug in to my tailwind config file when I noticed this abomination of a format:


OKLCH syntax I came across that blew my mind at first glance


WHAT IN THE HECK IS THAT???


Needless to say, I had to drop everything I was doing and hyperfixate on this new color format I'd never heard of and/or confirm to myself I wasn't having an existential crisis due to not being aware of OKLCH's long and storied existence (spoiler: it became accepted on all browsers in September 2023 and was only proposed to W3C back in 2021).


Now that my mental crises have been averted, what even is oklch() and why does it exist? We already have so many color formats in CSS and this just seems like yet another thing I need to know about for some ridiculous and arbitrary reason.

I mean, we already have:

  • Hexadecimal #663399
  • RGB/a : rgba(102, 51, 153, 1)
  • HSL/a : hsla(270, 50%, 40%, 1)
  • HSV : hsv(270, 67%, 60%)
  • HWB : hwb(270, 20%, 40%)
  • Many More I'm Forgetting (color keywords don't count)

(All of the above color codes are rebeccapurple; if you feel like a good cry today, read that link)

So WHY do we need any more color formats to confuse us with?? That exact question was the second portion of my hyperfixation search, which I'm hoping to condense in easy-to-reason-about terms and save you the precious time you probably need for a myriad of other things, like, oh I don't know, living a life, spending time with loved ones, etc., ad nauseum.

Let's dive into it:


WHY DOES oklch() EVEN EXIST?


Before we can dive into why oklch() exists, we first need to talk about its ancestor, lch():

lch stands for:

  • Lightness
  • Chroma
  • Hue

Much like hsl() (which stands for hue, saturation and lightness), lch() allows you to configure three separate parameters to determine the:

  • brightness (l)
  • intensity/saturation (c)
  • color (h)

However, there are two key differences that lch() possesses that hsl() does not:


  1. Homogeneous brightness perception by a human from any given color on a screen

  2. lch() isn't bound to any one color space


So what exactly do those previous two points mean?


To address the first point, it's basically what it sounds like; when you, I or anyone else that falls under the category of being a human perceives some color on a screen that's from the Internet, there exists the possibility that two colors with the same lightness value in hsl() will be perceived differently due to their intrinsic hue (or color) value.

Josh Comeau relays this perfectly in his blog post on css color formats. You can even see the difference between hsl() and lch() and how one color seems brighter than the other, even though their lightness values are identical in hsl().

That's what is meant by perception; the incredibly complex anatomy that our eyes have developed over eons to perceive lightness via rods and color via cones doesn't work well with the mathematical formula found in the hsl() format.


Wild stuff, right?


To address the second point on lch() not being bound to any one color space, basically all that means is that the color it produces will appear identical, regardless of what type of screen is displaying it.

The majority of today's screens use a color space known as sRGB. sRGB has been the standard for quite some time and for the most part, it has served its purpose well.

There also exists another color space that's slowly becoming the new standard, however. That color space is called DCI-P3 or simply, P3. It has its roots in cinematic displays for defining a color standard for digital movies to be displayed with.

This is all to say that regardless of what type of color space your screen is using, lch() will produce a perceptually identical color, no matter what.

And you wanna know what's even more mind-blowing about lch()? Because it isn't tied to any one color space, the upper-limit for its chroma (or saturation) value isn't even known. That ultimately amounts to guaranteed color perception on virtually ANY screen until we discover the limit that's intrinsic to lch().


WILD STUFF RIGHT??


Okay, now that we've touched on the basics of lch(), why is there a need for this new, arcane color format known as oklch()?

The main reason is a flaw found in lch() where blue appears to be more of a purple when adjusting the lightness value. You can read more about that in an article written by Vojtěch Vidra and Ondřej Pešička here.

Aside from that, there isn't much of a difference between the two....

UNLESS YOU'RE A TAILWIND USER

That's right, if you have yet to use Tailwind in your projects, please allow me to offer yet another compelling reason as to why you should consider it:


DYNAMIC COLOR THEMES


What are dynamic color themes you ask?

The website Evil Martians has a great post detailing this, but ultimately, using oklch() in conjunction with Tailwind allows for some pretty powerful dynamic color theme generations.

One such use-case can be found in allowing end-users to define what color scheme they prefer for their application's UI, which is demonstrated beautifully by Evil Martians' post in the previous paragraph.

Essentially all that's needed is an array to define the various chroma values you wish to use. Tailwind comes with out-of-the-box shade generation for any given color, so determining lightness values within an array isn't necessary. Simply choose your desired primary colors and you'll have all possible ranges of those primary colors for the user to choose from via a color slider placed in the UI.

Obviously, implementing the color slider and some more intricacies are needed, but a LOT of the heavy lifting is abstracted away for you thanks to Tailwind and oklch(). And to emphasize it yet again, check out that article from Evil Martians if you wish to actually implement that.

And to make that even more of a reality, there exists an oklch() color picker to determine precisely the values you need to generate an entire palette.


And that's pretty much all I have to say on that.

If you're interested in diving more into these two color formats, you can check out the CIELAB Wikipedia page, which is the organization that developed lch() in 1976(??), or check out Björn Ottosson, who developed the new oklch() format in 2020. His proposal can be found in the recently updated CSS Color 4 module.

Thanks for sticking with me through my hyperfixations and crises, as always!

<3<3

. . . . . . . . . . . . .
Terabox Video Player