Beginner’s Guide to Web Animation: Part 1, CSS

Step-by-step guide on the very basics of animating for the web.

Animation has been one of the biggest web design trends over the past five years, according to everyone from Awwwards to Forbes. Animation is also one of the reasons why I chose to make a career switch into web development two years ago (read about my coding bootcamp experience here!). Back then, I was bedazzled by neat hover effects that seemed to lift things off the page in 3D, cinematic parallax backgrounds, transforming logos. I just had to figure out how to do it myself.

But I was completely new to code and had no idea where to start. Researching the topic gave me tons of theoretical information like the “12 Principles of Animation Used by Disney”, but nothing like “3 Tech Methods for Animation and Code Examples for Each.” This is the resource I wish I had — an overview of the options and code snippets that walk through the very basics of animating for the web.

The Tools

When coding animation, you’re looking at three main options — CSS, Javascript, and SVG images. They all have their strengths and drawbacks:


Best forExamplesAdvantagesDisadvantages
CSSSimple user interface animations.

Defined transitions where the end state is known ahead of time.
Button hovers

Page transitions
Easiest to learn if you’re new to programming or animation.

Nearly effortless performance because it's optimized by the browser.
Difficult to manage timing if many animations need to happen in sequence, unlike Javascript libraries that come with tools to easily manage timelines.
JSDynamic animations based on user input or calculations that can’t be known ahead of time.

Chaining multiple sequences in a row to create things like longer, narrative animation.
Page read indicator

Character narrations
Only way to animate in response to user input (clicks, hovers, etc).

Easy to achieve consistency across browsers if using a third-party JS library.
Harder to learn than CSS.

Usually less performant than CSS.

Will need to use a third-party JS library to do complex animations and achieve better performance.

Not standardized like CSS, so may need to learn different syntax for different libraries.
SVGAnimating and manipulating images (in the form of SVG image files).

Complex graphics work.
Animating logos

Data visualization

Onboarding flows
SVGs are lossless, meaning they can scale to any size without losing image quality or crispness.Hard to get started if you don’t know how to create SVG images — would require knowledge of design tools like Illustrator.

What was hard for me to understand in the beginning was the overlap between the three. For example, SVG is animated using CSS and Javascript. Javascript is often used to trigger CSS animations. We’ll get into all the nuances, but first, let’s focus on CSS.

CSS

As you can see in the table above, CSS is the best place to start if you’re new to animation or coding. It’s easy to learn because CSS is a “declarative” language, meaning what you write is what you get — you tell the browser that a button has background color: red, and the browser figures out the rest.

It’s also nearly effortless performance because the browser can optimize certain CSS properties, which is super important (no one wants laggy animation!). There is a big caveat here, however, which I’ll explain in Part 3 on performance.

Within CSS, we have two tools available to us — transitions and keyframe animations.

CSS Transitions

This is the bread and butter of any developer’s toolkit — you want to start here and try to get as far as you can before introducing new tools. It’s easy to pick up and can pack a big punch if done right!

CSS Transitions allow you to take two states — a starting state and an end state — and decide how they should transition from one to the next. Let’s say we have a button with background-color: plum that should fade into background-color: palegreen when hovering over it. Below is what that would look like:

button {
  background-color: plum; /* starting state */
  transition-property: background-color; /* what is changing */
  transition-duration: 0.6s; /* how long it should take */
}

button:hover {
  background-color: palegreen; /* end state */
}

In order to make the transition happen, you need two things — the name of the CSS property that’ll be changing (transition-property), and how long it should take (transition-duration). In this case, we’re saying the button’s background-color should transition for 0.6 seconds.

What makes CSS Transitions powerful is that multiple properties can be transitioned at the same time. Let’s say we want the button to grow bigger as it becomes green:

button {
  background-color: plum;
  transform: scale(1); /* additional starting state */
  transition-property: background-color, transform; /* add transform to this list */
  transition-duration: 0.6s;
}

button:hover {
  background-color: palegreen;
  transform: scale(1.2); /* additional end state - grow button by 1.2x */
}

We’ve now scaffolded a basic CSS Transition. To take things to the next level, let’s incorporate an important CSS property — the transition-timing-function. You can think of this as what breathes life into animations. It allows us to specify the rate at which things should change over time (also known as easing).

