A slide-in panel looks smooth to most people. For someone with a vestibular disorder, it can bring on dizziness or nausea. Their OS already has a "Reduce motion" setting. Your CSS should listen to it.
Web HIG tip #9 (Quick #76 / HIG-A11Y-001): Respect prefers-reduced-motion — replace motion with instant state changes or minimal opacity fade.
Why it matters
Motion that's fine for one person can make another feel sick:
- People with vestibular disorders, migraines, or motion sensitivity turn on "Reduce motion" so large on-screen movement stops making them unwell
- Parallax, big slide-ins, zooming page transitions, and auto-playing carousels cause the most trouble
- If the page ignores the setting, people have no way to stop it, so they leave
- The fix is small: the browser already exposes the setting as a media query
The full HIG (§1.3, Reduced Motion & Animation Safety) makes this a mandatory HIG requirement, even though the matching WCAG criterion (2.3.3 Animation from Interactions) is Level AAA. Auto-playing carousels and looping motion also need a pause control under WCAG 2.2.2 Pause, Stop, Hide (Level A).
The rule of thumb
Ask: with "Reduce motion" turned on, does anything still slide, zoom, spin, or scroll by itself?
If yes, swap that motion for an instant change or a short fade.
/* Don't: the panel always slides in, whatever the user's setting */ .panel { transform: translateX(100%); transition: transform var(--duration-slow) var(--ease-out-momentum); } .panel.is-open { transform: none; } /* Do: keep the slide by default, and swap it for a short fade when reduced motion is on */ @media (prefers-reduced-motion: reduce) { .panel { transform: none; opacity: 0; visibility: hidden; /* a closed panel must not catch clicks or focus */ transition: opacity var(--duration-base) linear, visibility 0s linear var(--duration-base); } .panel.is-open { opacity: 1; visibility: visible; transition-delay: 0s; } } Motion started from JavaScript needs the same check:
document.querySelector("#to-pricing").addEventListener("click", () => { // Check at click time, so a setting changed mid-session is respected const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; document.querySelector("#pricing").scrollIntoView({ behavior: reduceMotion ? "instant" : "smooth", }); }); Use "instant", not "auto": "auto" follows your CSS scroll-behavior, so a site with html { scroll-behavior: smooth } would still glide.
Reduced motion doesn't mean no feedback. A button can still change color and a dialog can still fade in. What goes is the movement: sliding, scaling, bouncing, parallax, and smooth scrolling.
Do this instead
- Wrap slide, scale, and parallax effects so they're replaced under
prefers-reduced-motion: reduce - Use an instant state change or a minimal opacity fade as the reduced version, so the user still sees that something changed
- Turn off parallax, auto-playing carousels, decorative looping animations, and View Transitions morphing when reduced motion is on. Immersive storytelling surfaces have a narrow exception (§1.5), and they still need a calm equivalent
- Check the same setting in JavaScript before smooth scrolling or starting an animation
- Name the properties you animate instead of using
transition: all(Quick #85, tip #6), so it's clear what your reduced-motion rules need to cover
Quick check for your app
Turn on reduced motion: in Chrome DevTools, press Cmd/Ctrl+Shift+P, run "Show Rendering", and set "Emulate CSS media feature prefers-reduced-motion" to reduce, or use your OS setting. Then open a menu and a dialog, trigger a page transition, and click an in-page link. Nothing should slide, zoom, or smooth-scroll, and every state change should still be visible. A media query existing isn't the same as the motion actually calming down, so test it.
HIG.md §1.3 also includes a global CSS safety net that cuts animation and transition durations and turns off smooth scrolling under reduced motion. Treat it as a backstop, not a replacement for a calm version of each effect.
The Web HIG is a behavioral contract for how the web should behave, not a component library. Design systems define how it looks. The Web HIG defines how it behaves.
![Why Object.keys(user) Returns string[], Not (keyof User)[]: TypeScript Means It](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2q4w48b9bzs43tynyhsf.png)
