Back to writing
3 min read

Motion with a purpose

Most interface animation is decoration that costs milliseconds. The good kind does exactly one thing: it explains what just happened.

A dark interface with elements in motion

There's a simple test for whether an animation deserves to exist. Remove it and ask whether anyone would be lost.

If the answer is "it'd feel drier but it still reads", that animation is decoration. There's nothing wrong with decoration — not everything has to be functional — but it's worth knowing which is which, because decoration is paid for in milliseconds and function is paid back in comprehension.

What motion is actually for

Motion in an interface does three jobs, and nothing else:

It explains origin. A modal that grows out of the button that opened it tells the user where it came from and where it will go back to. One that cross-fades in from the centre tells them nothing.

It preserves continuity. When a list reorders, watching items travel to their new position keeps the mental model intact. If they simply redraw, the user loses the item they were tracking and has to find it again.

It communicates state. A button that responds to hover confirms it's interactive before you commit. A spinner says "still here". A border turning red says "this is wrong" faster than any text.

Everything else is ornament.

The numbers that matter

After a fair amount of iteration, these are the ranges I work with:

KindDurationWhy
Hover, focus120–180msMust feel instant without feeling abrupt
Element entrance300–450msLong enough to follow with the eye
Page transition250–350msAny longer and it reads as slow
Ambient (loops)6–20sSlow enough not to compete for attention

The curve matters as much as the duration. Almost anything entering should use a curve that decelerates at the end — something like cubic-bezier(0.22, 1, 0.36, 1) — because it mimics how objects with mass actually move. linear is only for continuous rotation and progress bars.

The cost nobody measures

Here's the technical part that matters.

The browser can animate four properties without recalculating layout: transform, opacity, filter and backdrop-filter. Everything else — width, height, top, margin, padding — forces a position recalculation, and across a list of thirty items that shows.

/* Expensive: recalculates layout every frame */
.card:hover { margin-top: -4px; }

/* Cheap: composited on the GPU */
.card:hover { transform: translateY(-4px); }

On a modern laptop the difference is invisible. On a mid-range phone with fifteen tabs open, it's the difference between smooth and stuttering.

The case of ambient animation

Animations that run permanently — a rotating sphere, a breathing gradient — carry a different cost: not a spike, but a constant tax on battery.

My rule: if it's pure CSS on transform or opacity, the compositor handles it and the main thread stays free. If it needs JavaScript per frame, it probably isn't worth it. A requestAnimationFrame running indefinitely while someone reads an article is a decision that needs justifying.

What isn't negotiable

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

This isn't a courtesy. For people with vestibular disorders, on-screen motion produces real dizziness and nausea. The operating system already has the preference set; all you have to do is honour it.

And honouring it doesn't mean killing everything. It means replacing movement with an opacity change, which communicates the same thing without displacing anything.

The most common mistake

Animating the entrance of content that was already there.

When a page loads and every block fades in sequentially, the user who came to read something specific has to wait for the interface to finish introducing itself. It's the web equivalent of clearing your throat before every sentence.

Entrance animation makes sense when content appears — scrolling into a new section, a panel opening, a value arriving — not when it was already in the HTML the server sent.

A well-animated interface goes unnoticed. You notice it when it's missing, because suddenly you don't know where that thing came from.

Summary

  • If removing it confuses nobody, it's decoration. That's fine, but measure the cost.
  • transform and opacity. Think twice about the rest.
  • Decelerating curves for entrances, linear only for loops.
  • prefers-reduced-motion, always.
  • Don't animate what was already there.