CSS Animations are a great tool to have in your back pocket. If you stick to animating only certain properties, you can make your site have all sorts of fun effects while keeping it performant. You can do more than you might expect without ever needing to add any Javascript animation libraries to your bundle!

Most of what I'll mention also applies to CSS transitions, like those that might take place when hovering over an element, but we'll focus on animations in this post! CSS animations are normally specified using a combination of the animation shorthand and a keyframes declaration.

.square {
  animation: moveRightThenLeft 2s;
}

@keyframes moveRightThenLeft {
  0% {
    transform: translateX(0px);
  }
  50% {
    transform: translateX(100px);
  }    
  100% {
    transform: translateX(0px);
  }
}

The above CSS moves the .square element from its original location to a spot 100px right of that original location and then back to its original location. Since we have specified that the animation should last two seconds, the square will move right for one second (from 0% to 50% of two seconds), then turn around and move left for one second (from 50% to 100% of two seconds).

CSS keyframe declarations are defined in terms of percentages, which is useful because they can then be reused for animations of different durations. For instance, you could have an animation called fadeOut that transitions opacity from 1 to 0 that could be used across your codebase to fade elements out over different lengths of time, in one spot over two seconds and in another over 5 seconds. If you look at the CSS of the above Codepen, you'll see that you can actually combine percentage values in the same block. Since the 0% and 100% styles are the same, we can write 0%, 100% {. One final thing on keyframe declarations, sometimes you will see 0% written as from and 100% written as to. They are interchangeable.

I mentioned earlier that the animation CSS keyword is a shorthand. It is actually used to represent up to eight different animation properties, animation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, animation-fill-mode, and animation-play-state. Instead of typing animation: moveRightThenLeft 2s; up above, we could have typed animation-name: moveRightThenLeft; and animation-duration: 2s;.

Try this out by opening the above Codepen, editing the CSS, and confirming nothing about the animation has changed. Try out the from and to keywords as well! Then see if you can make the square move right, down, left, and up over two seconds, 100 pixels at a time, changing from red to orange to yellow to green to blue, ending at blue. Be careful, the way things are right now, none of the keyframe styles will apply once the animation is finished, so you should give the .square class the styles you expect it to have once the animation has run its course. You can see the desired effect in the Codepen below. No cheating! Keep the CSS tab closed until you try it out yourself. πŸ€“

Nice work πŸŽ‰! Notice how we had to specify background-color: blue directly on the .square element. There's a simple way around that using the animation-fill-mode property. This property tells the element how it should behave before the animation starts and after the animation ends. It defaults to a value of none, which indicates that keyframe styles should be applied while the animation is running. If you give it a value of forwards, then the styles for the final frame of the animation, the 100% frame, will persist after the animation finishes. Using animation-fill-mode, can you update the Codepen above to no longer specify background-color styling directly on the .square element?

animation-fill-mode also supports the values backwards and both, which just combines forwards and backwards. backwards applies the styles from the first frame of the animation to the element before the animation starts. This may not seem very useful since all of our animations so far have begun immediately, but backwards comes in handy when we start using animation-delay, since it applies the styles from the first frame even if the animation hasn't started yet. animation-delay is exactly what it sounds like. It delays starting an animation for some amount of time, or when specifying a negative value, can start the animation somewhere in the middle! Check out these fantastic animation-delay examples from Jhey to get an idea:

Can you further edit your latest Codepen to delay the start of the animation for one second, continuing to avoid the need for background-color styling directly on the .square element? Once you try it out, you can find the answer in the Codepen below.

All right, we've covered four of the eight animation properties. Four more to go! Let's talk about animation-iteration-count, the property which indicates the number of times the animation should be repeated. This can take values like 2, 1.5, and infinite, but negative values are not permitted. Fractional values indicate the animation should only be partially run, and the infinite value indicates that the animation should continue indefinitely! infinite can be a huge help when developing even a single-run animation since it allows you to watch the animation multiple times without refreshing the page.

