UI/UX Atlas
Motion & Animation Intermediate

Easing & Timing

Master the physics of motion curves and duration budgets that make UI feel alive, natural, and trustworthy — not robotic or jarring.

7 min read

Interactive example · Easing & timing

Ease-out — fast start, gentle stop. The default for UI: elements entering or responding to input.

The full lesson

Two interfaces can run the exact same animation — a modal sliding in, a card expanding — and one feels polished while the other feels cheap. The visual path is identical. The difference comes down to easing and timing: how fast the motion runs and how it accelerates through that duration. Getting these two variables right is the single highest-leverage skill in motion design.

Easing and timing are not decoration. They are communication. A fast ease-out on a notification says “this arrived quickly, pay attention.” A slow ease-in-out on a full-screen transition says “context is changing, orient yourself.” Done well, motion reinforces the cause-and-effect logic of an interface. Done poorly, it creates friction — or worse, a nagging sense that something is slightly wrong.

What Easing Actually Is

An easing function maps time (0–1) to progress (0–1). Linear motion maps them 1:1: at 50% of the duration, the element is 50% of the way to its destination. That sounds neutral, but our visual system perceives linear motion as mechanical — like a conveyor belt, not a physical object.

Every object in the real world accelerates and decelerates. Easing curves simulate that by making the mapping non-linear. The three classic named curves are:

NameBehaviorNatural feel
ease-inSlow start, fast finishObject being launched; exits
ease-outFast start, slow finishObject arriving under gravity; entrances
ease-in-outSlow start and finish, fast middleObject that must start and stop; full-screen transitions
linearConstant speedIntentionally robotic; loading bars, progress indicators

CSS exposes these through the animation-timing-function and transition-timing-function properties. The shorthand keywords map to predefined cubic-bezier() curves:

/* equivalent */
transition-timing-function: ease-out;
transition-timing-function: cubic-bezier(0, 0, 0.58, 1);

A cubic-bezier() takes four numbers — the x and y coordinates of two control handles — giving you precise control over the curve shape. Tools like the Chrome DevTools cubic-bezier editor or the linear() easing generator at linear-easing.dev let you author and preview these visually.

The Modern Alternative: Spring Physics

The CSS cubic-bezier model approximates physical motion. Spring-based easing is the real thing. A spring animation is defined by three physical properties:

  • Stiffness — how tightly the spring pulls toward the target (higher = snappier)
  • Damping — how much energy is lost each oscillation (lower = more bouncy)
  • Mass — the weight of the moving object (higher = slower, heavier feel)

Spring animations are duration-independent. The spring resolves when its velocity reaches zero, not at a fixed time. This means spring animations automatically adapt to interrupted or mid-flight animations — a critical advantage when users move quickly and animations stack.

Libraries like Framer Motion, React Spring, and Motion One expose spring physics in JavaScript. The Web Animations API does not natively support springs (as of 2026), but the linear() easing function (now baseline-available) lets you approximate spring curves with many keyframe steps:

.card {
  transition: transform 0.4s linear(
    0, 0.009, 0.035 2.1%, 0.141, 0.281 6.7%, 0.723 12.9%, 0.938 16.7%,
    1.022, 1.03, 1.022, 1 25%, 0.984, 0.985 30.2%, 1
  );
}

Duration: The Timing Half of the Equation

Easing tells motion how to move. Duration tells it how long. The two are inseparable — a great curve on a 2-second modal entrance still feels sluggish, while a snappy spring on a page-level transition feels frantic.

The Duration Budget

A practical guide for UI motion durations:

Motion typeTarget duration
Micro-interactions (toggles, checkboxes, hover effects)80–150 ms
Element entrances / exits (tooltips, dropdowns)150–250 ms
Panel transitions (drawers, sheets, modals)250–350 ms
Full-screen or page transitions300–500 ms
Emphasis animations (attention pulses, celebratory moments)400–700 ms

Below roughly 100 ms, motion registers as instantaneous — the brain does not perceive it as animation, just a state change. Above roughly 500 ms for common interactions, users start to feel like they are waiting. Reserve longer durations for large-surface transitions where spatial context is genuinely being rebuilt.

Distance Affects Perceived Speed

An element moving 8 px at 200 ms feels very different from an element moving 400 px at 200 ms. Larger travel distances need slightly longer durations to feel equally comfortable. The reverse is also true: a 12-px micro-nudge at 300 ms feels artificially slow. Match duration to the visual weight and distance of the motion.

The Linear Exception

Linear easing is not always wrong. Loading bars, progress rings, and audio waveforms benefit from linear motion because they represent continuous, rate-stable processes. Constant speed communicates “this is ongoing at a steady rate.” Using ease-in-out on a progress bar implies the loading speed itself is changing, which misleads the user.

Entering vs. Exiting Motion

Entrances and exits need different easing strategies because they serve different purposes.

Entrances introduce new content. The user’s eye needs to find and settle on the arriving element. ease-out works best: the element moves fast initially (catching the eye) and decelerates gently (giving the eye time to lock on). Pair it with a small translate or fade-in for depth.

Exits remove content the user is done with. They should be fast and get out of the way. Use ease-in (slow start, fast end) at a shorter duration than the matching entrance — typically 60–70% of the entrance duration. An exit that lingers is an exit that blocks.

/* Entering tooltip */
.tooltip[data-state="open"] {
  animation: tooltip-in 200ms cubic-bezier(0, 0, 0.3, 1);
}