For example, a car that’s approaching a stop light will start off fast and decelerate quickly over time, until it reaches a stop. This movement is similar to a type of easing called “ease-out”. So if you use this value to set transition-timing-function: ease-out, your CSS transition will run in the same way — start off fast and end slow.

Predefined easing values include “linear,” “ease-in,” and “ease-in-out”. But the absolute best way to create realistic movement is through cubic-bezier values, as we’ll use in this example. Using cubic-bezier() allows us to create custom speeds that are more realistic and interesting than the predefined values. Sites like easings.net and cubic-bezier.com are great resources that allow you to try out and copy and paste cubic-bezier values.

Let’s put it all together with a transition-timing-function that injects a little more personality into our button:

See the Pen CSS Transition by Chloe Hwang (@chofsho) on CodePen.

Last thing to point out here is the transition shorthand that’s commented out on line 10 — this is a shortcut that lets you specify the transition-property, transition-duration, transition-timing-function, and transition-delay all in one line. You’ll notice it starts with the keyword all, which is another shorthand to say you want all properties to transition. Very useful for minimizing code!


CSS Keyframe Animations

CSS keyframe animations are your next go-to for situations where a CSS transition might not quite achieve the full effect you’re looking for. Typically, that’s when you need to transition between multiple states, rather than a binary start to finish. Keyframe animations make that possible by allowing you to define multiple steps that should take place during a single animation.

Let’s go back to our button — this time, we want it to go from plum to yellow to green. This is not possible with CSS transitions but can be done with keyframes. The first step is to create an @keyframes rule that lays out how our animation should change over time, step by step:

@keyframes changeColor {
  0% {
    background-color: plum;
  }
  50% {
    background-color: lemonchiffon;
  } 
  100% {
    background-color: palegreen;
  }
}

Each rule must have a unique name — in this case, changeColor. Keyframes use percentages to indicate when something should happen, with 0% marking the beginning of the animation and 100% the end. This rule states that the animation should start out plum, turn yellow halfway through, and end up as green. What is halfway? That depends on the duration — for a 2s animation, it would be at the 1s mark; for a 1s animation, at the 0.5s mark.

The second step is to associate the @keyframes rule to the correct element and set the duration. Just like CSS transitions, there is a shorthand to declare both at the same time:

button {
  animation: changeColor 2s;
  
  /* long-hand below: */
  /* animation-name: changeColor; */
  /* animation-duration: 2s; */
}

Also like CSS transitions, you can apply an animation-timing-function that adds personality to your element and makes it come alive.

Keyframes are more useful than CSS transitions when you need granular controls over your animation. With keyframes, you can:

  • animation-iteration-count - Set the animation to run multiple times, instead of just once.
  • animation-direction - Make it run in different directions — even in reverse!
  • animation-play-state - Pause and play animations as needed
  • Animate properties you can’t with transitions, such as z-index and transform-origin.
  • Reuse keyframe rules between many elements (e.g. you can use changeColor on both a <button> and <p>).

Let’s put some of the above together — our button now pulses in an infinite loop where the animation alternates each time:

See the Pen CSS Keyframe Animation by Chloe Hwang (@chofsho) on CodePen.

**⭐️ Note:** 1. CSS transitions and keyframes cannot animate all CSS properties, such as `display` and `position`. In general, the [properties that are animatable](http://canianimate.com/) are those that accept values of numbers, lengths, percentages, and colors. 2. Be aware that the default timing function for both transitions and keyframes is `ease` (start slow, then fast, then end slowly). This can make your animation look strange if you’re not expecting it! 3. Be sure to watch out for browser compatibility — although transitions and keyframe animations are supported on all major browsers, some styles may still need [vendor prefixes](https://css-tricks.com/how-to-deal-with-vendor-prefixes/). To check which browsers support specific CSS properties, visit [caniuse.com](https://caniuse.com/).

Next Time…

The limitation with both CSS methods is that you need to be able to know and define the end state ahead of time. But what if your end state is unknowable because it depends on some user interaction, or on calculations that needs to happen on load? In Part 2, we will see how we can make our animations dynamic using Javascript.