12 tips for amazing CSS animation

(Image credit: Getty)

Done well, CSS animation can breathe life into your site and engage your users without the bloat of additional libraries. Delivering thoughtful, fluid animations that contribute meaningful depth to your site doesn’t have to be difficult – modern CSS properties now hand you nearly all of the tools you’ll need to create memorable experiences for your users.

In this article, we'll run through some top tips for how to unleash the full capabilities of interactive CSS animation. While you're here, you might also want to check out our round up of amazing CSS animation examples.

Many of these tips refer to Disney's animation principles – and indeed any decent article on creating animations would be remiss to not make mention these. Check out our dedicated article on Disney’s 12 basic principles of animation for a full guide. These were featured in Frank Thomas and Ollie Johnston’s seminal book The Illusion Of Life, and form a great starting point for animation novices.

01. Stick to the core four properties

CSS animation provides a relatively simple way to smoothly transition a large swath of properties. Good solid animated interfaces are dependent on a smooth, fluid experience, and to that end this is best delivered at 60+ FPS (frames per second). To maintain performance in our animation timelines we’ll want to try our best to limit our animated properties to a core four:

  • Scale – transform:scale(2)
  • Position – transform:translateX(50rem)
  • Rotation – transform:rotate(180deg)
  • Opacity – opacity: 0.5

Animating properties such as border radius, heights/widths or margins will affect browser layout methods, and animation of backgrounds, colours or box shadows will affect browser paint methods and drop your FPS considerably. Animating these properties can yield some interesting effects, but they should be used sparingly to maintain performance.

02. Introduce velocity

One of Disney’s core principles of animation is squash and stretch. While this might sound a bit too playful for a clean, modern website, squash and stretch are principles that instill perceived performance, that is, we are able to instill a sense of page performance that might not exactly line up with our technical metrics. By squashing or stretching elements in motion, we give them an illusion of greater (or lesser) velocity, again lending a sense of genuine fluidity to our page.

03. Get the timing right

CSS animation: CSYes

(Image credit: Adam Kuhn)

No matter what library or stack you choose to leverage, the timing and easing of your animations are crucial to providing a fluid experience that feels natural and intuitive for your users. Fortunately, we have many excellent options baked right in to CSS to help deliver a more organic feel.

CSS spec offers us some pretty great native easing functions, the most natural being ease-in-out, which allows animation timing to gain and shed velocity as the animation begins and ends. But deeper customisation is possible and sometimes necessary – and additional JS libraries aren’t always the answer.

Enter cubic-bezier. It helps to think of your animation timing as following a vertical curve, as if you were to roll a ball up or down a hill, noting how velocity shifts with the each change to degree of incline. Using the cubic-bezier function, we’re able to effectively map our animation or transition to these curves, even allowing for both anticipation – the effective wind-up or introductory frames of our animation – and follow-through, which is the final frames of animation. 

With access to these properties we’re able to give our animation more realistic physics such as elasticity. For a great visual indicator of how these functions might work (and to test them yourself), visit the excellent easings.net by Andrey Sitnik. If you have any familiarity with the curves and handles of vector drawing you will instantly recognise how cubic-bezier works.

Since CSS animation keyframes generally contain multiple segments, rarely directly moving from 1 to 100%, it’s important that you should keep in mind that any timing function other than linear will execute from beginning to end of the motion curve for each segment. 

Example: animation keyframes range from 0% – 25% – 75% – 100%, and use ease-in-out as the timing function. This means the animation will effectively ease itself in and out on three occasions. With that in mind, animation timing functions can also be changed within the keyframe chains themselves if needed. As capable as cubic-bezier is, sometimes it’s best suited for CSS transitions. Check the CodePen below. 

04. Stop and start with animation-play-state

Using the animation-play-state property, you can easily start or pause your animations. For example, you may well want to halt an animation on hover. To do this, use animation-play-state: paused. This option will offer your users a greater degree of interactivity, and in tandem with comma-separated chained animation properties or a few short lines of JavaScript we can define exactly when an animation should be active.

05. Think about accessibility 

When working on enhancing our user experience with animation we’re often not thinking about accessibility. Not everyone can enjoy motion on the web. For some users – such as those prone to seizures – it could potentially cause serious health issues. 

Fortunately, there’s an increasingly well-supported media query we can use: prefers-reduced-motion. This query works in tandem with a user’s OS preferences, allowing us to limit our animations as necessary. For example, imagine we are employing a particularly frenetic animation used to convey a sense of urgency and draw the user to a call to action. Using prefers-reduced-motion we can keep this element static.

@media (prefers-reduced-motion: reduce){
.wildlyFlailing{ animation:none; }}

