scroll-behavior: smooth and prefers-reduced-motion: Making Them Work Together
AI generated
{ }
@
CSS · Accessibility · Motion
scroll-behavior: smooth and prefers-reduced-motion
A scroll animation should never override a user's explicit request for less motion

scroll-behavior: smooth turns jumps to anchor targets into animated scrolling, but for people with vestibular disorders that same animation can trigger real physical symptoms. The robust pattern activates smooth scrolling only inside @media (prefers-reduced-motion: no-preference), so the operating system's explicit choice always wins.

15 min read scroll-behavior · prefers-reduced-motion matchMedia · scrollIntoView · scrollTo

1. What scroll-behavior: smooth actually does and where it applies

The CSS property scroll-behavior: smooth makes a jump to an anchor target, for example through <a href="#section">, run as an animated scroll over several hundred milliseconds instead of an abrupt jump. It is usually set on html, which makes it apply globally to every native scroll trigger within the document, including anchor navigation, keyboard scrolling with page up/down, and programmatic scrolling through certain JavaScript APIs.

At first glance the effect looks purely cosmetic, but it has a real impact on orientation: an animated scroll visually shows the user where they currently are on the page and how far the target lies, while an abrupt jump skips that spatial context entirely. For most users that is a pleasant, orientation friendly detail, but for one specific user group it is exactly the opposite.

2. Why smooth scrolling is a real problem for motion sensitive users

People with vestibular disorders or general motion sensitivity can develop real physical symptoms from large, fast scroll animations, ranging from dizziness to nausea, especially when the animation is large, fast and unexpectedly triggered. A single click on a table of contents link that scrolls through the entire page in one fast motion can be a genuine accessibility problem for this group, not just an aesthetic preference.

That is exactly why the operating system setting Reduce Motion exists, which users deliberately turn on to suppress exactly these animations system wide, and which browsers expose through the CSS media query prefers-reduced-motion: reduce. Enabling scroll-behavior: smooth globally without regard for that setting ignores a choice the user explicitly made and delivers precisely the experience the setting is meant to protect against.

3. Detecting prefers-reduced-motion and using it as a switch

The prefers-reduced-motion media query has two values: no-preference when the user has made no particular choice, and reduce when motion reduction is active at the operating system level. The order of the logic matters: instead of setting smooth as the default and removing it again under reduce, the more robust approach is to stay on the browser default auto by default and activate smooth only inside @media (prefers-reduced-motion: no-preference).

This difference is more than a matter of taste: browsers without support for the media query ignore the whole rule and stay on the safe auto behavior, while with the reversed order, a non supporting but still motion sensitive user could accidentally get smooth scrolling once some other browser fallback kicks in. Using no-preference as the activation condition is therefore the safer default stance.


/* Default: browser's native, instant scroll behavior */
html {
  scroll-behavior: auto;
}

/* Only opt in to smooth scrolling when the user has not
   requested reduced motion */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

4. The robust @media pattern: activating smooth only under no-preference

The robust base pattern combines the media query with a deliberately chosen default: scroll-behavior: auto is the safe starting point for html, because it matches the actual user intent as long as no explicit preference for animation exists. Only inside @media (prefers-reduced-motion: no-preference) is smooth layered on top, which makes the behavior automatically adapt to any change in the operating system setting, with no JavaScript and no page reload required.

For projects that also want to offer a manual in-app toggle for animations, the same pattern can be paired with a data attribute that JavaScript flips once the user changes the app setting. The media query stays the primary, automatic source of truth, while the manual toggle serves only as an additional, deliberate override for users who want a different preference than their operating system.


html {
  scroll-behavior: auto;
}

@media (prefers-reduced-motion: no-preference) {
  /* data-motion="reduced" lets an in-app toggle override
     the OS preference without touching JavaScript logic elsewhere */
  html:not([data-motion="reduced"]) {
    scroll-behavior: smooth;
  }
}

html[data-motion="reduced"] {
  scroll-behavior: auto;
}