/* Exiting tooltip — faster, ease-in */
.tooltip[data-state="closed"] {
  animation: tooltip-out 130ms cubic-bezier(0.4, 0, 1, 1);
}

Do

Use ease-out for entrances and ease-in for exits. Make exits 60–70% of the entrance duration so exiting content clears quickly. Test both states — an animation suite that only styles the entrance is half-finished.

Don't

Apply the same easing and duration to both entering and exiting states. Do not use slow ease-in-out on exits — the content lingers and creates a frustrating “waiting for the animation to finish” experience. Avoid symmetry for its own sake.

Motion Tokens: Systematizing Easing and Timing

Hardcoded values scattered across a codebase are the biggest source of inconsistent motion. A design system that defines motion tokens makes easing and timing decisions once, then applies them everywhere.

Motion tokens follow the same three-tier structure as color tokens (W3C DTCG format):

{
  "motion": {
    "easing": {
      "enter": { "$value": "cubic-bezier(0, 0, 0.3, 1)", "$type": "cubicBezier" },
      "exit":  { "$value": "cubic-bezier(0.4, 0, 1, 1)",  "$type": "cubicBezier" },
      "standard": { "$value": "cubic-bezier(0.4, 0, 0.2, 1)", "$type": "cubicBezier" }
    },
    "duration": {
      "instant": { "$value": "100ms", "$type": "duration" },
      "fast":    { "$value": "200ms", "$type": "duration" },
      "standard":{ "$value": "300ms", "$type": "duration" },
      "slow":    { "$value": "500ms", "$type": "duration" }
    }
  }
}

These tokens map to CSS custom properties:

:root {
  --motion-easing-enter: cubic-bezier(0, 0, 0.3, 1);
  --motion-easing-exit: cubic-bezier(0.4, 0, 1, 1);
  --motion-duration-fast: 200ms;
  --motion-duration-standard: 300ms;
}

.dialog {
  transition:
    opacity var(--motion-duration-standard) var(--motion-easing-enter),
    transform var(--motion-duration-standard) var(--motion-easing-enter);
}

Material Design’s “M3” motion system, Apple’s Human Interface Guidelines, and Fluent Design all publish their easing curves as named tokens. Even if you don’t adopt a third-party system, adopt the structure — named semantic roles for curves, not raw cubic-bezier() literals scattered across a codebase.

Reduced Motion and Accessibility

WCAG 2.2 Success Criterion 2.3.3 (Animation from Interactions, Level AAA) recommends that motion triggered by interaction can be disabled. The prefers-reduced-motion: reduce media query is the implementation hook. As of 2026, it is supported in every major browser and respected on iOS, Android, macOS, and Windows.

Reduced motion does not mean no motion. It means no motion that can trigger vestibular disorders — large parallax, spinning, zooming, or fast-moving elements. Fades and cross-dissolves are generally safe replacements.

.card {
  transition: transform 300ms var(--motion-easing-enter);
}

@media (prefers-reduced-motion: reduce) {
  .card {
    transition: opacity 150ms linear;
    /* Replace transform with a fade — still communicates state change */
  }
}

A well-regarded approach is to define motion at the prefers-reduced-motion: no-preference level — meaning motion is opt-in and safe by default, not opt-out:

@media (prefers-reduced-motion: no-preference) {
  .card {
    transition: transform 300ms var(--motion-easing-enter);
  }
}

Common Mistakes and How to Diagnose Them

The bounce that shouldn’t be there. An aggressive spring or high-overshoot cubic-bezier adds a bounce to elements like nav drawers or modals. Bounces draw attention to the container, not the content inside it. Reserve bouncy motion for playful, brand-expressive moments — not structural UI chrome.

Animation that fights the user. If a user clicks to close a panel while the open animation is still playing, the two motions collide. Springs handle interruption gracefully because they inherit current velocity. Cubic-bezier transitions do not — the element snaps to the beginning of the exit curve. For interruption-prone interactions, use a library that supports velocity-preserving interruption, or keep durations short enough that collisions are unlikely.

Easing inconsistency across a product. When different engineers implement the same interaction type differently — because there are no tokens and no system — subtle incoherence accumulates. The product feels “not quite right” even when individual screens look polished. Motion tokens eliminate this.

Animating the wrong CSS properties. width, height, top, left, margin, and padding trigger layout recalculation on every frame. This causes jank, especially on lower-end devices. The safe, compositor-only properties are transform and opacity. Use translate(), scale(), and rotate() from the transform stack, and use opacity for fades. The rule: if animating a property would cause the browser to recalculate the geometry of surrounding elements, it is the wrong property.

/* Bad: triggers layout */
.panel { transition: height 300ms ease-out; }

/* Good: compositor-only */
.panel { transition: transform 300ms var(--motion-easing-enter); }
/* Use translate(0, 100%) -> translate(0, 0) for a slide-up effect */

Putting It Together: A Reference Checklist

When reviewing any animated component, run through these questions:

  1. Does the easing match the motion’s role? Entrance = ease-out; exit = ease-in; bidirectional = ease-in-out or spring.
  2. Is the duration appropriate to the motion’s visual weight and distance?
  3. Is the exit shorter than the entrance?
  4. Does the animation only touch transform and opacity? If not, is there a layout-free alternative?
  5. Is there a prefers-reduced-motion fallback? Does it replace motion with an equally communicative fade?
  6. Are values defined as motion tokens? Are those tokens applied consistently across equivalent interactions?
  7. Does the animation interrupt gracefully? Test by clicking rapidly.