Try out some different values, including fractional ones, for animation-iteration-count in the Codepen above. Then extend the animation to five seconds long and make it infinite like in the example below. At the end of the animation, the square will change jarringly from blue to red since there is no time between 100% and 0%, so update the code to have the blue square turn red from seconds four to five while it is in the top left corner.

animation-play-state is another of the animation properties. It accepts the values paused and running. This can be used to stop and start an animation right in the middle of its execution, perhaps in response to the click of a button.

animation-direction allows you to run an animation in reverse using reverse or change direction on every execution using alternate. Can you update the Codepen above to do one clockwise animation followed by one counterclockwise one? Answer below βœ….

animation-timing-function is in my opinion the most complicated of the animation properties. It specifies the curve over which the animation progresses from one keyframe to the next. The default is ease, which is slow at the edges but fast in the middle. Other values are linear, ease-in, ease-out, and ease-in-out. You can even specify your own curve using the cubic-bezier function, but that is outside the scope of this post! Finally, you can specify that the animation should move along in finite steps using something like steps(5, jump-start). Try out some of the different timing functions in your latest Codepen to get a feel for them and then see if you can achieve the effect below using the steps function.

Now that you know all the pieces that make up the animation shorthand, let's start using the shorthand! The browser is quite smart when interpreting the animation shorthand, and with a few exceptions, you can pretty much order the properties however you like. The browser will interpret any string it finds that is not an animation keyword as the name of your keyframes animation. If one timestamp is provided (you can use s or ms), then that will be the animation-duration. If two timestamps are provided, the second one is the animation-delay. Both of the following are equivalent implementations of the animation styles in the above Codepen. Try them out yourself to confirm!

animation: moveRight 5s 1s both infinite alternate steps(3, jump-end);
animation: 5s moveRight 1s alternate steps(3, jump-end) infinite both;

One last thing on the animation shorthand! You can specify multiple animations simply by comma-separating them. Can you take the following Codepen and update it to look like the one further down the page, adding a new keyframes declaration to fade out the square from opacity 1 to opacity .5 and back?

All right, now that you know how to animate CSS properties, let's talk about which properties can be animated and which properties should be animated, keeping performance ⚑️in mind 🧠!

All sorts of CSS properties can be animated, from width to top to color to line-height to border-width. In fact, so many properties can be animated that you can create some extremely creative animations. The full list of properties that can be animated can be found here on MDN. Many of them are represented visually on Animatable. I highly recommend checking them all out there!

However, if you're not careful about which you use, your animations can be janky and not achieve 60 frames per second. Wherever possible, you should animate only the following properties, as the browser can offload the heavy lifting of the animation to the GPU.

  • transform: translate() (move an element around the screen)
  • transform: scale() (grow or shrink an element)
  • transform: rotate() (spin an element about an axis)
  • opacity (fade an element in and out)

It may be tempting to animate top or left for an absolutely or fixed positioned element, for instance, if you need to move an element from the center of its parent to 50px from the top of its parent. However, it's not too difficult to just figure out how far the element needs to move and just translate it instead! You can reference a CSS variable --numPixelsToTranslateUp in the keyframe styles and then set that variable equal to -1 x (element.getBoundingClientRect().top - 40) before applying the animation class to the element.

If you do need to animate some other properties, do your best to avoid having your animation affect other elements on the page, as the page would have to be laid out again at every step of the animation, which would really slow down the page.

For instance, try to avoid doing something like in the following Codepen, where animating the border on the red square changes the location of the orange square.

Alternatives could be fixed or absolute positioning the red square or animating properties that do not affect layout, like outline or box-shadow:

So much can be done with CSS animations. To get an idea for some really creative implementations, check out this CSS Tricks article. You can animate pseudo-elements like :before and :after independent of the element itself, and you can even animate individual pieces of inline SVGs.

Hopefully you've learned a lot in this post! I'll leave a couple of videos below whose animations I'll hope you'll think about how to implement and which I might cover in a future post. I've had to build a few animations like these at work lately, and I'm really enjoying working on them. They are like little puzzles, and there are usually numerous ways to go about solving them!