What you'll learn
- Animate transform/opacity and respect reduced motion
- Explain why layout properties animate poorly compared with compositor-friendly ones
- Choose short durations and consistent easings stored as tokens
- Keep ARIA state and inert in sync with animated open and closed states
Animate only transform and opacity, keep motion short and purposeful, and respect reduced motion.
Why it matters
Good motion explains what happened: a menu grows from the button that opened it, a toast slides in from where it will live, a checkmark confirms a save. Bad motion is slow, janky, or makes people feel sick. Some users with vestibular disorders get dizziness or nausea from large movement on screen, and WCAG asks that motion triggered by interaction can be turned off (Success Criterion 2.3.3, Animation from Interactions, level AAA) and that anything moving for more than five seconds can be paused (2.2.2, level A).
Motion is also a performance problem. An animation that drops frames makes an app feel broken even when it works.
Why transform and opacity
To draw a frame, the browser may do three kinds of work:
- LayoutThe rendering step in which the browser works out the size and position of every affected box.: work out the size and position of every affected box.
- Paint: fill in the pixels of those boxes.
- CompositeThe rendering step that stacks painted layers together, often on the GPU; transform and opacity can usually be animated here alone.: stack the painted layers together, often on the GPU.
Animating width, height, top, left, margin or padding changes geometry, so the browser may redo layout and paint on every frame, for the element and everything around it. Animating transform (translate, scale, rotate) and opacity can usually be handled in the composite step alone, which is cheap and smooth even on modest phones.
/* Janky: layout every frame, and 'all' animates anything that changes */
.panel { transition: all 0.8s; left: -320px; }
.panel.is-open { left: 0; }
/* Smooth: compositor-friendly properties, named explicitly */
.panel {
opacity: 0;
transform: translateY(-8px) scale(0.98);
transition-property: opacity, transform;
transition-duration: 200ms;
transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}
.panel.is-open { opacity: 1; transform: none; }Never write transition: all. It animates properties you did not intend (including layout ones), and a later change to the element can suddenly become a slow animation.
Note that modern CSS also has standalone translate, scale and rotate properties. They are composited too, but you must transition the property you actually change: transitioning transform does nothing if your code sets scale.
To animate height, such as an accordion, prefer a technique that avoids per-frame layout of the whole page: animate a grid-template-rows from 0fr to 1fr on a small container, or scale and fade the content, and keep the duration short.
Timing that feels right
- Short. Most UI transitions are 100–300 ms. Small things (a hover colour, a checkbox) are near the low end; larger moves (a sheet sliding in) near the high end. Anything over about 500 ms feels slow for a routine action.
- EasingThe acceleration curve of an animation; entering elements usually decelerate and exiting ones accelerate.. Entering elements decelerate (ease-out); exiting elements accelerate (ease-in) and can be a little faster than they entered.
- Tokens. Put durations and easings in CSS custom properties (
--motion-fast: 150ms) so the whole app moves consistently and you can tune it in one place. - Purpose. If you cannot say what an animation communicates (where something came from, what changed, that an action succeeded), remove it.
Respecting reduced motion
Operating systems have a "reduce motion" setting, exposed to CSS as a media query:
@media (prefers-reduced-motion: reduce) {
.panel {
transform: none; /* no movement */
transition-property: opacity; /* a quick fade is still fine */
transition-duration: 150ms;
}
}Reduced motion does not have to mean no feedback. Replace movement (slides, zooms, parallax, spinning) with a short fade or an instant change. Keep essential indicators such as a loading spinner, and remove decorative effects like confetti entirely.
JavaScript animations (scrolling, canvas, the Web Animations API) need the same check:
function prefersReducedMotion() {
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}
target.scrollIntoView({ behavior: prefersReducedMotion() ? "auto" : "smooth" });To test it, turn on Reduce Motion in your operating system settings, or use DevTools: Rendering panel > Emulate CSS media feature prefers-reduced-motionA media query that is true when the user's system asks for less motion, so movement should become a fade or an instant change..
Accessibility of animated components
Motion must not break the component's semantics. A menu that fades out is still in the DOM and still focusable while invisible unless you stop that. Mark the closed panel inert (or hidden after the exit finishes) so keyboard and screen reader users cannot land in an invisible menu, and keep aria-expanded on the toggle in sync with the real state.
Common mistakes
transition: all, or animatingwidth,leftorheighton large elements.- Durations of a second or more for everyday actions.
- Ignoring
prefers-reduced-motion, or handling it by disabling every transition including focus and colour feedback. - Invisible but focusable panels after a fade-out.
- Motion that only plays on page load and delays content the user came for.
Quick recap
- Animate
transformandopacity(ortranslate/scale/rotate), named explicitly; nevertransition: all. - Keep transitions short (about 100–300 ms) with consistent, tokenised easings.
- Under
prefers-reduced-motion: reduce, replace movement with a fade or an instant change, in CSS and JavaScript. - Keep semantics in sync:
aria-expanded, andinertorhiddenon closed content.
Try it
In the Do tab, rework a janky menu animation so it uses only compositor-friendly properties, respects reduced motion, and keeps its ARIA state correct.
Key terms (5)
- Layout
- The rendering step in which the browser works out the size and position of every affected box.
- Composite
- The rendering step that stacks painted layers together, often on the GPU; transform and opacity can usually be animated here alone.
- prefers-reduced-motion
- A media query that is true when the user's system asks for less motion, so movement should become a fade or an instant change.
- Easing
- The acceleration curve of an animation; entering elements usually decelerate and exiting ones accelerate.
- inert
- An attribute that makes an element and its contents unfocusable and hidden from assistive technology, used on closed panels.