OKLCH is a polar coordinate space. Hue is angle in this space. So to interpolate hue from one angle to another, to get from one side of a circle to the other, you go round the edge. This leads to extreme examples like the one shown:
linear-gradient(in oklch, #f0f, #0f0)
You can also go round the circle the other way, which will take you via blue–aqua instead of via red–yellow:
linear-gradient(in oklch longer hue, #f0f, #0f0)
The gradient shown (in either case) is a good example of a way that perceptual colour spaces are really bad to work in: practically the entire way round the edge of the circle, it’s outside sRGB, in fact way outside of the colours humans can perceive. Perceptual colour spaces are really bad at handling the edges of gamuts, where slightly perturbing the values take you out of gamut.
Accordingly, there are algorithms defined (yes, plural: not every application has agreed on the technique to use) to drag the colour back in-gamut, but it sacrifices the perceptual uniformity. The red in that gradient is way darker than the rest of it.
When you’re looking for better gradients, if you’re caring about perceptual uniformity (which frequently you shouldn’t, perceptual colour spaces are being massively overapplied), you should probably default to interpolating in Oklab instead, which takes a straight line from one side of the circle to the other—yes, through grey, if necessary.
linear-gradient(in oklab, #f0f, #0f0)
And in this case, that gets you about as decent a magenta-to-lime gradient as you can hope for, not going via red and yellow, and not exhibiting the inappropriate darkening of sRGB interpolation (… though if I were hand-tuning such a gradient, I’d actually go a bit darker than Oklab does).
During its beta period, Tailwind v4 tried shifting from sRGB to Oklch for gradient interpolation; by release, they’d decided Oklab was a safer default.
ta8645 · 2h ago
Very interesting. Is this just a limitation of our current hardware? How much of this problem would still exist if everyone had a wider gamut monitor, say full DCI-P3? That still doesn't cover the full gamut of Oklch, but would it make the problem practically disappear?
chrismorgan · 1h ago
No. We’re talking about colours way beyond the ranges of human perception.
For this specific gradient, see https://oklch.com/#0.7017,0.3225,328.36,100 and https://oklch.com/#0.86644,0.294827,142.4953,100, and look at the Chroma panel, see how far out of our screen gamuts they are (even tick “Show Rec2020”, which adds a lot of chroma around blue–green and magenta–red), and try to imagine the colours between the lime and magenta (in either direction). The red direction is probably the easier to reason about: there’s just no such colour as a light, bright red. You can have bright or light, but not both. (Its 3D view can also be useful to visualise these things: you’re building a straight-line bridge between two peaks, and there’s a chasm in between.)
shrx · 1h ago
I don't get it, why am I seeing the "out of gamut" colors if my sRGB monitor is unable to display them? Would the charts look different on a P3 monitor?
edit: Also, you mentioned the colors "beyond the ranges of human perception" but I don't think there is any such limitation here, the bottleneck is the hardware (computer monitors).
chrismorgan · 1h ago
It’s squashing the range of the colours down to simulate it.
ta8645 · 1h ago
But once an algorithm to drag the colours back in-gamut was applied, would the lost perceptual uniformity still be a problem practically speaking, with DCI-P3 monitors?
chrismorgan · 1h ago
Yes. I repeat: these colours are way outside gamut. Not just a little bit. P3 helps a little bit, Rec.2020 would help a fair bit more, but you’re still asking for a yellow that is about twice as vibrant as is possible.
Vingdoloras · 1h ago
For anyone else copy pasting the gradients into dev tools to look at them: The second one is missing the # sign on the first color.
And yes, both oklch gradients look pretty weird while the oklab gradient looks nice (if you can accept it going through grey).
chrismorgan · 1h ago
Sorry, fixed.
twhb · 1h ago
Great post!
Also check out oklch.com, I found it useful for building an intuition. Some stumbling blocks are that hues aren’t the same as HSL hues, and max chroma is different depending on hue and lightness. This isn’t a bug, but a reflection of human eyes and computer screens; the alternative, as in HSL, is a consistent max but inconsistent meaning.
Another very cool thing about CSS’s OKLCH is it’s a formula, so you can write things like oklch(from var(--accent) calc(l + .1) c h). Do note, though, that you’ll need either some color theory or fiddling to figure out your formulas, my programmer’s intuition told me lies like “a shadow is just a lightness change, not a hue change”.
Also, OKLCH gradients aren’t objectively best, they’re consistently colorful. When used with similar hues, not like the article’s example, they can look very nice, but it’s not realistic; if your goal is how light mixes, then you actually want XYZ. More: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value....
Also, fun fact: the “ok” is actually just the word “ok”. The implication being that LCH was not OK, it had some bugs.
OKLCH: a color model based on OKLab (a perceptually uniform color space) where you control Lightness, Chroma (saturation), and Hue.
"OK" because "it does an ok job" according to its creater Björn Ottosson.
pavlov · 2h ago
He’s Swedish, so I bet OK is actually short for “Ottossons kulör” but he’s just being modest.
rollcat · 2h ago
Wherever you're working with colors and text/emblems, please also consider contrast and legibility: <https://apcacontrast.com/>
robin_reala · 2h ago
While the above site is great for measuring with a modern contrast algorithm (it’s the current algorithm for the yet-to-be-released WCAG 3 accessibility standards), it’s worth bearing in mind that the WCAG 2 algorithm is somewhat different, and a legal requirement in many markets / industries. You can check your colours against that with a tool like https://www.siegemedia.com/contrast-ratio, although there are many more.
seanwilson · 1h ago
You can pick colors that pass both APCA and WCAG contrast checks though so it's not a problem to use APCA recommendations now.
I find APCA is a little stricter than WCAG for light themes, and APCA is much stricter than WCAG for dark themes, to the point where you really shouldn't use WCAG for dark themes. So most of the time APCA is giving you stricter contrast that easily pass WCAG also.
I keep seeing mentions that APCA will let you finally use e.g. white on orange, or white on vibrant blue that pass APCA but fail on WCAG, but my feeling is there's not a lot of examples like this and most of these pairings only have okay contrast anyway, not great contrast, so it's not ideal to be stuck with WCAG's false negatives but not that big of a deal.
robin_reala · 1h ago
If your colours have enough contrast to pass them both then of course that’s fine!
I only bring it up because I had a situation last week where the better APCA was giving results for both white-on-colour and #111-on-colour as suitable for headline copy under WCAG3, but #111-on-colour was 7.5:1 and white-on-colour was 2.5:1 under WCAG2, hence we could only use one of them legally.
seanwilson · 1h ago
> I only bring it up because I had a situation last week where the better APCA was giving results for both white-on-colour and #111-on-colour as suitable for headline copy under WCAG3, but #111-on-colour was 7.5:1 and white-on-colour was 2.5:1 under WCAG2, hence we could only use one of them legally.
Yeah I understand, would you agree this is fairly rare when using APCA though?
I've had the opposite where the brand guide was suggesting we use a light on dark combo that passed WCAG2, yet it failed APCA, and worst of all clearly had poor contrast just by looking at it. Yet, some people will still go with it because WCAG2 gave it the okay haha.
robin_reala · 40m ago
Rare, yes. Checking with both is definitely recommended!
seanwilson · 33m ago
Maybe you've seen this but there's also https://github.com/Myndex/bridge-pca that does the "this color pair passes both APCA and WCAG2?" check in one pass.
mattdesl · 2h ago
Nice post. OKLCH is quite handy but for writing colors in CSS I hope eventually we’ll get some form of OKHSL/OKHSV[1] so users don’t need to worry about gamut boundaries.
Aside from a few criticisms that others have already raised I think this is quite a nice introduction to OKLCH and how to use them in CSS.
With that out of the way, I'd like to go on a tangent here: can anyone explain the modern trend of not including publishing dates in blog articles? It stood out to me here in particular because the opening sentence said that "OKLCH is a newer color model" and the "newer" part of that sentence will get dated quicker than you think. The main site does mention a date, but limits it to "August 2025" so this seems like a conscious choice and I just don't get it.
> can anyone explain the modern trend of not including publishing dates in blog articles?
In such cases, I usually try to see if the `Last-Modified` header served with the HTML document over HTTP, can be useful, but I conclude that often the same people who don't bother with dating their content -- you'd think they'd understand where the word _blog_ comes from, as in "[web]-log" where timestamps are paramount -- these same people don't know or care how HTTP works. Hint: the `Last-Modified` is the last modification time of the _resource_, in this case the actual HTML document. Just because your "backend" re-rendered the content because you didn't bother with setting up your server caching correctly, doesn't mean you should pretend it's a brand new content every day (which https://jakub.kr/components/oklch-colors does, unfortunately, so you won't know the timestamp from HTTP).
roer · 1h ago
There is a youtuber (Gneiss Name) making educational content through the medium of Minecraft.
He's made one on OKLab as well: https://youtu.be/nJlZT5AE9zY
Aissen · 1h ago
On the comparison between Color spaces, it's weird, my monitor from 2008 does show a difference between the Display-P3 and the sRGB purple colors, yet I didn't think it'd have such a big color range. Is there some color conversion at the browser or OS-level? (my distro/desktop enables colormgr by default).
chrismorgan · 56m ago
It’s just completely wrong.
The first uses oklch(0.65 0.20 300), comfortably inside sRGB, not even at the boundary. The second uses oklch(0.65 0.28 300), which is well outside P3 and even Rec.2020.
The smallest fix would be to make the second one oklch(0.65 0.2399 300) to bring it inside P3 so the demo doesn’t get slightly warped if Rec.2020-capable (not really necessary, but preferable, I’d say), and the first #a95eff (oklch(0.6447 0.2297 301.67)) which is CSS’s fallback.
But purple is also pretty much the worst choice for such a demo—P3 adds the least to sRGB around there, so the difference will be smallest. A better choice is red or green.
So a better pair would be oklch(0.65 0.2977284 28) on the right (a bright red at the very edge of the P3 gamut, well outside sRGB) and #f00 on the left (the sRGB value CSS will map it to if out of gamut).
promiseofbeans · 2h ago
I've not touched oklch, but I've played with oklab gradients a fair amount.
How do they (oklch & oklab) compare for different uses?
tovej · 2h ago
oklch uses the oklab color space.
raincole · 48m ago
That's quite misleading. The parent commenter was asking about gradient (interpolation between colors). Gradients look completely different in oklch and oklab.
tovej · 30m ago
Well yes, it's a different coorsinate system, so gradients would look different. It is still the same color space, but I agree I could have worded that better.
cluckindan · 1h ago
The oklch color space is polar: it’s a cylindrical transformation of the oklab cartesian color space.
tiedemann · 2h ago
Seems nice. But how to pronounce it?
rollcat · 2h ago
That's one reason to prefer Oklab ;)
thiht · 13m ago
o k l c h
Etheryte · 2h ago
With great difficulty.
idle_zealot · 2h ago
Like "okay lock" I would guess.
BrenBarn · 1h ago
"Oh, clutch"?
dev_l1x_be · 51m ago
I was wondering about this exact subject the other day when working on some web dev task. Now I know why this way is better.
TiredOfLife · 1h ago
That gradient example is hilarious.
With RGB you order green salad you get green salad.
With OKLCH you order green salad you get beet soup.
skylurk · 2h ago
In the chromaticity diagram, is my Display-P3 screen mapping everything outside the triangle back into the triangle?
jonnybarnes · 2h ago
Yes, the diagram is a representation. Kind off like drawing 3D shapes on a 2D surface.
seanwilson · 1h ago
> It also works the other way around, where you can change the lightness value to create various color shades and there is no hue or saturation drift unlike in other color modes.
> In the example above, you can see that the OKLCH colors maintain consistent blueness across all of the shades, while in the HSL example, the lighter shades drift to purple and the darker ones muddy out towards grayish.
I see lots of automatic palette generator projects where the shades of each color are generated with OKLCH by only varying the lightness value on some chosen base color. The problem I find is if you look at popular open source palettes, the way the hand-crafted hue and saturation values vary across the shades for different hues isn't that predictable (the curve of the hue/saturation values over shades aren't straight lines or typical easing curves).
Hawking my own tool (using HSLuv with RGB for now), but you can load and compare the hue and saturation curves as they vary over shades of a color using example palettes from Tailwind 3, USWDS and IBM Carbon, plus tweak each shade to your liking:
So I think OKLCH is a nice starting base for palettes and a quick way to generate a color you need in CSS, but I think designers will always want to tweak the hue and saturation of each shade so it looks just right as there's no single right answer.
Hi Alexei, thanks for your work on HSLuv! I love the simplicity of it, where you can use HSLuv as mostly a drop-in replacement for HSL which most designers are familiar with.
I keep seeing new tutorials on designing accessible palettes that still use HSL, where the WCAG2 contrast breaks and goes all over the place as you vary the hue and saturation. HSLuv makes life so much easier here and lets you focus on exploring colors that you know will pass, using a familiar looking color picker.
tovej · 2h ago
Really happy about oklch. You do have to learn the hue numbers, but once you do, everything is more intuitive.
lieblingautor · 2h ago
seems like a great idea, this will make working with colors so much easier
CIAOBENGA · 2h ago
Awesome Post
teekert · 1h ago
These IP addresses being released at some point, and making their way into something else is probably the reason I never got to fully run my mailserver from my basement. These companies are just massively giving IP addresses a bad reputation, messing them up for any other use and then abandoning them. I wonder what this would look like when plotted: AI (and other toxic crawling) companies slowly consuming the IPv4 address space? Ideally we'd forced them into some corner of the IPv6 space I guess. I mean robots.txt seems not to be of any help here.
OKLCH is a polar coordinate space. Hue is angle in this space. So to interpolate hue from one angle to another, to get from one side of a circle to the other, you go round the edge. This leads to extreme examples like the one shown:
You can also go round the circle the other way, which will take you via blue–aqua instead of via red–yellow: The gradient shown (in either case) is a good example of a way that perceptual colour spaces are really bad to work in: practically the entire way round the edge of the circle, it’s outside sRGB, in fact way outside of the colours humans can perceive. Perceptual colour spaces are really bad at handling the edges of gamuts, where slightly perturbing the values take you out of gamut.Accordingly, there are algorithms defined (yes, plural: not every application has agreed on the technique to use) to drag the colour back in-gamut, but it sacrifices the perceptual uniformity. The red in that gradient is way darker than the rest of it.
When you’re looking for better gradients, if you’re caring about perceptual uniformity (which frequently you shouldn’t, perceptual colour spaces are being massively overapplied), you should probably default to interpolating in Oklab instead, which takes a straight line from one side of the circle to the other—yes, through grey, if necessary.
And in this case, that gets you about as decent a magenta-to-lime gradient as you can hope for, not going via red and yellow, and not exhibiting the inappropriate darkening of sRGB interpolation (… though if I were hand-tuning such a gradient, I’d actually go a bit darker than Oklab does).During its beta period, Tailwind v4 tried shifting from sRGB to Oklch for gradient interpolation; by release, they’d decided Oklab was a safer default.
For this specific gradient, see https://oklch.com/#0.7017,0.3225,328.36,100 and https://oklch.com/#0.86644,0.294827,142.4953,100, and look at the Chroma panel, see how far out of our screen gamuts they are (even tick “Show Rec2020”, which adds a lot of chroma around blue–green and magenta–red), and try to imagine the colours between the lime and magenta (in either direction). The red direction is probably the easier to reason about: there’s just no such colour as a light, bright red. You can have bright or light, but not both. (Its 3D view can also be useful to visualise these things: you’re building a straight-line bridge between two peaks, and there’s a chasm in between.)
edit: Also, you mentioned the colors "beyond the ranges of human perception" but I don't think there is any such limitation here, the bottleneck is the hardware (computer monitors).
And yes, both oklch gradients look pretty weird while the oklab gradient looks nice (if you can accept it going through grey).
Also check out oklch.com, I found it useful for building an intuition. Some stumbling blocks are that hues aren’t the same as HSL hues, and max chroma is different depending on hue and lightness. This isn’t a bug, but a reflection of human eyes and computer screens; the alternative, as in HSL, is a consistent max but inconsistent meaning.
Another very cool thing about CSS’s OKLCH is it’s a formula, so you can write things like oklch(from var(--accent) calc(l + .1) c h). Do note, though, that you’ll need either some color theory or fiddling to figure out your formulas, my programmer’s intuition told me lies like “a shadow is just a lightness change, not a hue change”.
Also, OKLCH gradients aren’t objectively best, they’re consistently colorful. When used with similar hues, not like the article’s example, they can look very nice, but it’s not realistic; if your goal is how light mixes, then you actually want XYZ. More: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value....
Also, fun fact: the “ok” is actually just the word “ok”. The implication being that LCH was not OK, it had some bugs.
https://evilmartians.com/chronicles/oklch-in-css-why-quit-rg...
Along with their picker / converter here:
https://oklch.com/
Discussed on Hacker News here:
https://news.ycombinator.com/item?id=43073819 (6 months ago, 30 comments)
"OK" because "it does an ok job" according to its creater Björn Ottosson.
I find APCA is a little stricter than WCAG for light themes, and APCA is much stricter than WCAG for dark themes, to the point where you really shouldn't use WCAG for dark themes. So most of the time APCA is giving you stricter contrast that easily pass WCAG also.
I keep seeing mentions that APCA will let you finally use e.g. white on orange, or white on vibrant blue that pass APCA but fail on WCAG, but my feeling is there's not a lot of examples like this and most of these pairings only have okay contrast anyway, not great contrast, so it's not ideal to be stuck with WCAG's false negatives but not that big of a deal.
I only bring it up because I had a situation last week where the better APCA was giving results for both white-on-colour and #111-on-colour as suitable for headline copy under WCAG3, but #111-on-colour was 7.5:1 and white-on-colour was 2.5:1 under WCAG2, hence we could only use one of them legally.
Yeah I understand, would you agree this is fairly rare when using APCA though?
I've had the opposite where the brand guide was suggesting we use a light on dark combo that passed WCAG2, yet it failed APCA, and worst of all clearly had poor contrast just by looking at it. Yet, some people will still go with it because WCAG2 gave it the okay haha.
[1] https://bottosson.github.io/posts/colorpicker/
With that out of the way, I'd like to go on a tangent here: can anyone explain the modern trend of not including publishing dates in blog articles? It stood out to me here in particular because the opening sentence said that "OKLCH is a newer color model" and the "newer" part of that sentence will get dated quicker than you think. The main site does mention a date, but limits it to "August 2025" so this seems like a conscious choice and I just don't get it.
[0] https://jakub.kr/
In such cases, I usually try to see if the `Last-Modified` header served with the HTML document over HTTP, can be useful, but I conclude that often the same people who don't bother with dating their content -- you'd think they'd understand where the word _blog_ comes from, as in "[web]-log" where timestamps are paramount -- these same people don't know or care how HTTP works. Hint: the `Last-Modified` is the last modification time of the _resource_, in this case the actual HTML document. Just because your "backend" re-rendered the content because you didn't bother with setting up your server caching correctly, doesn't mean you should pretend it's a brand new content every day (which https://jakub.kr/components/oklch-colors does, unfortunately, so you won't know the timestamp from HTTP).
The first uses oklch(0.65 0.20 300), comfortably inside sRGB, not even at the boundary. The second uses oklch(0.65 0.28 300), which is well outside P3 and even Rec.2020.
The smallest fix would be to make the second one oklch(0.65 0.2399 300) to bring it inside P3 so the demo doesn’t get slightly warped if Rec.2020-capable (not really necessary, but preferable, I’d say), and the first #a95eff (oklch(0.6447 0.2297 301.67)) which is CSS’s fallback.
But purple is also pretty much the worst choice for such a demo—P3 adds the least to sRGB around there, so the difference will be smallest. A better choice is red or green.
So a better pair would be oklch(0.65 0.2977284 28) on the right (a bright red at the very edge of the P3 gamut, well outside sRGB) and #f00 on the left (the sRGB value CSS will map it to if out of gamut).
How do they (oklch & oklab) compare for different uses?
With RGB you order green salad you get green salad.
With OKLCH you order green salad you get beet soup.
> In the example above, you can see that the OKLCH colors maintain consistent blueness across all of the shades, while in the HSL example, the lighter shades drift to purple and the darker ones muddy out towards grayish.
I see lots of automatic palette generator projects where the shades of each color are generated with OKLCH by only varying the lightness value on some chosen base color. The problem I find is if you look at popular open source palettes, the way the hand-crafted hue and saturation values vary across the shades for different hues isn't that predictable (the curve of the hue/saturation values over shades aren't straight lines or typical easing curves).
Hawking my own tool (using HSLuv with RGB for now), but you can load and compare the hue and saturation curves as they vary over shades of a color using example palettes from Tailwind 3, USWDS and IBM Carbon, plus tweak each shade to your liking:
https://www.inclusivecolors.com/?style_dictionary=eyJjb2xvci...
So I think OKLCH is a nice starting base for palettes and a quick way to generate a color you need in CSS, but I think designers will always want to tweak the hue and saturation of each shade so it looks just right as there's no single right answer.
I keep seeing new tutorials on designing accessible palettes that still use HSL, where the WCAG2 contrast breaks and goes all over the place as you vary the hue and saturation. HSLuv makes life so much easier here and lets you focus on exploring colors that you know will pass, using a familiar looking color picker.
Apologies! (I can't delete the post though, feel free to down-vote into oblivion)