UI Animation

Motion in an interface has exactly one job: to explain what just happened. A panel that slides in from the right tells you where it came from and where it will go when dismissed. A list item that shifts smoothly when another is deleted tells you the list changed rather than that the screen was replaced. A button that depresses tells you the tap registered. In every case the animation is answering a question the user would otherwise have to work out.

Motion that doesn't answer a question is decoration, and decoration has a cost measured in milliseconds of the user's time — every time, forever. This is why the strongest guideline in the field is short: 150–300 ms for most interface transitions, which is long enough to be perceived as continuous and short enough not to be waited on.

The technical constraint is equally simple and equally often violated. The browser can animate transform and opacity on the compositor, without touching layout or paint. Animating anything else — width, top, margin — forces work on the main thread every frame and produces jank on exactly the devices where smoothness matters most.

TL;DR

Quick Example

A dialog with correct easing, compositor-friendly properties, and a reduced-motion fallback:

Note that the exit is faster than the entrance. Users want to see something appear; they don't want to wait to see it leave. That asymmetry is one of the highest-value small refinements in interface motion.

Core Concepts

Duration

Larger distances and larger elements justify slightly longer durations — a full-screen sheet moving 800px at 200ms feels violent. Small elements moving small distances should be fast. A useful heuristic: keep perceived velocity roughly constant rather than duration.

Easing

Almost nothing in the physical world starts and stops instantly, which is why linear reads as artificial. The default browser ease is acceptable and unremarkable; a custom cubic-bezier with a strong ease-out is what makes motion feel designed.

What's cheap to animate

will-change: transform hints the browser to promote an element to its own layer, but it consumes memory and should be applied just before the animation and removed after — not left on permanently across many elements.

Animating layout changes with FLIP

You often need to animate something that is a layout change — a card moving position when a list reorders. FLIP does it without animating layout properties:

The element jumps instantly to its final position, is transformed back to where it looked like it was, and then animates that transform to zero. The browser only ever animates a transform.

The View Transitions API

The modern browser-native version of the same idea, and dramatically less code:

The browser snapshots before and after, and animates between them — including elements that moved, resized, or changed position in the DOM. It works for same-document updates and, in supporting browsers, cross-document navigation. See View Transitions.

Spring physics

Duration-based curves have a problem with interactive motion: if a user interrupts an animation halfway, restarting a 300ms curve produces a visible discontinuity. Springs are defined by physical parameters and handle interruption naturally, because they carry velocity.

Springs are the right choice for drag interactions, gesture-driven motion, and anything a user can interrupt. Duration curves remain fine for discrete state changes like a dialog opening.

Reduced Motion

⚠️ Warning: For users with vestibular disorders, large-scale motion — parallax, zooming, sliding full-screen transitions — can cause genuine nausea, dizziness, and migraine. prefers-reduced-motion is not a stylistic preference; it's an accessibility requirement, and around 35% of adults over 40 have some degree of vestibular dysfunction.

Reduced motion doesn't mean no motion. A cross-fade or an instant change still communicates that something happened; what to remove is large positional movement, parallax, and scale. Removing all feedback makes the interface feel broken, which serves nobody.

Best Practices

Make every animation answer a question

"Where did this come from?" "Did my click register?" "What changed in this list?" If an animation isn't answering one of those, it's costing the user time for nothing. This is the filter that separates motion design from motion decoration.

Animate only transform and opacity

This one rule prevents most animation performance problems. Where you need to animate size, use scale and accept the visual difference, or use FLIP. Where you need to animate position, use translate rather than top/left.

Make exits faster than entrances

Roughly 2:3 — a 200ms entrance with a 130ms exit. Users are willing to watch something arrive and are not willing to wait for it to leave.

Tokenize durations and easings

Three durations and three easing curves cover almost everything. Ad-hoc values produce an interface where nothing quite matches, in a way users perceive as unpolished without being able to name it. See Design Tokens.

Stagger lists, but cap the total

A 30–50ms stagger reads as a coordinated sequence. Applied to fifty items it means the last one appears 2.5 seconds later, which is unacceptable — cap the stagger after the first 6–10 items.

Respect reduced motion per component

A global transition-duration: 0.01ms override is a reasonable safety net and a blunt one — it also kills color transitions and focus indicators that aren't motion at all. Handling it per component lets you keep the useful feedback and remove the vestibular triggers.

Test on a slow device with throttling

Animation that's smooth on a development machine can be visibly janky on a mid-range phone. Chrome DevTools' 4× CPU throttle plus the Performance panel's frame chart shows you which frames are being dropped and why.

Don't animate on page load

Entrance animations for content that was already there when the page loaded delay the user's first interaction and provide no information. Animate transitions between states, not the initial render.

Common Mistakes

Animating layout properties

Ignoring reduced motion

Note the inversion: opting into motion when no preference is expressed is safer than opting out, because it fails closed.

Animations that are too long

transition: all

Non-interruptible animation

Staggering an unbounded list

FAQ

CSS transitions, Web Animations API, or a library?

CSS transitions for state-driven changes — hover, focus, open/closed. They're declarative, run off the main thread, and need no JavaScript. The Web Animations API when you need to compute keyframes at runtime, control playback, or implement FLIP. A library (Motion, GSAP) when you need springs, gesture-driven motion, orchestrated sequences, or layout animations — the abstraction earns its bundle size for genuinely complex motion and not for a fade.

How do I animate height: auto?

You can't directly with a transition, and there are three workable answers. Modern CSS supports interpolate-size: allow-keywords with transition-behavior: allow-discrete, which makes height: auto animatable in supporting browsers. Otherwise: animate grid-template-rows from 0fr to 1fr (a widely used trick that works today), or measure the height in JavaScript and animate to an explicit pixel value.

What frame rate should I target?

60 fps means 16.6ms per frame; 120 fps displays halve that. Compositor-only animations (transform, opacity) generally hit it without effort because they bypass the main thread entirely. The Performance panel's frame chart shows dropped frames — and if you're dropping them on a simple transition, you're almost certainly animating a layout property.

Do animations hurt Core Web Vitals?

They can. Animating layout properties contributes to INP by occupying the main thread when the user is trying to interact, and content that animates into place after load can contribute to CLS if it isn't reserved space. Compositor-only animations are essentially free from a Vitals perspective. See Core Web Vitals.

How much motion is too much?

If a user notices the animation rather than the content, it's too much. A practical test: use the interface for ten minutes and see whether any animation has become tiresome. Motion that's delightful on the first encounter can be irritating on the fiftieth, and interface elements are encountered thousands of times.

Should page transitions be animated?

Sparingly, and briefly. A 150–200ms cross-fade smooths the change without adding perceptible delay. Elaborate page transitions add latency to every navigation and are one of the more common ways an app feels slower than it is. The View Transitions API makes tasteful versions much easier, which is both an opportunity and a temptation.

Related Topics

References