Transitions and Animations That Don't Annoy People
Choose what to animate, how long it should take, and when to stop, with the WCAG rules for moving content. After this, motion tells the user something or it does not ship.
Course content
Sign in to save your progress.
-
Architecture That Scales
-
Modern CSS Without a Framework Crutch
-
Cascade Layers and Specificity You Control
-
-
-
Layout Systems for Real Sites
-
Complex Grids and Nested Layout Patterns
-
Container Queries When the Component Is the Breakpoint
-
-
-
Motion and Interaction Done Right
-
Transitions and Animations That Don't Annoy People
-
prefers-reduced-motion and Respecting the User
-
-
-
Performance and Delivery
-
Critical CSS and What Blocks First Paint
-
Font Loading Without Layout Jump
-
Responsive Images: srcset, sizes and Lazy Load
-
-
-
Ship and Maintain
-
Design Tokens and Theming Without Duplicating Everything
-
CSS That Survives a Redesign
-
Audit Accessibility and Visual Regressions Before Launch
-
-
- Final Check: HTML and CSS Advanced
Skip ahead: If you already limit transitions to transform and opacity, avoid transition: all, and know the WCAG rule for moving content, skip to "Rules for motion that lasts", then continue.
Motion is feedback. A button that dips slightly when pressed tells you the click registered. A panel that slides in tells you where it came from. Motion that does neither, such as a logo that bounces forever or a card that spins on hover, is decoration, and decoration is the first thing people stop tolerating. The test is simple: if you removed the animation, would the user lose information? If not, keep it short or drop it.
Pick what you animate
.btn {
transition: transform 150ms ease-out, background-color 150ms ease-out;
}
.btn:hover { background-color: #0238c4; }
.btn:active { transform: translateY(1px); }
Animate transform and opacity when you can. Changing them does not force the browser to recalculate layout. Animating width, height, top or margin does, and the whole page may reflow on every frame. Colour changes are cheap enough for hover states.
Why transition: all bites
/* Avoid */
.card { transition: all 300ms; }
/* Better */
.card { transition: box-shadow 200ms ease-out, transform 200ms ease-out; }
With all, anything that changes on that element animates, including things you did not plan. Add a border-width change to the hover state later and it animates too. List the properties you mean.
A keyframe animation that stops itself
@keyframes nudge {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(4px); }
}
.hint {
animation: nudge 600ms ease-in-out 2;
}
The last number is the iteration count. The hint plays twice and stops. animation-iteration-count: infinite on something next to body text keeps pulling the eye away from the sentence the reader is trying to finish.
Durations that fit the job
There is no official table. As a working range, 100 to 200 ms suits small feedback like hover and press, and 200 to 300 ms suits things that move across the screen, like menus and drawers. Longer than about 400 ms starts to feel like waiting. Leaving, or closing, can be a bit shorter than entering. I would rather tune these by clicking through the page than defend a number.
Rules for motion that lasts
WCAG 2.2.2 says moving content that starts automatically, lasts more than five seconds and sits beside other content needs a way to pause, stop or hide it. Carousels and looping background videos are the usual offenders. WCAG 2.3.1 sets a limit on content that flashes more than three times in one second. Both are about people who can be made ill or unable to read by the page.
⚠ Warning: Do not hide important content behind an animation. If a form field only becomes usable after a 600 ms slide, you made every user wait for a visual effect.
Common mistakes
- Using
transition: allon components, then adding a property that should snap. - Animating
heightfrom0toauto. It does not interpolate that way by default, so people reach formax-heightand then get a delay that depends on the guessed value. Newer Chrome versions can opt in withinterpolate-size, so check support before you rely on it. - Setting
will-changeon everything. It reserves memory for each element. Add it to the one element that janks, after measuring. - Making hover the only trigger. Touch and keyboard users never see it, so the information must be there without it.
💡 Tip: If an animation looks fine on your laptop and janky on a phone, record it in the DevTools Performance panel. Look for layout work on every frame. That is the sign you animated the wrong property.
What happens if you skip this
The page works, then someone with a vestibular disorder opens it and has to leave. Or the page works for everyone, but a long animation on every menu makes it feel slow, and nobody can say why.
Try it
Take one hover effect on your site. Rewrite it to animate only transform and one colour, and set a duration of 150 ms. Compare it with the old version side by side.
Which animation on your site would nobody notice if it were gone?
About
Choose what to animate, how long it should take, and when to stop, with the WCAG rules for moving content. After this, motion tells the user something or it does not ship.
- Duration
- 5 min
- Last update
- 08/11/2026
Comments (0)
Sign in to commentNo comments yet.