Chris’ Corner: Better CSS Animation
CSS animation rules. Just have a look at… CodePen lol.
There was a (long) time when it didn’t exist, though. My old buddy Jonathan Snook wrote in 2007 that he didn’t like the idea of animation coming to CSS at all, but had changed his mind by 2009.
They’ve evolved a bit since those early days. They are GPU-accelerated now. We don’t need vendor prefixes on the properties. We can animate more properties, including the values of Custom Properties (especially if we type them). But they haven’t changed that much, and that’s one of the cool things about the web. Actual web platform things don’t change under our feet that dramatically, making them worth learning because that knowledge has a long shelf life.
What also evolves is how people use them, best practices, and the clever tricks that need to be discovered.
Here’s one!
I saw a blog post by Harold Cooper a little while back called Spinning Diagrams with CSS. It’s since been gone’d from the internet, but the ol’ archive caught it and I’ve made a Pen out of it, too, for posterity. Harold was showing off mathematical formulas that benefitted from a 3D look, like this:
I completely wimped out there and used an animated GIF. But Harold did not:
Several people expressed surprise that the spinning diagrams don’t use any JavaScript or animated image formats, just HTML and CSS.
One of the cool parts of Harold’s tricky is using one animation to spin things, and another animation to unspin them. Notice how the numbers at letters appear to move in 3D space, but always “face forward”. Nobody was thinking about that in 2007, I’ll tell ya that.
I remember I first read this post over RSS and the animations worked even in RSS. Probably because the styling was built in <style>
blocks that my reader respected. So cool.
Josh Collinsworth wrote Ten tips for better CSS transitions and animations which is full of good practical stuff. Huge fan of #1: “Make them shorter than you think they should be”. I don’t think I’ve ever picked a transition-duration
that didn’t get lower and lower over the time I played with it, and even after deploying.
So many great examples and real-world advice!
Some of the advice, like “don’t use browser defaults”, means then having to replace easing with more bespoke stuff ala cublic-bezier
curves. Josh has a more in-depth post on that as well that is very helpful. I probably rely on browser defaults too much when it comes to this stuff, but sometimes when I play with custom curves they end up feeling worse to me. Maybe I shouldn’t just take the first thing Copilot gives me 😬 and actually understand it.
If you haven’t heard of After Dark, you’ve probably heard of Flying Toasters. It’s this iconic screensaver from the 1990s where, uh, flying toasters fly in sort-of 3D-looking space across your screen. From the top right to the bottom left, actually.
Good news, Bryan Braun did it in CSS, along with all the other After Dark screensavers.
… annnnnd he did that in 2014 which shows you how on top of things I am. (I like clicking on the Contributors page of a repo as it clearly shows you the year of the first commit.) You can kinda get a feel of the vintage by seeing chunks of CSS like this:
-webkit-animation: fly 10s linear infinite;
-moz-animation: fly 10s linear infinite;
-ms-animation: fly 10s linear infinite;
-o-animation: fly 10s linear infinite;
animation: fly 10s linear infinite;
That’s just how we used to roll.
VERY NEW THING ALERT. I need to do a deeper dive on this but I think it’s worth mentioning here right now. I’m assuming you know what cubic-bezier()
is, partially because I literally linked to an article about it from Josh above.
Cubic bezier has four parameters. No more. No less.
The new thing here is linear()
which functions as an alternative for animation timing values, which can take any number of values. Ollie Williams has a great article on it, and I’ll take a quote that he took because it’s a nice quick summary:
MDN explains the syntax:
linear(0, 0.25 75%, 1)
produces a linear easing function that spends 75% of the time transitioning from0
to.25
and the last 25% transitioning from.25
to1
.”A more real-world example looks like this:
animation-timing-function: linear(0, 0.218 2.1%, 0.862 6.5%, 1.114, 1.296 10.7%, 1.346, 1.37 12.9%, 1.373, 1.364 14.5%, 1.315 16.2%, 1.032 21.8%, 0.941 24%, 0.891 25.9%, 0.877, 0.869 27.8%, 0.87, 0.882 30.7%, 0.907 32.4%, 0.981 36.4%, 1.012 38.3%, 1.036,1.046 42.7% 44.1%, 1.042 45.7%, 0.996 53.3%, 0.988, 0.984 57.5%, 0.985 60.7%,1.001 68.1%, 1.006 72.2%, 0.998 86.7%, 1);
Fancy! There is a 92% chance this will be generated by a tool for you, like maybe this one, and others likely to spring up. Get it? Spring?
All those points, well, there is a straight line between them, which I guess is why “linear” is the name. But it’s kind of a sucky name because it makes you think it’s going to behave like the”linear” keyword which is boring and straight all the way through. Oh well.
You should probably read Create complex animation curves in CSS with the linear()
easing function by Bramus. Ben Holmes made a very clicky button I’ll make you click over to click.
Adam Argyle’s one-minute explanation will probably do you right, as well.
Remember when CSS nesting was coming of age and browser vendors were putting out all those polls to get you to vote on the syntax? I thought that was kinda cool, even though every single person was just like “can it be like Sass?” and they are like “no” and then they made it just like Sass.
But anyway!
I thought that’s what they were doing with animation-composition
in Bramus’ Specify how multiple animation effects should composite with animation-composition
, but it turns out he’s just showing us how to use it.
It’s a little in the weeds really, but it really is something that comes up sometimes with animation. It helps with when multiple animations affect the same property. The transform
property is notorious for making this complicated because one transform
might be rotating an element, while another is translate
moving it, but they would overwrite each other in the past, so you’d have to nest elements or something. Now you can replace
, add
, or accumulate
the values, which is really pretty cool.
My first test was this thinking it would work:
button {
position: relative;
animation-composition: accumulate;
&:hover {
translate: 0 1px;
}
&:active {
translate: 0 1px;
}
}
But nope, I guess pseudo-states like that totally replace the other, so a button isn’t hovered and active at the same time? I dunno. Honestly, it’s more useful with keyframes than transitions anyway.
OK, last thing I promise. Excellent video: Animation vs. Math.