Motion is one of the best tools a frontend engineer has. It explains where things came from, confirms that an action worked and gives a product personality. It is also one of the easiest ways to make a site uncomfortable, slow or even physically unpleasant for some people. Good motion is not just smooth; it is considerate.
Respect the system setting
Every major operating system lets users ask for reduced motion, and browsers expose it as a media query. People enable it for many reasons, including vestibular disorders, migraines and simple preference. Honour it everywhere, not just in CSS:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
In React, Framer Motion’s useReducedMotion() lets components choose a calmer variant instead of disabling animation entirely:
const reduced = useReducedMotion();
<motion.div
initial={reduced ? false : { opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
/>
“Reduced” does not have to mean “none”. Fades and colour changes are usually fine; large movement, parallax, zooming and spinning are what cause trouble.
Give motion a job
Before adding an animation, ask what it communicates. Useful motion tends to fall into a few categories:
- Orientation: a panel slides from the button that opened it.
- Feedback: a button compresses on press; a toast confirms a send.
- Continuity: a card expands into the page it links to.
- Delight: a small flourish on a key moment, used sparingly.
If an animation fits none of these, it is decoration, and decoration has to earn its cost.
Keep it fast and interruptible
Most interface transitions feel right between 150 and 400 ms. Longer is fine for rare, dramatic moments, but never for something users trigger dozens of times. Equally important: animations must never block input. If a user clicks again mid-transition, respond immediately rather than queuing behind the animation.
Prefer animating transform and opacity. They can be handled by the compositor, while animating width, top or box-shadow forces layout or paint every frame.
Be careful with scroll
Scroll-driven effects are popular, and scroll hijacking is where motion most often goes wrong. If you take over scrolling, for example to snap between full-screen sections:
- Keep one wheel gesture equal to one step, and swallow trackpad momentum so a flick does not skip three sections.
- Leave touch devices on native scrolling.
- Keep keyboard navigation working: arrows, Page Up/Down, Space, Home and End.
- Never trap focus or prevent users from reaching content.
- Make sure scroll-linked reveals finish even if the page stops early, for example with a timed fallback once the element is in view.
Never hide content behind animation
Text that starts invisible and only appears when an animation runs is fragile. If JavaScript fails, the observer never fires or the user prints the page, the content is gone. Start from a readable state where possible, and always provide a fallback so animated elements end up visible.
Test like a user
- Turn on reduced motion in your OS and use the site for five minutes.
- Navigate with only the keyboard.
- Throttle the CPU 4× in DevTools and check that transitions stay smooth.
- Try a trackpad and a mouse wheel; they produce very different event streams.
Summary
Great motion feels inevitable: it explains, confirms and delights without getting in the way. Respect the user’s settings, give every animation a purpose, keep it fast and interruptible, and make sure nothing important depends on it.
