I recently deployed a new homepage and a redesigned blog for a project called AppData. The homepage now features a "How it works" section, a work feed integrating Apify actors, and a services block with a toggle. The blog articles and the /tools pages received animated headers, text that fades in, and interactive CTA blocks.

I updated the site metadata and OG images for the blog articles. I excluded the old /build path from the sitemap since it now redirects to the founders section on the homepage, maintaining its anchor links. Part of the final launch checklist was making sure these new animations respected the operating system's reduced motion setting.

Heavy animations can cause discomfort. Operating systems have an accessibility setting to tone down movement, which browsers expose through a CSS media query. Since I had animations in plain CSS, Tailwind classes, and React state, I needed different approaches to handle it across the project.

First, I checked the animations relying on standard CSS. The blog layout has transitions that slide elements onto the screen. To disable them for users who prefer less movement, I wrapped the animation rules in a media query.

@media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } } 

This snippet targets all elements and removes the duration from transitions and animations. It keeps the final state of the animation intact so the layout does not break. It also forces standard scrolling instead of smooth scrolling.

For the CTA cards, the services toggle, and the FAQ section, I used Tailwind CSS. Tailwind has built-in modifiers for this media query. Instead of writing custom CSS, I prefixed the transition utility classes with the reduced motion variant.

<div class="transition-transform hover:scale-105 motion-reduce:transition-none motion-reduce:hover:scale-100"> Keep reading </div> 

This disables the scale effect if the user has requested reduced motion. I went through the homepage components and applied these classes anywhere an element moved or scaled on hover.

The animated headers and text fade-ins on the blog pages rely on React to calculate timing and sequence. CSS alone cannot stop the JavaScript timers from running, which could cause layout shifts or delayed rendering. I needed to read the user's preference directly inside the React components.

I wrote a custom hook that checks the media query using JavaScript.

import { useState, useEffect } from 'react'; export function useReducedMotion() { const [matches, setMatches] = useState(false); useEffect(() => { const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); setMatches(mediaQuery.matches); const handleChange = (event) => { setMatches(event.matches); }; mediaQuery.addEventListener('change', handleChange); return () => mediaQuery.removeEventListener('change', handleChange); }, []); return matches; } 

Inside the animated header component, I called the hook. If it returned true, I skipped the animation sequence and rendered the text in its final visible state.

function AnimatedHeader({ text }) { const prefersReducedMotion = useReducedMotion(); if (prefersReducedMotion) { return <h1>{text}</h1>;  } return <ComplexAnimation>{text}</ComplexAnimation>; } 

Browsers allow you to simulate the preference. In Chrome, open the developer tools, press Control+Shift+P, and type "Show Rendering". In the Rendering tab, there is a dropdown to emulate the CSS media feature prefers-reduced-motion. This made checking the pages fast across different display modes.

I verified there was no horizontal scroll introduced by the final animation states. All updated pages display correctly in the reduced motion mode.

What's next

I need to audit the older pages on the site to ensure no legacy animations slip through the new CSS rules. I also plan to configure the testing pipeline to force the reduced motion flag in a headless browser to catch visual regressions automatically.

FAQ

How do you handle reduced motion in Tailwind CSS?

You can use the motion-reduce: prefix on your utility classes. This allows you to remove transitions or animations for users who prefer reduced motion.

Can you read reduced motion preferences in React?

Yes, you can read the preference in React by using window.matchMedia inside a custom hook. This lets you skip JavaScript animation sequences and render the final state immediately.

How do you test reduced motion in the browser?

You can simulate the preference in your browser's developer tools. In Chrome, open the developer tools, find the Rendering tab, and select the option to emulate the prefers-reduced-motion media feature.

Originally published at watershowdigital.com/blog/handle-prefers-reduced-motion-css-react. Need a hand with your app or automation? WaterShow Digital fixes and builds it, fixed quote first.