A few important things to keep in mind: if animated elements are utilising an animation fill mode that defines an ending state, be sure elements are loaded at that end state when targeting motion-sensitive users. If an element relies on animation to move it into viewport position, we'll want to be sure it changes position via a two-frame stepped animation or with an 0s transition. 

06. Chain your animations for impact

One of the most overlooked capabilities of CSS animation is the option to chain your animations. That is, we can define each animation separately via comma separation, yet run them in tandem.

For instance, we can create one animation to rotate our element, a secondary animation to scale its background size, and a tertiary to adjust its opacity to fade in and out. What’s more, we can apply further properties to these animations, such as comma-separated values to control delays, timing functions, durations or even play states.

While chained animations can take a good amount of tweaking to sync up all properties in motion, they also offer us a great deal of granular control over how we choose to time and ease each animated property, creating complex motion with far fewer total lines of CSS (or necessary DOM elements).

07. Use will-change to boost performance

One excellent property that belongs in every single CSS animator’s toolkit is the little-known but very useful will-change.  There used to be a number of hacky methods you could employ to trick the browser into hardware accelerating our animated or transitioned properties – and they kinda, sorta worked. Now, however, we have a dedicated property to tap into; and will-change can lend a needed boost to your animation performance.

While there are some generalised default values for will-change out there, typically we will want to define just which property will be updated on which element – such as will-change:transform or, where chaining is necessary, will-change:border-radius, opacity.

Simply put, the will-change property tells our browser that these elements – surprise – will change. As a result, performance of these elements will generally be prioritised and hardware accelerated.

Let’s will-change all the things! No, no, no – overuse of this property, or prioritising all of your animated or transitioned elements, will place more strain on the hardware than it will offset. So remember, used sparingly and only for animations or transitions that are crucial to the experience you’re building, will-change can provide the boost you need.  

08. Animate type with variable fonts

(Image credit: Adam Kuhn)

Typography has always been tricky to properly animate. Sure, we can animate font size (a property better handled by animating scale) and colour, but with the introduction of variable fonts, we now have a whole new set of animatable options in our toolkit. 

For the uninitiated, variable fonts contain multiple font variations in a single font file, with potential variations in weight, angles, decorations and more. What’s more, these variations are all animatable in CSS using font-variation-settings. Using this, we are now able to smoothly transition from normal to italic, light to bold, or swashes to swash-less, all with the feel of a morphing SVG – because in many ways that’s exactly what’s happening here.

09. Use variables to keep things consistent

A great way to maintain consistency is to use CSS variables or preprocessor variables to define your animation timing.

:root{ timing-base: 1000;}

Setting a baseline animation or transition duration without defining a unit (seconds or milliseconds) affords us the flexibility to call this duration within a calc() function. While the duration may vary from our base CSS variable, it will always be a simple modification of this number and maintain a consistent experience.

10. Keep things natural with an arc

Again returning to Disney’s 12 Principles of Animation, we see mention of the arc. The arc in question refers to the inherently circular motion in which most natural movements occur. Very rarely in daily life do we observe motion in a straight line. And while it’s easier, and requires fewer keyframes, to animate strictly left-to-right or top-to-bottom, we can offer our users a more relatable experience by keeping this arc in mind while acknowledging that some layouts will ultimately prohibit us from animating our elements around curved paths (often creating interference with other elements).

11. Play it back

Sometimes the best way to truly get a feel for how your interface animations are shaping up is to create a screen recording. Seeing how something works is obviously a great way to make sure that your animations are working as you want. 

So how does the author of this article make sure he is getting what he wants? As a terminal oversharer of CodePen demos, he loves to create screencap GIFs of his animations. Dialing in these GIFs and getting them to loop nicely actually serves another purpose. It allows him to review the animations and diagnose any issues with timing or easing. 

12. Think about staggering

When working with looping geometric or pattern-based CSS animations, it can often be helpful to stagger our animations using animation delays. This can become problematic if the element has entered the viewport but we’re asking the user to wait for the animation to begin. We can achieve the same type of animation offset using negative delays ensuring the animation timeline is started immediately on page load, with no wait times for the user.

This article originally appeared in Web Designer magazine. Explore all of our web design articles here.

Generate speakers x 4

Learn how to build better JavaScript at GenerateJS (Image credit: Future)

Join us in April 2020 with our lineup of JavaScript superstars at GenerateJS – the conference helping you build better JavaScript. Book now at generateconf.com 

Read more:

Thank you for reading 5 articles this month* Join now for unlimited access

Enjoy your first month for just £1 / $1 / €1

*Read 5 free articles per month without a subscription

Join now for unlimited access

Try first month for just £1 / $1 / €1

Adam is a frontend web developer at Kong Inc by day, and creative code enthusiast by night. He is also a CodePen Pro.