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.
Usually you fix it by moving your point through a different colour space. Choice depends on your requirements and mediums you're working with (normally different types of light sources or screens).
I had to write a low level colour interpolation librar for a few interactive art projects, so I dipped a bit into this, but I'm no colour expert
I recently implemented both, first I started with OKLab which turned out really well, the gradients you get from it are amazing and the usual color sets (analogous etc.) produce really pleasing sets.
However I quickly ran into the main problem with it, which is that fiddling with its Lightness, Chroma and Hue dials doesn't produce human understandable results. For example sometimes changing L or C induces a color shift, or for some given values changing L only gives midrange values that doesn't go up or down all the way.
I then implemented OKLCH on top of that, which was the way I assumed everyone was doing it. Just have it act as the controller for the human layer, then convert to OKLab for creating gradients etc. The article doesn't really go into it, but having OKLCH as the frontend controller fixes the LCH sliders such that they produce values that make sense to us humans, while still having the superior OKLab setup in the back.
Which brings me to point at the crux of the matter: avoid gradients between two dissimilar colors in the first place.
[EDIT] Ahh.. The W3C has already looked at this. https://www.w3.org/Graphics/Color/Workshop/slides/talk/lille...
See slide 19: https://www.w3.org/Graphics/Color/Workshop/slides/talk/lille... -- if you ask CIELAB to make "pure blue" (RGB 0 0 100%) become grayscale, the intermediate colors become purple to the human eye. The entire point of a perceptual color space is that that doesn't happen. OKLCH fixes that.
BTW, credit to Björn Ottosson, who basically side-projected a color space into the web standards and more: https://bottosson.github.io/posts/oklab/ ... folks like him are why we sometimes have nice things!
> you should probably default to interpolating in Oklab instead
The article says as much. Quoting:
> This can be a double edged sword. While some gradients might look smoother, you might also see colors that you've never defined. This is because hue in OKLCH is circular and gradients can take unexpected detours.
> To avoid this, many tools use OKLAB for gradients, which interpolates in a straight line and gives more consistent results.
And yes, both oklch gradients look pretty weird while the oklab gradient looks nice (if you can accept it going through grey).
Isn't it any continuous function that starts at a specified color and ends at another specified color?
How then does one say that any gradient is good or bad?
Isn't the problem you are highlighting guaranteed to exist for any colorspace that defines colors outside of human perception?
For a spot color (from a gel covering a light) the light diffuses further from the center of the projected light — two spot colors (with different gels) then, next to each other, would give a kind of gradient from one color to the next as you walk a line from the center of one light to the other.
I wonder what the closest analog to this is algorithmically?
I guess where I am going with this is: is there precedent in nature as to how gradients are supposed to work (and therefore an analog which we should try to model) or are we going strictly on how the human eye perceives color and what algorithm we think "looks" right?
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.)
I had claude build me a comparison (not sure why 2x the same HSL one) https://i.imgur.com/uziQibR.png
Also super hard in RGB: https://jsfiddle.net/nhgvzm5p/2/ it's just a 2 color OKLCH gradient:
oklch(0 0.07 279) 66%,
oklch(0.98 0.09 276) 99%That is the biggest problem with these colour spaces: the edges are unclear, and overflowing them has bad effects.
- how to easily add "warmth". you can't just add red+green - no good tools for it (e.g. a nice gradient picker ui that lets me specify if I want to allow running out of bounds of teh color space and clip a few colors at max saturation)
It doesn't look significantly darker to me.
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.
While it’s more useful for a perceptual color space, relative colors are supported for all CSS color spaces e.g.
background-color: rgb(from var(--base-color) calc(r - 76.5) g calc(b + 76.5));https://github.com/hazelgrove/hazel/blob/dev/src/web/www/sty...
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.
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).
On some blogs I can only tell the timeframe of the content from the timestamps on the comments ... but many blogs like the OP's don't support comments. I'm not likely to revisit them. (The blurb on the OP's main page is ironic ... rather than obsessing over the smallest details I see obsession over esthetics to the detriment of functionality.)
https://evilmartians.com/chronicles/oklch-in-css-why-quit-rg...
Along with their picker / converter here:
Discussed on Hacker News here:
https://news.ycombinator.com/item?id=43073819 (6 months ago, 30 comments)
There is a very clear shift towards green in the OKLCH lightness value change example, enormously more so than any purple vibe in the HSL example.
Clearly being able to select colours of the same perceptual intensity has value, but some of the claims here as to the benefits are exaggerated.
https://oklch.com/#0.7684,0.1754,218.1,100
To increase brightness past the limit of the Hue band for the color, the rendered output color on a display shifts cyan due to the limited brightness range of a saturated blue in the Display P3 color space. OKLCH is, when varying brightness along a gradient, Saturation-invariant rather than Hue-invariant; whether that effect is desirable is a matter of aesthetic preference, but after decades of Hue-invariant desaturated web color, it’s certainly refreshing to have a choice about which compromised invariance to take.
https://news.ycombinator.com/item?id=44588388
Science recently invented a much-deeper blue LED than we have now, so I expect in a decade or two, whatever ends up succeeding Display P3 will be much more able to represent that gradient without cyan-shift, and all of the years of OKLCH gradients created before that time will end up showing a more accurate blue gradient with only the colorspace change. In the meantime? Do whatever’s aesthetically pleasing; there is no Right Answer in design :)
ps. One could argue that a post-OKLCH colorspace should not accept the binary decision of Hue or Saturation invariance, and instead should be Saturation-invariant to the display’s native limit and then transition smoothly to Hue-invariance at that threshold. I believe that’s a Difficult Problem in color profile specification terms, since it isn’t just pre-calculating the changeover threshold for all monitors (not to mention, do you change blue sooner or same as red, etc) but it’ll be a while longer before I’m versed enough in ICCv4 to explain that perception. It sure would make for an interesting experimental DisplayCAL target, though!
This misses the point: the example claims the hue isn't shifting, when it very clearly is. It's not a question of why.
This totally has uses but it is not, as claimed, "there is no hue or saturation drift" given the hue has shifted so much.
If that's a correct implementation of OKLCH, then it's not something I would ever touch. Something seems to be deeply wrong with however they're calculating hue.
HSL/HSV have issues with perceptual lightness. But not with hues. The hue is constant and doesn't need any correction depending on saturation or lightness.
Also if I use the macOS app "Digital Color Meter" I get essentially the same green value for the rightmost OKLCH and HSL colors (226 and 227 for "display native values" and 228 and 227 for sRGB).
"OK" because "it does an ok job" according to its creater Björn Ottosson.
I am not sure what the status is.
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.
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).
[0] https://gist.github.com/dkaraush/65d19d61396f5f3cd8ba7d1b4b3...
With RGB you order green salad you get green salad.
With OKLCH you order green salad you get beet soup.
How do they (oklch & oklab) compare for different uses?
Edit: I would imagine that the only way to definine a perceptually uniform color space is by tons of user testing. This is how Munsell developed his color space… specifically presenting test subjects with pairs of identical and near-identical color swatches and asking if they could tell the difference.
In this way, pairwise comparisom of similararity became the bedrock of color perception science.
The newer CIE colorspaces (CIECAM02 CIECAM16) seem to address the effect, that color-perception goes wild if you change background and illumination. Oklab seems to only make some fixed choice about how to include the chromatic part to lightness. I'm not so sure, how this definition of lightness is any better than grayscale (from 1931).
I always understood those “muddy midpoints” as a failure to properly gamma correct the interpolation between two (s)RGB colors. Is that happening here, or is the mud coming from something else?
The OKLCH lighter shade veers off into bright cyan! I don't see HSL getting grey on the dark side.
Also, I do hope people begin to see the value of "designing toward intuition," I can't help but notice that efforts like these are exact opposite of what happened when (most of) the world forcibly converted to metric.
> 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 need to tweak the hue and saturation of each shade so it looks just right as there's no single right answer you could encode into the color space.
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.
https://bottosson.github.io/posts/oklab/#what-about-existing...
colors are much more accurate in terms of
how humans perceive them and it makes
working with them much easier
This is an aim, which I am not sure fully addressed.
Think of the reason why can't I get oklch or any other method of color definition in HTML to produce the "gold color" as we humans think of it.The fundamental reason you can't get a truly convincing "gold" color using a single value in oklch, rgb, or any other color definition is because gold is a material, not a color.
What our brain perceives as "gold" is not a single, flat hue. It's a complex interplay of light, reflection, and texture.
Point is, even with sophisticated CSS involved with linear gradient it is still a challenge
See demo at https://jsfiddle.net/oyw9fjda/
---
<style>
.flat-gold {
width: 200px;
height: 200px;
/* Using Oklch for a perceptually uniform yellow */
background: oklch(85% 0.11 85);
}
.gradient-gold {
width: 200px;
height: 200px;
background: linear-gradient(
135deg,
oklch(60% 0.1 65), /* Dark, desaturated shadow (brownish) */
oklch(85% 0.11 85) 45%, /* Rich mid-tone gold */
oklch(98% 0.1 90) 50%, /* Sharp, bright highlight (almost white-yellow) */
oklch(85% 0.11 85) 55%, /* Back to the mid-tone */
oklch(70% 0.1 75) /* Softer shadow on the other side */
);
}
.conic-gold {
width: 200px;
height: 200px;
border-radius: 50%; /* Often looks best on a circle */
background: conic-gradient(
from 90deg,
#B38728, /* Darker Start */
#FEE9A0, /* Bright Highlight */
#D4AF37, /* Mid-tone */
#FEE9A0, /* Another Highlight */
#B38728 /* Darker End */
);
}
.premium-gold {
width: 200px;
height: 200px;
background-color: #B38728; /* Fallback color */
background-image:
/* Layer 1: Sharp highlight on top */
linear-gradient(
175deg,
rgba(255, 253, 240, 0.6) 0%,
rgba(255, 215, 0, 0.2) 40%,
rgba(136, 96, 0, 0.5) 90%
),
/* Layer 2: Base metallic gradient */
linear-gradient(
105deg,
transparent 35%,
#FEE9A0 48%,
#D4AF37 52%,
transparent 65%
);
border: 2px solid oklch(60% 0.1 65);
box-shadow: inset 0 0 10px rgba(0,0,0,0.4);
}
</style>
<div class="flat-gold"></div>
<hr />
<div class="gradient-gold"></div>
<hr />
<div class="conic-gold"></div>
<hr />
<div class="premium-gold"></div>this is _especially_ vital if you're not from color science & color correction industry, because that means you have no prior experience(s) with many problems & implications faced in the last 30 years of say, film post-production, which might have seen or solved some of these newly experienced "problems", which might've been caused by hardware choice(s) for example.
Apologies! (I can't delete the post though, feel free to down-vote into oblivion)