An Interactive Guide to CSS Transitions

The world of web animations has become a sprawling jungle of tools and technologies. Libraries like GSAP and Framer Motion and React Spring have sprung up to help us add motion to the DOM.

The most fundamental and critical piece, though, is the humble CSS transition. It’s the first animation tool that most front-end devs learn, and it’s a workhorse. Even the most grizzled, weathered animation veterans still reach for this tool often.

There’s a surprising amount of depth to this topic. In this tutorial, we’ll dig in and learn a bit more about CSS transitions, and how we can use them to create lush, polished animations.

Link to this heading

ease-out comes charging in like a wild bull, but it runs out of energy. By the end, it’s pootering along like a sleepy turtle.

Try scrubbing with the timeline; notice how drastic the movement is in the first few frames, and how subtle it becomes towards the end.

If we were to graph the displacement of the element over time, it’d look something like this:


When would you use ease-out? It’s most commonly used when something is entering from off-screen (eg. a modal appearing). It produces the effect that something came hustling in from far away, and settles in front of the user.

Link to this heading

ease-in, unsurprisingly, is the opposite of ease-out. It starts slow and speeds up:

As we saw, ease-out is useful for things that enter into view from offscreen. ease-in, naturally, is useful for the opposite: moving something beyond the bounds of the viewport.


This combo is useful when something is entering and exiting the viewport, like a modal. We’ll look at how to mix and match timing functions shortly.

Note that ease-in is pretty much exclusively useful for animations that end with the element offscreen or invisible; otherwise, the sudden stop can be jarring.

Link to this heading

Next up, ease-in-out. It’s the combination of the previous two timing functions:

This timing function is symmetrical. It has an equal amount of acceleration and deceleration.


I find this curve most useful for anything that happens in a loop (eg. an element fading in and out, over and over).

It’s a big step-up over linear, but before you go slapping it on everything, let’s look at one more option.

Link to this heading

If I had a bone to pick with the CSS language authors when it comes to transitions, it’s that ease is poorly named. It isn’t descriptive at all; literally all timing functions are eases of one sort or another!

That nitpick aside, ease is awesome. Unlike ease-in-out, it isn’t symmetrical; it features a brief ramp-up, and a lot of deceleration.

ease is the default value — if you don’t specify a timing function, ease gets used. Honestly, this feels right to me. ease is a great option in most cases. If an element moves, and isn’t entering or exiting the viewport, ease is usually a good choice.


Link to this heading
Custom curves

If the provided built-in options don’t suit your needs, you can define your own custom easing curve, using the cubic bézier timing function!

All of the values we’ve seen so far are really just presets for this cubic-bezier function. It takes 4 numbers, representing 2 control points.

Bézier curves are really nifty, but they’re beyond the scope of this tutorial. I’ll be writing more about them soon though!

In the meantime, you can start creating your own Bézier timing functions using this wonderful helper from Lea Verou:

Once you come up with an animation curve you’re satisfied with, click “Copy” at the top and paste it into your CSS!

You can also pick from this extended set of timing functions. Though beware: a few of the more outlandish options won’t work in CSS.

A screenshot of many different types of timing function curves

When starting out with custom Bézier curves, it can be hard to come up with a curve that feels natural. With some practice, however, this is an incredibly expressive tool.

Link to this heading
Hardware Acceleration