← All articles

An Interactive Guide to CSS Transitions

Juniper Hale · 2 min read

Part of the series Wobbly Motion

Share image
Share image

Of all the animation tools in our kit, the humble transition property is the one I reach for most. It's small, it's declarative, and when you understand its pieces, it can make an interface feel genuinely delightful.

The fundamentals

Let's start with a button that moves up when hovered:

Code PlaygroundHTML · CSS
.btn { transition: transform 250ms; } .btn:hover { transform: translateY(-10px); }

Without the transition, the button would teleport. With it, the browser interpolates between the two states. transition takes a number of values, but only two are required:

  1. The name of the property we want to animate.

  2. The duration of the animation.

.btn {
  transition:
    transform 250ms,
    opacity 400ms;
}

.btn:hover {
  transform: scale(1.2);
  opacity: 0;
}

Selecting all properties. transition-property accepts all. It saves typing, but I recommend against it: months later, somebody changes an unrelated property and an animation slips through that nobody asked for.

Timing functions

When we move an element from A to B over one second at 60fps, the browser has to work out 60 in-between positions. The timing function decides how they're spaced.

A dashed linear line and a pink ease-in-out curve plotted against time
The same journey, two different stories.

linear

Constant speed from start to finish. It looks robotic for movement, but it's perfect for things that loop forever, like a spinner.

ease-out

Comes in fast, like a ball thrown towards a wall, and gently settles. My default for anything entering the screen.†ease-in is the mirror image, and rarely what you want on its own: it makes things feel sluggish to start.

Custom curves

springy.css
.springy {  transition: transform 500ms cubic-bezier(.34, 1.56, .64, 1);}
ease
linear
spring

Hover the track →

Delays

transition-delay is how dropdowns stay open when your cursor slips off for a moment. Add a short delay on the way out, none on the way in:

.menu {
  transition: opacity 250ms;
  transition-delay: 300ms;
}
.wrapper:hover .menu {
  opacity: 1;
  transition-delay: 0ms;
}

Respect motion preferences

Some people get motion sick. Wrap anything bigger than a fade in a media query:

@media (prefers-reduced-motion: no-preference) {
  .btn { transition: transform 250ms; }
}

0 comments

  • Be the first to comment.