DEV Community

Cover image for The New CSS Color Format You Didn't Know You Needed; OKLCH()
Nathan G Bornstein
Nathan G Bornstein

Posted on • Edited on

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

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

Top comments (10)

Collapse
 
moopet profile image
Ben Sinclair

This is really interesting. Having looked through the examples, I wonder if my eyesight is different in some way, because things which the article/tool implies should have the same perceptual lightness to me often look quite different. My eyes are getting old though :)

OKLCH definitely seems like the future. HSL seemed like the future though as well, but people still seem to stick to hex or RGBA, so I guess we'll see how it shakes out. I'm going to use it on my next project though!

My obligatory annoyance: why do so many people write so well about things like colour perception and then make tools which use inaccessible colours, tab stops, and signifiers in their controls? It's really weird but loads of them seem to do it.

Collapse
 
greenteaisgreat profile image
Nathan G Bornstein

That's a really good point you made; there are so many factors at play with all this stuff, that I'm sure there's bound to be hiccups in what it sets out to do sometimes. That also made me think of folks with color-blindness in some shape or form and how those shades would come across to them.

Good food for thought 🤝

Collapse
 
ben profile image
Ben Halpern

Huh, I had no idea. Thanks for this!

Collapse
 
joestrout profile image
JoeStrout

Any chance you could put periods at the end of your sentences, even when it's the last sentence in the paragraph?

The lack of them trips me up every time, and makes it impossible to concentrate on the actual content of what you're saying.

Collapse
 
greenteaisgreat profile image
Nathan G Bornstein

I'm actually really happy you said that. I went back and forth between including them in this post, thinking the flow might be visually better to not have hard stops due to the format on here.

But I agree, it can blend in with everything after a while. Plus, screen-readers might not acknowledge breaks, so it could be one long run-on sentence for folks that use those devices. My future and present self thanks you 🤝

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

High five everyone who already used OKLCH ✋

Collapse
 
greenteaisgreat profile image
Nathan G Bornstein • Edited

*me in the corner, unaware of the magic of oklch() *

seriously, i love your content and am so happy i found your account on here. you're a real one 🤝

Collapse
 
best_codes profile image
Best Codes

Awesome and informative article. I knew nothing about this; thanks for writing!

Collapse
 
greenteaisgreat profile image
Nathan G Bornstein

Of course, happy to brain-vomit on any particular topic I find interest on at any given point 😎 and even more grateful that you took the time to read said exclamations
<3<3

Collapse
 
best_codes profile image
Best Codes

No problem! I guess now I will follow you for more good content. :)