How to animate gradients darkening with pure CSS
I should have emphasized “pure CSS. The fastest and easiest way to accomplish this effect is using an SCSS preprocessor. It has the darken
feature built in, which will take care of about 85% of your needs.
But for me, that strategy didn’t meet two conditions that I needed, and I had to come up with an alternative solution.
1. Darken in Pure CSS
💡 TL;DR Use the CSS filter: brightness attribute, set below 1.0
The first condition was that I was writing pure CSS. That meant the darken
feature was unavailable for me to use, but the CSS filter
attribute was.
The filter
attribute has loads of potential game-changing features to bring more of your favorite design tool features into your code, including blur
, contrast
, invert
, saturate
, sepia
, and even opacity
is recycled here to give you another means of increasing transparency. But the one that is most relevant for darkening and lightening is the brightness
feature.
Brightness runs on the same scale as opacity, 1.0
being the baseline and scaling up to raise the brightness of all colors. The inverse is also true; scaling down the number in decimals between 1.0
and 0.0
will deliver darkening capability within pure CSS.
I was animating a sunset, with the land and sky both darkening as the sun dips beyond the horizon line.
This was part of the Divtober challenge of making single-div CSS Art. Because the entire page was confined to being pieces of one div
, it did have the desired effect of updating the whole image, everything on the page… except the background gradient.
2. Darken the Background Gradient
💡 TL;DR Use a dark overlay and adjust opacity with keyframes.
The filter: brightness
was darkening everything on the page except the background image on the body tag, which stayed vibrant and bright.
This is not how sunsets work. The first solution I attempted was to set up different gradients at different keyframes, like this:
This is not a usable solution because there is no graceful transition between gradients; the background just abruptly jumps from one to the next at the point in time of the keyframe.
I also tried using two background gradients and adjusting the opacity of the color layer. I converted my hex colors to rgba and set the alpha to decrease at each keyframe.
This still was making abrupt cuts and was not providing the intended results:
I realized that opacity
was the right direction, but as I had it applied within the RGBA, linear gradients just wouldn’t work. So I went with an approach I didn’t love but suspected could work; making a masking layer with a dark color on it set to transparent and then increasing the opacity of that layer with each keyframe.
This solution did indeed prove successful.
And not that this is relevant if you aren’t trying to adhere to the rules of Divtober, but I was still able to keep from adding a second <div>
and by using the :after
psuedo-class for the masking layer.
2. Darken the Background Gradient (2nd option)
💡 TL;DR Transform one long linear-gradient.
I had this whole article finished here and was looking up source links when I discovered that Keith J. Grant addressed this very solution, and Chris Coyier CSS-Tricks post built upon that work and offered alternative approaches, one of which stood out to me.
The 2nd option he shared in this Codepen — use one long background that transforms — felt like the way a sunset works. I went back to work and adjusted my Figma templates to explore the colors of the sunset.
then I added a minor update to my background gradient and a new animation:
Now the background image was moving with the sun as it set. This was looking how I wanted it. I made this my final solution.
If you want to see the project referenced throughout this article, you can find it in my Codepen collection here:
https://codepen.io/robleto/pres/BaxYwwd
And I made a time-lapse video of building out this project available on my YouTube channel here:
https://www.youtube.com/user/gregrobleto/featured
Disclosures: All thoughts and explanations — the accurate ones and especially the inaccurate ones — are all my own and do not reflect my employer, who does not pay me to participate in and write about CSS Art challenges. Big thanks to Jim Nielsen, Keith Grant, and Chris Coyier, the giants whose shoulders I stood on connecting their concepts into this treatment. If you want to participate in #divtober, there is a lengthy sign-up form to fill out… I’m kidding; just do it. Make something, post something, and share it with me; I’d love to see it.