Fun with animated gradients
🎉
This post is an adaptation of a presentation I gave at the first PNW Web Codepen Meetup. Disclaimer: I work on the Microsoft Edge browser platform team but represent my own opinions, yadda yadda.
I find it kind of funny that, now that we no longer need to 9-slice images to get a button with a gradient on it, CSS gradients are possibly somewhat underutilized. I am thankful we are no longer in an era of web design where everything needs to be mega glossed, but gradients are a little more versatile than you might think—especially when we add animation into the equation.
Let's have fun with gradients!
But first, let's be responsible >:[
Some considerations to keep in mind when it comes to animating gradients:
Prefixing tools
Code generators can spit out some nasty stuff; Ana Tudor explained some key problems with using gradient generators. If you choose to use pre- or post-processing tools, be sure to vet those for efficiency and accuracy—both prefixes and property values.
Note: for simplicity's sake, my demos will be shown unprefixed.
Performance
This is a complicated space because there are so many different factors that can differ page to page, but a good place to start is by consulting CSS triggers. The site shows what CSS properties will cause the browser to do more rendering work on first load (the "change from default" column) and on subsequent changes to elements' properties. "Layout" is when the browser calculates how elements are positioned and sized on the screen. "Paint" is when it draws pixels to the screen. Sometimes repaints are done in layers; "composite" refers to the step in the rendering process where the browser combines these layers.
A CSS property can be said to be "expensive" when it triggers more of these rendering events, and so it might make sense to avoid applying it to a large number of elements or in quick succession. One thing to note, however, is that the information on CSS Triggers refers to changing CSS properties via JavaScript events. So some of these properties may be less expensive when modified via CSS animation @keyframes, as we will be doing in most of the following experiments. For example, Microsoft Edge tries to render these animated changes on the composite step wherever possible.
This is all to say: test your code for performance, especially if you are making changes to properties that would change the dimensions or placement of an element within a page.
Accessibility
Animation should be used to enhance an experience, not hinder it. The Web Content Accessibility Guidelines (WCAG) 2.0 document says that if an animation starts automatically, lasts more than 5 seconds, and is "presented in parallel" with other content, the user should be provided with the means to pause or stop that animation. Elements also should not flash more than 3 times per second; in Level AA compliance (legal compliance ranges between levels A–AAA), those flashes can be below "general flash and red flash guidelines".
Thsese guidelines can help improve experiences for those with seizures, cognitive disabilities, and motion sensitives, but can be beneficial to anyone trying to focus on a task or read some content. I've paraphrased WCAG here, so I would suggest checking out the official document in order to correctly follow compliance guidelines (see guidelines 2.2 and 2.3 in the link).
Related properties
The experiments below make use of most of these properties related to background images, transitions, and animations:
background-image
background-size
background-position
transition-property
transition-duration
transition-timing-function
transition-delay
animation-name
animation-duration
animation-timing-function
animation-iteration-count
animation-direction
animation-play-state
animation-delay
animation-fill-mode
There are shorthands for these properties (background
, transition
, animation
), but for clarity, I'll be using the non-shorthand versions.
The experiments focus more on different effects you can achieve using these properties. For more in-depth information and available property values, you can refer to these CSS specifications: CSS images (gradients), backgrounds and borders, transitions, animations.
Now, onto the experiments!
Experiment #1: Instagram link
What prompted me to explore animated/transitioned gradients is a little quirk I ran into while trying to style an instagram link on my personal site. Basically, I wanted to fade the background of the link from the Instagram brand gradient to the main color on my site.
See the Pen Gradient presentation: example 1 by Melanie Richards (@somelaniesaid) on CodePen.
The result is that the background gradient just flips to the solid color, rather than fading. I thought this might be because a linear gradient and a background color are two different property types, namely, a CSS image and a color. Sidenote: the background
shorthand property is not actually animatable, per the CSS transitions specification.
So next I tried faking a solid color with a gradient:
See the Pen Gradient pres: 1.b by Melanie Richards (@somelaniesaid) on CodePen.
The fake solid color is a little messy, but the transitions spec requires an equal number of stops:
gradient: interpolated via the positions and colors of each stop. They must have the same type (radial or linear) and same number of stops in order to be animated.
Woo hoo! That works...in Microsoft Edge. No transitions in Chrome, Firefox, or Safari. I didn't want this to be the only social media icon in the batch without a transition, so I had to do some pseudo element trickery:
See the Pen Gradient pres: 1.c by Melanie Richards (@somelaniesaid) on CodePen.
The gradient is applied to the ::before
pseudo element, whose opacity is changed on hover. One bonus from this approach is that opacity
is a less costly CSS change, perf-wise, per the CSS Triggers site.
Experiment #2: Ripple button
In this experiment, submitting a form (represented in the JavaScript pseudo-code as a click event) triggers a "ripple" animation effect on the submit button.
See the Pen Gradient pres: 3 by Melanie Richards (@somelaniesaid) on CodePen.
A pseudo element on the button has a repeating radial gradient backround. When the submitted
class is added to the button via click, a CSS animation changes the padding on the button, thereby revealing more rings and creating a "ripple". The animation of padding
here is one we would have to pay attention to, perf-wise, as we are we are forcing the engine to have to do relayout through to composite for the duration of the animation. I initially considered changing the background-size
property to achieve this effect, but that has undesirable effects cross-browser, such as tiling the ellipses, or stretching them.
Experiment #3: gradient-as-marketing
Animated gradients can appeal to emotions in more obvious ways. An AC company could speak to frazzled, overheated customers by transitioning a splash area from a hot red to a cool blue:
See the Pen Gradient pres: 4 by Melanie Richards (@somelaniesaid) on CodePen.
In this example, the gradient background is divided into thirds: the first third is red, the middle transitions from red to blue, and the last third is blue. The background size is set to 300%, and its position is animated vertically. This way, we end on a completely "cool" background.
The transition-fill-mode
property tells the browser how to apply the styles from the animation; by setting forwards
as the property value, we are saying that we want the browser to keep/apply the styles from the last frame of the animation (otherwise, the background would jump back to red).
Experiment #4: shiny callout
Motion draws the eye. If you have a callout that you want to draw the user's attention to that is outside the flow of the main content, you could do a "shiny" effect:
See the Pen Gradient pres: 5 by Melanie Richards (@somelaniesaid) on CodePen.
This effect employs the same general principles as #4, except this time we're animating at a 135deg angle, and the background is scaled to 400% so that the last quarter of the gradient covers the box when the animation ends.
The example assumes the callout is at the top of the page. There's a 2 second delay but in production a longer delay (perhaps 5 seconds) would be better to allow the user time to cognitively process the page layout. If your callout is not at the top of a page, you may want hook this animation into a Javascript scroll event (in that case, you could instead use a CSS transition instead of an animation).
Experiment #5: "plaid" loading spinner
Sometimes accidents can create interesting effects. This gradient is displayed at 200% background-size, and its position is set to 150%. This glitches out to create a somewhat "plaid" effect:
See the Pen 'Plaid' loading spinner by Melanie Richards (@somelaniesaid) on CodePen.
The spinner only runs 4 times to account for accessibility benchmarks (if it were longer than 5s we'd need to provide a way to turn the animation off). I do hope your content loads in less than 5 seconds.
Experiment #6: animated intrinsic placeholder
This experiment builds on the intrinsic placeholder approach to prevent page jumping when images load. A picture
element has a top padding equal to the image ratio of the asset inside it, which will be positioned absolutely when it loads. You may need to throttle your connection to see this one.
See the Pen Instric placeholder animation by Melanie Richards (@somelaniesaid) on CodePen.
We can use some background sizing trickery on a radial gradient to produce a "breathing dots" effect. Sadly, this one doesn't perform super well in Chrome; the dots feel a bit shaky as the background size animates. This might be one where we choose to drop animation and just use gradients for their ability to provide a little repeat pattern without having to load an image asset.
Those are my micro-experiments with animating gradients! Feel free to share your own in the comments.