5. Anchor links and history navigation: where scroll-behavior technically applies

scroll-behavior: smooth technically applies to every scroll triggered as the result of same-document navigation: clicking an anchor link with a fragment identifier, forward and back navigation in browser history whenever the target fragment changes, and keyboard navigation with page up, page down, home and end, as long as the focused context is the document itself. The property therefore covers not just clickable links but the document's entire native scroll behavior.

What it does not cover is any scrolling triggered purely by mouse wheel, trackpad gesture or touch swipe, because that kind of scrolling is already continuous and user driven and needs no discrete jump animation. Anyone worried that scroll-behavior: smooth would artificially slow down normal scrolling can rest easy: the property only affects jumps to a specific target triggered by navigation or code.


<nav>
  <a href="#introduction">Introduction</a>
  <a href="#main-section">Main Section</a>
  <a href="#conclusion">Conclusion</a>
</nav>

<section id="introduction">...</section>
<section id="main-section">...</section>
<section id="conclusion">...</section>

<style>
/* These anchor jumps automatically become smooth once
   scroll-behavior: smooth is active on <html> */
html {
  scroll-behavior: auto;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}
</style>

6. Wiring scrollIntoView() and scrollTo() correctly into the pattern

Programmatic scrolling from JavaScript through element.scrollIntoView() or window.scrollTo() only respects scroll-behavior: smooth on html automatically when no own behavior argument is passed, or when behavior: 'auto' is set explicitly. If the JavaScript call instead hardcodes behavior: 'smooth', that call overrides any CSS setting and, with it, ignores prefers-reduced-motion entirely, defeating the whole CSS based safeguard.

The clean solution is to decide motion behavior in exactly one place, either purely through CSS and JavaScript calls without an explicit behavior, or, if JavaScript has to make the decision, to query the same media query there too via window.matchMedia('(prefers-reduced-motion: reduce)') and translate the result consistently into the behavior value, instead of maintaining two parallel, contradictory sources of truth.


const prefersReducedMotion = window.matchMedia(
  "(prefers-reduced-motion: reduce)"
).matches;

document
  .querySelector("#back-to-top")
  .addEventListener("click", () => {
    window.scrollTo({
      top: 0,
      behavior: prefersReducedMotion ? "auto" : "smooth",
    });
  });

7. Exceptions: when a short animation is still reasonable

Not every animation has to disappear completely once prefers-reduced-motion: reduce is active. The specification and common practice distinguish between large, spatially extensive movements, such as scrolling through an entire long page, and small, short transitions, such as a tooltip gently fading in and out over a few pixels and milliseconds, which usually remain unproblematic even for motion sensitive users.

For scroll interactions a more nuanced view is therefore worthwhile: a jump to a section far away on the page should genuinely happen instantly under reduced motion, while a very short, few pixel corrective scroll, for example nudging a form field just into the visible viewport, is in many cases still acceptable even with reduce active. When in doubt, though, the rule is to err on the side of caution rather than carelessness with the user's preference.

8. Testing: operating system setting, DevTools emulation and manual checks

The Reduce Motion setting can be found on Windows under Accessibility settings, on macOS under Accessibility settings, Display, and on most mobile operating systems in their respective accessibility menus as well. Once enabled, every modern browser evaluates the CSS media query correctly without requiring a page reload, because the computed value of the media query updates live alongside the system setting.

For development, Chromium and Firefox DevTools offer an emulation of prefers-reduced-motion directly in the rendering panel, letting you test the behavior without permanently changing your own system setting. It is still worth a short manual test with the system setting genuinely enabled, because individual browser engines do not always handle every edge case of the emulation exactly like the real operating system setting.


/* Quick visual debug helper: highlights every element whose
   animation/transition is currently suppressed by the OS setting */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

9. Best practices for accessible scroll behavior

Accessible scroll behavior ultimately needs no complicated logic, just a consistent order: auto by default, smooth only inside @media (prefers-reduced-motion: no-preference), and every JavaScript call that sets its own behavior wired into the same preference check. That keeps a single, consistent source of truth for the page's entire scroll behavior.

