Workarounds for Buggy Gradients
CSS has a great new feature where you can specify the color space to use by
adding in <colorspace>
to the gradient (not yet supported in Firefox).
This is especially useful for avoiding the
gray dead zone
that comes from rectangular color spaces like the default sRGB.
Just look at the difference between a blue-to-yellow gradient in srgb compared
to oklch with the same beginning and end:
linear-gradient(to right, blue, yellow)
linear-gradient(to right in oklch, blue, yellow)
I like that second one a lot better, though there certainly may be specific use cases where the first could make sense, too.
Unfortunately, if you’ve experimented with this much, you may have run into some weird behavior when building any gradients to white, black, gray, or transparent. This is especally bad when using blue, but it does affect other hues as well. Look at the teal and purple that shows up in these gradients, when they really should consist only of shades of blue:
Here are red gradients, some of which show an orange or burgandy center.
And to make matters worse, this behavior isn’t the same across browsers. The center of that second gradient (blue to gray) is teal in Chrome, but purple in Safari. The red to gray gradient differs between the browsers as well. Thankfully, there’s a reasonable workaround once you understand the problem.
The most reasonable solution is to do the gradient in sRGB or another rectangular color space. When you aren’t defining a gradient between different hues, the benefit of using a polar color space doesn’t really apply. However, out of academic curiousity, I dug into the cause of these bugs and happened to find some reasonable workaround within the OKLCH and HSL color spaces.
Workarounds in OKLCH¶
As far as I understand it, this is a browser bug, possibly relating to fact that the colors
white, black, transparent, and any true neutral gray can be represented multiple
ways all the polar color spaces.
oklch(100% 0 0deg)
is white, but so is oklch(100% 0 240deg)
.
The first is a “red” white with no chroma, and the second is a “blue” white
with no chroma.
For some reason, the color white
is interpreted as having just the tiniest bit
of chroma, but in a yellow-green hue.
Below is an incorrectly interpreted blue to white gradient, followed by a correctly
interpreted gradient to oklch(100% 0.001 90deg)
.
The two appear exactly the same, and I think for some reason this shows how
the browsers are converting white
to OKLCH.
The result occurs if you specify white in a non-oklch color syntax, including
hex, RGB, and HSL.
Thankfully, there’s a workaround: specify the neutral color in the same color
space as the gradient, and match the hue: oklch(100% 0 264deg)
.
This seems to fix the issues in Safari and about half of the cases in Chrome.
To fix those last remaining bugs in Chrome, add back just a smidge of chroma:
oklch(100% 0.001 264deg)
.
This trick works for black, gray, and transparent.
Here are all the same gradients as above, but corrected:
And corrected red gradients:
Workarounds in HSL¶
Some HSL gradients suffer from the same issues. Red gradients seem to behave as expected, but these blue gradients all have some vibrant purple in the middle:
A similar approach can fix this, but it’s a little more finnicky. For white, black, and transparent, you need to set the saturation to 100%. Saturation as low as 1% can work, but I've found that in Safari, it’s very particular based on the precise luminescent value. Using 100% seems to be the most reliable fix, and this is fine because at these lightness levels, saturation is irrelevant anyway.
Dark grays can be problematic. Chrome seems to handle this fine, but in Safari, variations of a tenth of a percent can have wild effects on the result. In the following gradients, lightness of 10% and 9.7% work fine, but values between that inexplicably result in a purple gradient:
In these cases, a slighly higher saturation (at least 2%) seems to fix the issues. Here are fixed gradients in HSL:
Issues in LAB and LCH color¶
As a final note, the LAB and LCH color spaces also display unexpected hues in some gradients, most notably a purple showing up when the gradient should be blue. The bug in these cases lies in the definitions of the color spaces themselves, and not in the browser’s interpretation.
You should avoid these color spaces and use the superceding ones instead: OKLAB and OKLCH.