Once this pattern is established globally on html, or in a central JavaScript utility, there is no need to rethink it for every new component, as long as new scroll calls consistently avoid a hardcoded behavior: 'smooth'. That turns accessible scrolling into a one-time architectural decision instead of a recurring source of bugs with every new feature.

Trigger Covered by scroll-behavior Respects prefers-reduced-motion Recommendation
Anchor link click (#target) Yes Yes, automatically via CSS html { scroll-behavior: smooth } inside the media query
Browser back/forward with fragment Yes Yes, automatically via CSS No extra code needed
Mouse wheel, trackpad, touch swipe No Not relevant Always user driven, no animation
scrollIntoView() without behavior Yes, inherits CSS value Yes, automatically via CSS Do not pass an explicit behavior
Hardcoded scrollTo({behavior:'smooth'}) No, overrides CSS No, must be checked manually Query matchMedia yourself in JS

Mironsoft

Modern CSS, layout architecture and rendering performance

CSS that stays maintainable instead of breaking with every change?

We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.

CSS Audit

Systematically uncovering specificity issues, cascade conflicts and unused selectors.

Architecture Refactoring

Introducing cascade layers, custom properties and design tokens cleanly.

Performance Tuning

Fixing layout thrashing, expensive selectors and rendering bottlenecks.

10. Summary

scroll-behavior: smooth and prefers-reduced-motion: The Essentials at a Glance

Core idea

scroll-behavior: smooth animates jumps to anchors and programmatic scroll targets, but does not affect mouse wheel or touch scrolling.

Safe pattern

Default to auto, activate smooth only inside @media (prefers-reduced-motion: no-preference), never the other way around.

JavaScript trap

A hardcoded behavior: 'smooth' in scrollIntoView or scrollTo overrides CSS and must check the preference itself via matchMedia.

Testing

DevTools emulation for fast iteration, but finish with the real system setting on Windows, macOS and mobile devices.

11. FAQ: scroll-behavior: smooth and prefers-reduced-motion: The Essentials at a Glance

1What exactly does scroll-behavior: smooth do?
The property turns jumps to a scroll target, such as anchor links, into an animated motion over several hundred milliseconds, instead of showing the target abruptly.
2Why is smooth scrolling problematic for some users?
People with vestibular disorders can experience dizziness or nausea from fast, large scroll animations. The Reduce Motion operating system setting exists exactly for this reason.
3How do I combine scroll-behavior: smooth with prefers-reduced-motion?
Set html to scroll-behavior: auto by default and activate smooth only inside @media (prefers-reduced-motion: no-preference), never the other way around.
4Does scroll-behavior also affect normal mouse wheel scrolling?
No. The property only affects discrete jumps to a target, such as anchor links or programmatic scrolling, not continuous, user driven scrolling.
5Why does my JavaScript override the CSS setting?
A hardcoded behavior: 'smooth' in scrollIntoView() or scrollTo() ignores CSS entirely. Query the preference there too with window.matchMedia.
6Do I have to remove every animation entirely under reduced motion?
Not necessarily. Large, spatially extensive movements should be removed, very short, small transitions are often still acceptable. When in doubt, reduce more cautiously.
7How do I test prefers-reduced-motion without changing my system setting?
Chromium and Firefox DevTools offer an emulation of the media query in the rendering panel, ideal for fast iteration during development.
8Where do I find the Reduce Motion setting?
On Windows under Accessibility settings, on macOS under Accessibility settings, Display, and on mobile operating systems in the accessibility menu as well.
9Can I offer users their own in-app toggle in addition to the system setting?
Yes, through a data attribute or CSS custom property that deliberately overrides the media query decision, while the system setting remains the primary automatic source.
10Is scroll-behavior: smooth alone enough for accessibility?
No, without pairing it with prefers-reduced-motion it ignores a system setting the user deliberately chose and can trigger real discomfort for motion sensitive users.