[{"data":1,"prerenderedAt":2589},["ShallowReactive",2],{"site-header-nav":3,"page-\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002F":314,"content-navigation":2437},[4,84,88,216],{"title":5,"path":6,"stem":7,"children":8},"Core Accessibility Principles For Modern Frameworks","\u002Fcore-accessibility-principles-for-modern-frameworks","core-accessibility-principles-for-modern-frameworks",[9,12,18,24,36,48,66,78],{"title":10,"path":6,"stem":11},"Core Accessibility Principles for Modern Frameworks","core-accessibility-principles-for-modern-frameworks\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Accessible Color Contrast & Theming","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Faccessible-color-contrast-theming","core-accessibility-principles-for-modern-frameworks\u002Faccessible-color-contrast-theming\u002Findex",[17],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Accessible Form Validation & Error States","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Faccessible-form-validation-error-states","core-accessibility-principles-for-modern-frameworks\u002Faccessible-form-validation-error-states\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"Focus Management Strategies for SPAs","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Ffocus-management-strategies-for-spas","core-accessibility-principles-for-modern-frameworks\u002Ffocus-management-strategies-for-spas\u002Findex",[29,30],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"Handling Focus Restoration After Dynamic Route Changes","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Ffocus-management-strategies-for-spas\u002Fhandling-focus-restoration-after-dynamic-route-changes","core-accessibility-principles-for-modern-frameworks\u002Ffocus-management-strategies-for-spas\u002Fhandling-focus-restoration-after-dynamic-route-changes\u002Findex",[35],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Keyboard Navigation Patterns for Modals","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fkeyboard-navigation-patterns-for-modals","core-accessibility-principles-for-modern-frameworks\u002Fkeyboard-navigation-patterns-for-modals\u002Findex",[41,42],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Building Accessible Dropdowns Without External UI Kits","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fkeyboard-navigation-patterns-for-modals\u002Fbuilding-accessible-dropdowns-without-external-ui-kits","core-accessibility-principles-for-modern-frameworks\u002Fkeyboard-navigation-patterns-for-modals\u002Fbuilding-accessible-dropdowns-without-external-ui-kits\u002Findex",[47],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Reduced Motion & Animation Accessibility","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility","core-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Findex",[53,54,60],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Accessible Loading Skeletons and Spinners","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Faccessible-loading-skeletons-and-spinners","core-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Faccessible-loading-skeletons-and-spinners\u002Findex",[59],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Respecting prefers-reduced-motion in React and CSS","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Frespecting-prefers-reduced-motion-in-react-and-css","core-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Frespecting-prefers-reduced-motion-in-react-and-css\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Screen Reader Compatibility Testing","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing","core-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing\u002Findex",[71,72],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Testing ARIA Live Regions with Jest and Testing Library","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing\u002Ftesting-aria-live-regions-with-jest-and-testing-library","core-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing\u002Ftesting-aria-live-regions-with-jest-and-testing-library\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"Semantic HTML vs ARIA in Component Trees","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fsemantic-html-vs-aria-in-component-trees","core-accessibility-principles-for-modern-frameworks\u002Fsemantic-html-vs-aria-in-component-trees\u002Findex",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87},"Modern Framework Accessibility","\u002F","index",{"title":89,"path":90,"stem":91,"children":92},"React Nextjs Accessibility Patterns","\u002Freact-nextjs-accessibility-patterns","react-nextjs-accessibility-patterns",[93,96,108,132,156,162,180,204],{"title":94,"path":90,"stem":95},"React & Next.js Accessibility Patterns","react-nextjs-accessibility-patterns\u002Findex",{"title":97,"path":98,"stem":99,"children":100},"Accessible Component Libraries in React","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-component-libraries-in-react","react-nextjs-accessibility-patterns\u002Faccessible-component-libraries-in-react\u002Findex",[101,102],{"title":97,"path":98,"stem":99},{"title":103,"path":104,"stem":105,"children":106},"Building Accessible Tabs in React Without Radix UI","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-component-libraries-in-react\u002Fbuilding-accessible-tabs-in-react-without-radix-ui","react-nextjs-accessibility-patterns\u002Faccessible-component-libraries-in-react\u002Fbuilding-accessible-tabs-in-react-without-radix-ui\u002Findex",[107],{"title":103,"path":104,"stem":105},{"title":109,"path":110,"stem":111,"children":112},"Accessible Data Tables & Grids in React","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids","react-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Findex",[113,114,120,126],{"title":109,"path":110,"stem":111},{"title":115,"path":116,"stem":117,"children":118},"Accessible Pagination for React Data Tables","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Faccessible-pagination-for-react-data-tables","react-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Faccessible-pagination-for-react-data-tables\u002Findex",[119],{"title":115,"path":116,"stem":117},{"title":121,"path":122,"stem":123,"children":124},"Building a Sortable Accessible Data Table in React","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Fbuilding-a-sortable-accessible-data-table-in-react","react-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Fbuilding-a-sortable-accessible-data-table-in-react\u002Findex",[125],{"title":121,"path":122,"stem":123},{"title":127,"path":128,"stem":129,"children":130},"Virtualizing Long Lists Accessibly in React","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Fvirtualizing-long-lists-accessibly-in-react","react-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Fvirtualizing-long-lists-accessibly-in-react\u002Findex",[131],{"title":127,"path":128,"stem":129},{"title":133,"path":134,"stem":135,"children":136},"Dynamic Content & State Announcements","\u002Freact-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements","react-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Findex",[137,138,144,150],{"title":133,"path":134,"stem":135},{"title":139,"path":140,"stem":141,"children":142},"Accessible Toast Notifications in React","\u002Freact-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Faccessible-toast-notifications-in-react","react-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Faccessible-toast-notifications-in-react\u002Findex",[143],{"title":139,"path":140,"stem":141},{"title":145,"path":146,"stem":147,"children":148},"Announcing Client-Side Route Changes in React","\u002Freact-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Fannouncing-client-side-route-changes-in-react","react-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Fannouncing-client-side-route-changes-in-react\u002Findex",[149],{"title":145,"path":146,"stem":147},{"title":151,"path":152,"stem":153,"children":154},"React Context for Accessibility Preferences","\u002Freact-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Freact-context-for-global-accessibility-preferences","react-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Freact-context-for-global-accessibility-preferences\u002Findex",[155],{"title":151,"path":152,"stem":153},{"title":157,"path":158,"stem":159,"children":160},"Form Handling with React Hook Form & A11y","\u002Freact-nextjs-accessibility-patterns\u002Fform-handling-with-react-hook-form-a11y","react-nextjs-accessibility-patterns\u002Fform-handling-with-react-hook-form-a11y\u002Findex",[161],{"title":157,"path":158,"stem":159},{"title":163,"path":164,"stem":165,"children":166},"Next.js App Router Accessibility","\u002Freact-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y","react-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Findex",[167,168,174],{"title":163,"path":164,"stem":165},{"title":169,"path":170,"stem":171,"children":172},"Skip Links in Next.js App Router","\u002Freact-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Fimplementing-skip-links-in-nextjs-app-router","react-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Fimplementing-skip-links-in-nextjs-app-router\u002Findex",[173],{"title":169,"path":170,"stem":171},{"title":175,"path":176,"stem":177,"children":178},"Next.js Dynamic Imports & Keyboard Navigation","\u002Freact-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Fnextjs-dynamic-imports-and-keyboard-navigation","react-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Fnextjs-dynamic-imports-and-keyboard-navigation\u002Findex",[179],{"title":175,"path":176,"stem":177},{"title":181,"path":182,"stem":183,"children":184},"React Hooks for Accessibility","\u002Freact-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility","react-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Findex",[185,186,192,198],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189,"children":190},"Building a useAnnouncer Hook for Live Regions","\u002Freact-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Fbuilding-a-useannouncer-hook-for-live-regions","react-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Fbuilding-a-useannouncer-hook-for-live-regions\u002Findex",[191],{"title":187,"path":188,"stem":189},{"title":193,"path":194,"stem":195,"children":196},"Fixing Focus Trap Issues in React Portals","\u002Freact-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Ffixing-focus-trap-issues-in-react-portals","react-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Ffixing-focus-trap-issues-in-react-portals\u002Findex",[197],{"title":193,"path":194,"stem":195},{"title":199,"path":200,"stem":201,"children":202},"Making React useEffect Accessible for Screen Readers","\u002Freact-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Fmaking-react-useeffect-accessible-for-screen-readers","react-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Fmaking-react-useeffect-accessible-for-screen-readers\u002Findex",[203],{"title":199,"path":200,"stem":201},{"title":205,"path":206,"stem":207,"children":208},"Server Components & Client-Side Interactivity","\u002Freact-nextjs-accessibility-patterns\u002Fserver-components-client-side-interactivity","react-nextjs-accessibility-patterns\u002Fserver-components-client-side-interactivity\u002Findex",[209,210],{"title":205,"path":206,"stem":207},{"title":211,"path":212,"stem":213,"children":214},"Accessible Modals in Next.js 14 Server Components","\u002Freact-nextjs-accessibility-patterns\u002Fserver-components-client-side-interactivity\u002Fhandling-accessible-modals-in-nextjs-14-server-components","react-nextjs-accessibility-patterns\u002Fserver-components-client-side-interactivity\u002Fhandling-accessible-modals-in-nextjs-14-server-components\u002Findex",[215],{"title":211,"path":212,"stem":213},{"title":217,"path":218,"stem":219,"children":220},"Testing And Automating Accessibility","\u002Ftesting-and-automating-accessibility","testing-and-automating-accessibility",[221,224,242,260,278,296],{"title":222,"path":218,"stem":223},"Testing & Automating Accessibility","testing-and-automating-accessibility\u002Findex",{"title":225,"path":226,"stem":227,"children":228},"Accessibility Audits with Lighthouse","\u002Ftesting-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse","testing-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Findex",[229,230,236],{"title":225,"path":226,"stem":227},{"title":231,"path":232,"stem":233,"children":234},"Interpreting Lighthouse Accessibility Scores","\u002Ftesting-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Finterpreting-lighthouse-accessibility-scores","testing-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Finterpreting-lighthouse-accessibility-scores\u002Findex",[235],{"title":231,"path":232,"stem":233},{"title":237,"path":238,"stem":239,"children":240},"Setting Lighthouse CI Accessibility Budgets","\u002Ftesting-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Fsetting-lighthouse-ci-accessibility-budgets","testing-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Fsetting-lighthouse-ci-accessibility-budgets\u002Findex",[241],{"title":237,"path":238,"stem":239},{"title":243,"path":244,"stem":245,"children":246},"Automated Accessibility Testing with axe-core","\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core","testing-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Findex",[247,248,254],{"title":243,"path":244,"stem":245},{"title":249,"path":250,"stem":251,"children":252},"Catching Color Contrast Failures with axe-core","\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fcatching-color-contrast-failures-with-axe-core","testing-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fcatching-color-contrast-failures-with-axe-core\u002Findex",[253],{"title":249,"path":250,"stem":251},{"title":255,"path":256,"stem":257,"children":258},"Writing Custom axe-core Rules","\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fwriting-custom-axe-core-rules","testing-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fwriting-custom-axe-core-rules\u002Findex",[259],{"title":255,"path":256,"stem":257},{"title":261,"path":262,"stem":263,"children":264},"Component Testing with jest-axe","\u002Ftesting-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe","testing-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Findex",[265,266,272],{"title":261,"path":262,"stem":263},{"title":267,"path":268,"stem":269,"children":270},"Debugging jest-axe Violations in CI","\u002Ftesting-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Fdebugging-jest-axe-violations-in-ci","testing-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Fdebugging-jest-axe-violations-in-ci\u002Findex",[271],{"title":267,"path":268,"stem":269},{"title":273,"path":274,"stem":275,"children":276},"Testing React Components with jest-axe","\u002Ftesting-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Ftesting-react-components-with-jest-axe","testing-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Ftesting-react-components-with-jest-axe\u002Findex",[277],{"title":273,"path":274,"stem":275},{"title":279,"path":280,"stem":281,"children":282},"End-to-End Accessibility Testing with Playwright","\u002Ftesting-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright","testing-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Findex",[283,284,290],{"title":279,"path":280,"stem":281},{"title":285,"path":286,"stem":287,"children":288},"Asserting Focus Order in Playwright","\u002Ftesting-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Fasserting-focus-order-in-playwright","testing-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Fasserting-focus-order-in-playwright\u002Findex",[289],{"title":285,"path":286,"stem":287},{"title":291,"path":292,"stem":293,"children":294},"Keyboard Navigation Tests in Playwright","\u002Ftesting-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Fkeyboard-navigation-tests-in-playwright","testing-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Fkeyboard-navigation-tests-in-playwright\u002Findex",[295],{"title":291,"path":292,"stem":293},{"title":297,"path":298,"stem":299,"children":300},"Gating Accessibility in CI\u002FCD Pipelines","\u002Ftesting-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines","testing-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Findex",[301,302,308],{"title":297,"path":298,"stem":299},{"title":303,"path":304,"stem":305,"children":306},"Accessibility Regression Testing in GitHub Actions","\u002Ftesting-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Faccessibility-regression-testing-in-github-actions","testing-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Faccessibility-regression-testing-in-github-actions\u002Findex",[307],{"title":303,"path":304,"stem":305},{"title":309,"path":310,"stem":311,"children":312},"Failing Pull Requests on axe Violations","\u002Ftesting-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Ffailing-pull-requests-on-axe-violations","testing-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Ffailing-pull-requests-on-axe-violations\u002Findex",[313],{"title":309,"path":310,"stem":311},{"id":315,"title":49,"body":316,"date":2430,"description":2431,"extension":2432,"image":2430,"meta":2433,"modifiedAt":2430,"navigation":553,"noindex":2434,"path":50,"publishedAt":2430,"seo":2435,"stem":51,"updatedAt":2430,"__hash__":2436},"content\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Findex.md",{"type":317,"value":318,"toc":2418},"minimark",[319,323,337,343,368,373,397,400,405,412,415,426,432,441,443,450,475,481,597,618,729,742,855,862,872,874,878,897,1200,1221,1242,1249,1251,1255,1269,1276,1384,1387,1674,1685,1692,1694,1698,1703,1722,1924,1934,1941,1948,1950,1954,1960,1983,2194,2209,2222,2224,2228,2302,2304,2308,2320,2341,2355,2364,2379,2381,2385,2414],[320,321,49],"h1",{"id":322},"reduced-motion-animation-accessibility",[324,325,326,327,331,332,336],"p",{},"Motion is one of the most overused tools in modern framework UIs. Parallax sections, spring-physics page transitions, auto-playing carousels, and shimmering skeletons all ship by default in component libraries, yet for a meaningful slice of users they range from distracting to physically nauseating. Building on ",[328,329,10],"a",{"href":330},"\u002Fcore-accessibility-principles-for-modern-frameworks\u002F",", this guide shows how to detect the user's motion preference, honor it in both CSS and React, and design a reduced-motion ",[333,334,335],"em",{},"variant"," of your interface rather than a broken, feedback-free fallback.",[324,338,339],{},[340,341,342],"strong",{},"WCAG Success Criteria Mapped:",[344,345,346,353,358,363],"ul",{},[347,348,349],"li",{},[350,351,352],"code",{},"2.3.3 Animation from Interactions",[347,354,355],{},[350,356,357],{},"2.2.2 Pause, Stop, Hide",[347,359,360],{},[350,361,362],{},"2.3.1 Three Flashes or Below Threshold",[347,364,365],{},[350,366,367],{},"1.4.13 Content on Hover or Focus",[324,369,370],{},[340,371,372],{},"Key Implementation Objectives:",[344,374,375,378,381,391,394],{},[347,376,377],{},"Understand which motion harms vestibular and cognitive users, and what the SCs require",[347,379,380],{},"Adopt a motion-safe default so animation is opt-in, not opt-out",[347,382,383,384,387,388],{},"Read ",[350,385,386],{},"prefers-reduced-motion"," reliably in CSS and in React via ",[350,389,390],{},"matchMedia",[347,392,393],{},"Design crossfade-style reduced variants that preserve feedback without movement",[347,395,396],{},"Tame auto-playing carousels, parallax, video, spinners, and skeletons",[398,399],"hr",{},[401,402,404],"h2",{"id":403},"why-motion-harms-some-users","Why Motion Harms Some Users",[324,406,407,408,411],{},"Large or unexpected motion can trigger a vestibular response—dizziness, nausea, headaches, or disorientation—in users with vestibular disorders, which affect a significant portion of adults. The trigger is not \"animation\" in the abstract; it is specifically ",[333,409,410],{},"spatial"," movement: content sliding across large distances, scaling dramatically, rotating, or moving in parallax where foreground and background travel at different speeds. A subtle 150ms opacity fade rarely causes harm, but a full-viewport slide transition routinely does.",[324,413,414],{},"Motion also affects users beyond the vestibular system. People with attention-related and cognitive disabilities can lose their place when content shifts unexpectedly, and looping or auto-playing motion competes for attention with the task at hand. This is why WCAG addresses motion from two angles.",[324,416,417,419,420,422,423],{},[350,418,352],{}," (Level AAA) asks that motion triggered by user interaction—scrolling, hovering, clicking—can be disabled unless the motion is essential to the functionality. ",[350,421,357],{}," (Level A) requires that any moving, blinking, or auto-updating content that starts automatically and lasts more than five seconds can be paused, stopped, or hidden by the user. Together they push you toward a single principle: ",[340,424,425],{},"the user, not the designer, decides how much motion they experience.",[324,427,428,429,431],{},"The operating system already exposes that decision. macOS (\"Reduce motion\"), iOS, Windows (\"Show animations\"), Android, and GNOME all surface a system-level toggle, and browsers translate it into the ",[350,430,386],{}," media feature. Your job is to read it and respond.",[433,434,435],"blockquote",{},[324,436,437,440],{},[340,438,439],{},"Testing Hook:"," Enable \"Reduce motion\" in your OS, reload the app, and confirm that no large spatial transitions, parallax, or auto-playing loops remain. Anything still moving is a candidate for a reduced variant.",[398,442],{},[401,444,446,447,449],{"id":445},"the-prefers-reduced-motion-media-query","The ",[350,448,386],{}," Media Query",[324,451,452,454,455,458,459,462,463,466,467,470,471,474],{},[350,453,386],{}," has two states: ",[350,456,457],{},"no-preference"," (the user has expressed no preference, so motion is acceptable) and ",[350,460,461],{},"reduce"," (the user wants minimal motion). The naming matters: there is no ",[350,464,465],{},"prefers-reduced-motion: more",". You are detecting ",[333,468,469],{},"reduction",", and the safest mental model is ",[340,472,473],{},"motion-safe",": treat reduced motion as the baseline and layer richer animation on top only for users who have not asked to reduce it.",[324,476,477,478,480],{},"The naive approach scopes a ",[350,479,461],{}," query to switch things off:",[482,483,488],"pre",{"className":484,"code":485,"language":486,"meta":487,"style":487},"language-css shiki shiki-themes github-light github-dark","\u002F* Reactive approach: define motion, then strip it under reduce *\u002F\n.card {\n  transition: transform 200ms ease, box-shadow 200ms ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .card {\n    transition: none;\n  }\n}\n","css","",[350,489,490,499,510,542,548,555,564,572,586,592],{"__ignoreMap":487},[491,492,495],"span",{"class":493,"line":494},"line",1,[491,496,498],{"class":497},"sJ8bj","\u002F* Reactive approach: define motion, then strip it under reduce *\u002F\n",[491,500,502,506],{"class":493,"line":501},2,[491,503,505],{"class":504},"sScJk",".card",[491,507,509],{"class":508},"sVt8B"," {\n",[491,511,513,517,520,523,527,530,533,535,537,539],{"class":493,"line":512},3,[491,514,516],{"class":515},"sj4cs","  transition",[491,518,519],{"class":508},": transform ",[491,521,522],{"class":515},"200",[491,524,526],{"class":525},"szBVR","ms",[491,528,529],{"class":515}," ease",[491,531,532],{"class":508},", box-shadow ",[491,534,522],{"class":515},[491,536,526],{"class":525},[491,538,529],{"class":515},[491,540,541],{"class":508},";\n",[491,543,545],{"class":493,"line":544},4,[491,546,547],{"class":508},"}\n",[491,549,551],{"class":493,"line":550},5,[491,552,554],{"emptyLinePlaceholder":553},true,"\n",[491,556,558,561],{"class":493,"line":557},6,[491,559,560],{"class":525},"@media",[491,562,563],{"class":508}," (prefers-reduced-motion: reduce) {\n",[491,565,567,570],{"class":493,"line":566},7,[491,568,569],{"class":504},"  .card",[491,571,509],{"class":508},[491,573,575,578,581,584],{"class":493,"line":574},8,[491,576,577],{"class":515},"    transition",[491,579,580],{"class":508},": ",[491,582,583],{"class":515},"none",[491,585,541],{"class":508},[491,587,589],{"class":493,"line":588},9,[491,590,591],{"class":508},"  }\n",[491,593,595],{"class":493,"line":594},10,[491,596,547],{"class":508},[324,598,599,600,602,603,606,607,609,610,613,614,617],{},"This works but is fragile: every new animation you add must remember its own ",[350,601,461],{}," override, and one forgotten rule ships motion to someone who explicitly disabled it. The ",[340,604,605],{},"motion-safe default"," inverts the responsibility—animation only exists inside a ",[350,608,457],{}," query, so forgetting the query means ",[333,611,612],{},"no"," animation rather than ",[333,615,616],{},"unwanted"," animation:",[482,619,621],{"className":484,"code":620,"language":486,"meta":487,"style":487},"\u002F* Motion-safe default: no animation unless the user is OK with it *\u002F\n.card {\n  \u002F* No transition here — the resting state is static *\u002F\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  .card {\n    transition: transform 200ms ease, box-shadow 200ms ease;\n  }\n  .card:hover {\n    transform: translateY(-4px); \u002F* spatial movement — gated behind no-preference *\u002F\n  }\n}\n",[350,622,623,628,634,639,643,647,654,660,682,686,693,719,724],{"__ignoreMap":487},[491,624,625],{"class":493,"line":494},[491,626,627],{"class":497},"\u002F* Motion-safe default: no animation unless the user is OK with it *\u002F\n",[491,629,630,632],{"class":493,"line":501},[491,631,505],{"class":504},[491,633,509],{"class":508},[491,635,636],{"class":493,"line":512},[491,637,638],{"class":497},"  \u002F* No transition here — the resting state is static *\u002F\n",[491,640,641],{"class":493,"line":544},[491,642,547],{"class":508},[491,644,645],{"class":493,"line":550},[491,646,554],{"emptyLinePlaceholder":553},[491,648,649,651],{"class":493,"line":557},[491,650,560],{"class":525},[491,652,653],{"class":508}," (prefers-reduced-motion: no-preference) {\n",[491,655,656,658],{"class":493,"line":566},[491,657,569],{"class":504},[491,659,509],{"class":508},[491,661,662,664,666,668,670,672,674,676,678,680],{"class":493,"line":574},[491,663,577],{"class":515},[491,665,519],{"class":508},[491,667,522],{"class":515},[491,669,526],{"class":525},[491,671,529],{"class":515},[491,673,532],{"class":508},[491,675,522],{"class":515},[491,677,526],{"class":525},[491,679,529],{"class":515},[491,681,541],{"class":508},[491,683,684],{"class":493,"line":588},[491,685,591],{"class":508},[491,687,688,691],{"class":493,"line":594},[491,689,690],{"class":504},"  .card:hover",[491,692,509],{"class":508},[491,694,696,699,701,704,707,710,713,716],{"class":493,"line":695},11,[491,697,698],{"class":515},"    transform",[491,700,580],{"class":508},[491,702,703],{"class":515},"translateY",[491,705,706],{"class":508},"(",[491,708,709],{"class":515},"-4",[491,711,712],{"class":525},"px",[491,714,715],{"class":508},"); ",[491,717,718],{"class":497},"\u002F* spatial movement — gated behind no-preference *\u002F\n",[491,720,722],{"class":493,"line":721},12,[491,723,591],{"class":508},[491,725,727],{"class":493,"line":726},13,[491,728,547],{"class":508},[324,730,731,732,734,735,86,738,741],{},"For codebases too large to refactor every rule, a global escape hatch curtails the worst offenders while you migrate. Keep it conservative—near-zero duration rather than ",[350,733,583],{},", so JavaScript that waits on ",[350,736,737],{},"transitionend",[350,739,740],{},"animationend"," events still fires:",[482,743,745],{"className":484,"code":744,"language":486,"meta":487,"style":487},"@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important; \u002F* stop infinite loops *\u002F\n    transition-duration: 0.01ms !important;\n    scroll-behavior: auto !important; \u002F* disable smooth-scroll jumps *\u002F\n  }\n}\n",[350,746,747,753,762,771,780,797,815,830,847,851],{"__ignoreMap":487},[491,748,749,751],{"class":493,"line":494},[491,750,560],{"class":525},[491,752,563],{"class":508},[491,754,755,759],{"class":493,"line":501},[491,756,758],{"class":757},"s9eBZ","  *",[491,760,761],{"class":508},",\n",[491,763,764,766,769],{"class":493,"line":512},[491,765,758],{"class":757},[491,767,768],{"class":504},"::before",[491,770,761],{"class":508},[491,772,773,775,778],{"class":493,"line":544},[491,774,758],{"class":757},[491,776,777],{"class":504},"::after",[491,779,509],{"class":508},[491,781,782,785,787,790,792,795],{"class":493,"line":550},[491,783,784],{"class":515},"    animation-duration",[491,786,580],{"class":508},[491,788,789],{"class":515},"0.01",[491,791,526],{"class":525},[491,793,794],{"class":525}," !important",[491,796,541],{"class":508},[491,798,799,802,804,807,809,812],{"class":493,"line":557},[491,800,801],{"class":515},"    animation-iteration-count",[491,803,580],{"class":508},[491,805,806],{"class":515},"1",[491,808,794],{"class":525},[491,810,811],{"class":508},"; ",[491,813,814],{"class":497},"\u002F* stop infinite loops *\u002F\n",[491,816,817,820,822,824,826,828],{"class":493,"line":566},[491,818,819],{"class":515},"    transition-duration",[491,821,580],{"class":508},[491,823,789],{"class":515},[491,825,526],{"class":525},[491,827,794],{"class":525},[491,829,541],{"class":508},[491,831,832,835,837,840,842,844],{"class":493,"line":574},[491,833,834],{"class":515},"    scroll-behavior",[491,836,580],{"class":508},[491,838,839],{"class":515},"auto",[491,841,794],{"class":525},[491,843,811],{"class":508},[491,845,846],{"class":497},"\u002F* disable smooth-scroll jumps *\u002F\n",[491,848,849],{"class":493,"line":588},[491,850,591],{"class":508},[491,852,853],{"class":493,"line":594},[491,854,547],{"class":508},[324,856,857,858,861],{},"This blanket rule is a floor, not a ceiling. It prevents catastrophic motion but it also kills ",[333,859,860],{},"useful"," feedback, which is why the next section is about designing a real reduced variant rather than scorching every animation to zero.",[433,863,864],{},[324,865,866,868,869,871],{},[340,867,439],{}," In Chrome DevTools, open the Rendering panel and set \"Emulate CSS media feature prefers-reduced-motion\" to ",[350,870,461],{},". Visual transitions should collapse instantly without the layout breaking.",[398,873],{},[401,875,877],{"id":876},"reading-the-preference-in-react","Reading the Preference in React",[324,879,880,881,884,885,888,889,892,893,896],{},"CSS handles declarative styling, but framework animations—",[350,882,883],{},"Framer Motion",", ",[350,886,887],{},"react-spring",", GSAP, or hand-rolled ",[350,890,891],{},"requestAnimationFrame"," loops—run in JavaScript and need the preference as a value. Read it through ",[350,894,895],{},"window.matchMedia"," and subscribe to changes so a user toggling the OS setting mid-session is respected without a reload.",[482,898,902],{"className":899,"code":900,"language":901,"meta":487,"style":487},"language-tsx shiki shiki-themes github-light github-dark","'use client';\n\nimport { useEffect, useState } from 'react';\n\nconst QUERY = '(prefers-reduced-motion: reduce)';\n\n\u002F**\n * Returns true when the user has requested reduced motion.\n * Defaults to `true` (the safe, motion-light state) so SSR and the\n * first paint never flash large animation before hydration resolves.\n *\u002F\nexport function useReducedMotion(): boolean {\n  const [reduced, setReduced] = useState(true);\n\n  useEffect(() => {\n    \u002F\u002F window only exists on the client — read inside the effect.\n    const mql = window.matchMedia(QUERY);\n    setReduced(mql.matches);\n\n    const onChange = (event: MediaQueryListEvent) => setReduced(event.matches);\n    mql.addEventListener('change', onChange); \u002F\u002F live updates if the user toggles the OS setting\n    return () => mql.removeEventListener('change', onChange);\n  }, []);\n\n  return reduced;\n}\n","tsx",[350,903,904,912,916,932,936,952,956,961,966,971,976,981,1003,1036,1041,1055,1061,1084,1093,1098,1131,1151,1175,1181,1186,1195],{"__ignoreMap":487},[491,905,906,910],{"class":493,"line":494},[491,907,909],{"class":908},"sZZnC","'use client'",[491,911,541],{"class":508},[491,913,914],{"class":493,"line":501},[491,915,554],{"emptyLinePlaceholder":553},[491,917,918,921,924,927,930],{"class":493,"line":512},[491,919,920],{"class":525},"import",[491,922,923],{"class":508}," { useEffect, useState } ",[491,925,926],{"class":525},"from",[491,928,929],{"class":908}," 'react'",[491,931,541],{"class":508},[491,933,934],{"class":493,"line":544},[491,935,554],{"emptyLinePlaceholder":553},[491,937,938,941,944,947,950],{"class":493,"line":550},[491,939,940],{"class":525},"const",[491,942,943],{"class":515}," QUERY",[491,945,946],{"class":525}," =",[491,948,949],{"class":908}," '(prefers-reduced-motion: reduce)'",[491,951,541],{"class":508},[491,953,954],{"class":493,"line":557},[491,955,554],{"emptyLinePlaceholder":553},[491,957,958],{"class":493,"line":566},[491,959,960],{"class":497},"\u002F**\n",[491,962,963],{"class":493,"line":574},[491,964,965],{"class":497}," * Returns true when the user has requested reduced motion.\n",[491,967,968],{"class":493,"line":588},[491,969,970],{"class":497}," * Defaults to `true` (the safe, motion-light state) so SSR and the\n",[491,972,973],{"class":493,"line":594},[491,974,975],{"class":497}," * first paint never flash large animation before hydration resolves.\n",[491,977,978],{"class":493,"line":695},[491,979,980],{"class":497}," *\u002F\n",[491,982,983,986,989,992,995,998,1001],{"class":493,"line":721},[491,984,985],{"class":525},"export",[491,987,988],{"class":525}," function",[491,990,991],{"class":504}," useReducedMotion",[491,993,994],{"class":508},"()",[491,996,997],{"class":525},":",[491,999,1000],{"class":515}," boolean",[491,1002,509],{"class":508},[491,1004,1005,1008,1011,1014,1016,1019,1022,1025,1028,1030,1033],{"class":493,"line":726},[491,1006,1007],{"class":525},"  const",[491,1009,1010],{"class":508}," [",[491,1012,1013],{"class":515},"reduced",[491,1015,884],{"class":508},[491,1017,1018],{"class":515},"setReduced",[491,1020,1021],{"class":508},"] ",[491,1023,1024],{"class":525},"=",[491,1026,1027],{"class":504}," useState",[491,1029,706],{"class":508},[491,1031,1032],{"class":515},"true",[491,1034,1035],{"class":508},");\n",[491,1037,1039],{"class":493,"line":1038},14,[491,1040,554],{"emptyLinePlaceholder":553},[491,1042,1044,1047,1050,1053],{"class":493,"line":1043},15,[491,1045,1046],{"class":504},"  useEffect",[491,1048,1049],{"class":508},"(() ",[491,1051,1052],{"class":525},"=>",[491,1054,509],{"class":508},[491,1056,1058],{"class":493,"line":1057},16,[491,1059,1060],{"class":497},"    \u002F\u002F window only exists on the client — read inside the effect.\n",[491,1062,1064,1067,1070,1072,1075,1077,1079,1082],{"class":493,"line":1063},17,[491,1065,1066],{"class":525},"    const",[491,1068,1069],{"class":515}," mql",[491,1071,946],{"class":525},[491,1073,1074],{"class":508}," window.",[491,1076,390],{"class":504},[491,1078,706],{"class":508},[491,1080,1081],{"class":515},"QUERY",[491,1083,1035],{"class":508},[491,1085,1087,1090],{"class":493,"line":1086},18,[491,1088,1089],{"class":504},"    setReduced",[491,1091,1092],{"class":508},"(mql.matches);\n",[491,1094,1096],{"class":493,"line":1095},19,[491,1097,554],{"emptyLinePlaceholder":553},[491,1099,1101,1103,1106,1108,1111,1115,1117,1120,1123,1125,1128],{"class":493,"line":1100},20,[491,1102,1066],{"class":525},[491,1104,1105],{"class":504}," onChange",[491,1107,946],{"class":525},[491,1109,1110],{"class":508}," (",[491,1112,1114],{"class":1113},"s4XuR","event",[491,1116,997],{"class":525},[491,1118,1119],{"class":504}," MediaQueryListEvent",[491,1121,1122],{"class":508},") ",[491,1124,1052],{"class":525},[491,1126,1127],{"class":504}," setReduced",[491,1129,1130],{"class":508},"(event.matches);\n",[491,1132,1134,1137,1140,1142,1145,1148],{"class":493,"line":1133},21,[491,1135,1136],{"class":508},"    mql.",[491,1138,1139],{"class":504},"addEventListener",[491,1141,706],{"class":508},[491,1143,1144],{"class":908},"'change'",[491,1146,1147],{"class":508},", onChange); ",[491,1149,1150],{"class":497},"\u002F\u002F live updates if the user toggles the OS setting\n",[491,1152,1154,1157,1160,1162,1165,1168,1170,1172],{"class":493,"line":1153},22,[491,1155,1156],{"class":525},"    return",[491,1158,1159],{"class":508}," () ",[491,1161,1052],{"class":525},[491,1163,1164],{"class":508}," mql.",[491,1166,1167],{"class":504},"removeEventListener",[491,1169,706],{"class":508},[491,1171,1144],{"class":908},[491,1173,1174],{"class":508},", onChange);\n",[491,1176,1178],{"class":493,"line":1177},23,[491,1179,1180],{"class":508},"  }, []);\n",[491,1182,1184],{"class":493,"line":1183},24,[491,1185,554],{"emptyLinePlaceholder":553},[491,1187,1189,1192],{"class":493,"line":1188},25,[491,1190,1191],{"class":525},"  return",[491,1193,1194],{"class":508}," reduced;\n",[491,1196,1198],{"class":493,"line":1197},26,[491,1199,547],{"class":508},[324,1201,1202,1203,1205,1206,1209,1210,1213,1214,1216,1217,1220],{},"Defaulting to ",[350,1204,1032],{}," is deliberate. On the server ",[350,1207,1208],{},"window"," does not exist, so the initial render must assume ",[333,1211,1212],{},"something","; assuming reduced motion means the worst case at first paint is a missing flourish, never an unwanted lurch for a vestibular user. The depth of the hook—the SSR caveat, the change listener, and gating ",[350,1215,883],{}," on the result—is covered in ",[328,1218,61],{"href":1219},"\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Frespecting-prefers-reduced-motion-in-react-and-css\u002F",".",[324,1222,1223,1225,1226,1229,1230,1233,1234,1237,1238,1241],{},[350,1224,883],{}," ships first-class support: its own ",[350,1227,1228],{},"useReducedMotion()"," hook reads the same media feature, and the ",[350,1231,1232],{},"MotionConfig"," component with ",[350,1235,1236],{},"reducedMotion=\"user\""," makes every nested ",[350,1239,1240],{},"motion"," element automatically drop transform and layout animations while preserving opacity. Prefer that built-in path when you already depend on the library, and reserve a custom hook for non-Framer animation.",[433,1243,1244],{},[324,1245,1246,1248],{},[340,1247,439],{}," Toggle the OS \"Reduce motion\" setting while the app is open. With the change listener wired, gated animations should switch off immediately—no refresh required.",[398,1250],{},[401,1252,1254],{"id":1253},"designing-a-reduced-motion-variant","Designing a Reduced-Motion Variant",[324,1256,1257,1258,1261,1262,1265,1266,1220],{},"The most common mistake is treating reduced motion as a kill switch. When you delete a transition entirely, you often delete the ",[333,1259,1260],{},"feedback"," it carried: a toast that slid in now pops with no indication it is new, a modal that scaled up now appears with no sense of where it came from, an accordion that animated open now teleports. Reduced motion does not mean ",[333,1263,1264],{},"no change","—it means ",[333,1267,1268],{},"no large spatial movement",[324,1270,1271,1272,1275],{},"The accessible substitute is almost always a ",[340,1273,1274],{},"crossfade",". Opacity transitions do not move content through space, so they sidestep vestibular triggers while still signaling that something changed. The decision flow below summarizes the rule of thumb.",[1277,1278,1285,1286,1285,1290,1285,1294,1285,1337,1285,1371],"svg",{"role":1279,"ariaLabelledBy":1280,"viewBox":1283,"style":1284},"img",[1281,1282],"rmTitle","rmDesc","0 0 760 300","width:100%;height:auto;max-width:760px","\n  ",[1287,1288,1289],"title",{"id":1281},"Reduced-motion decision flow",[1291,1292,1293],"desc",{"id":1282},"A flow chart: check prefers-reduced-motion. If no-preference, use the full motion variant with transforms and slides. If reduce, branch to essential-only motion or a crossfade rather than removing feedback entirely.",[1295,1296,1299,1300,1299,1308,1299,1314,1299,1317,1299,1323,1299,1327,1299,1330,1299,1333,1285],"g",{"style":1297,"fill":583,"stroke":1298},"stroke-width:2","currentColor","\n    ",[1301,1302],"rect",{"x":1303,"y":1304,"width":522,"height":1305,"rx":1306,"fill":1307},"280","20","56","8","var(--primary-soft)",[1301,1309],{"x":1310,"y":1311,"width":522,"height":1312,"rx":1306,"fill":1313},"40","150","64","var(--surface)",[1301,1315],{"x":1316,"y":1311,"width":522,"height":1312,"rx":1306,"fill":1313},"520",[1301,1318],{"x":1319,"y":1320,"width":1321,"height":1322,"rx":1306,"fill":1307},"268","236","224","48",[1324,1325],"path",{"d":1326},"M340 76 L150 150",[1324,1328],{"d":1329},"M420 76 L620 150",[1324,1331],{"d":1332},"M620 214 L420 236",[1324,1334],{"style":1335,"d":1336},"stroke-dasharray:6 5","M620 214 L468 236",[1295,1338,1299,1341,1299,1347,1299,1353,1299,1357,1299,1361,1299,1364,1299,1367,1285],{"style":1339,"fill":1340},"font-size:15px;text-anchor:middle","var(--text)",[1342,1343,1346],"text",{"x":1344,"y":1345},"380","44","prefers-reduced-motion?",[1342,1348,1352],{"style":1349,"x":1344,"y":1350,"fill":1351},"font-size:13px","62","var(--muted)","read in CSS & React",[1342,1354,457],{"x":1355,"y":1356},"140","178",[1342,1358,1360],{"style":1349,"x":1355,"y":1359,"fill":1351},"198","full motion: slides, scale",[1342,1362,461],{"x":1363,"y":1356},"620",[1342,1365,1366],{"style":1349,"x":1363,"y":1359,"fill":1351},"essential-only motion",[1342,1368,1370],{"x":1344,"y":1369},"266","crossfade, keep feedback",[1295,1372,1299,1375,1299,1380,1285],{"style":1373,"fill":1374},"font-size:12px;text-anchor:middle","var(--primary-strong)",[1342,1376,1379],{"x":1377,"y":1378},"235","120","rich",[1342,1381,1383],{"x":1382,"y":1378},"540","minimal",[324,1385,1386],{},"In React this becomes a branch on the hook's value—same component, two presentation paths:",[482,1388,1390],{"className":899,"code":1389,"language":901,"meta":487,"style":487},"'use client';\n\nimport { motion } from 'framer-motion';\nimport { useReducedMotion } from '.\u002FuseReducedMotion';\n\nexport function Toast({ children }: { children: React.ReactNode }) {\n  const reduced = useReducedMotion();\n\n  \u002F\u002F Full variant slides up; reduced variant only crossfades — feedback survives,\n  \u002F\u002F spatial movement does not. (Honors 2.3.3 Animation from Interactions.)\n  const variants = reduced\n    ? { hidden: { opacity: 0 }, visible: { opacity: 1 } }\n    : { hidden: { opacity: 0, y: 24 }, visible: { opacity: 1, y: 0 } };\n\n  return (\n    \u003Cmotion.div\n      role=\"status\"          \u002F\u002F announce the toast to assistive tech\n      initial=\"hidden\"\n      animate=\"visible\"\n      variants={variants}\n      transition={{ duration: reduced ? 0.12 : 0.25 }}\n    >\n      {children}\n    \u003C\u002Fmotion.div>\n  );\n}\n",[350,1391,1392,1398,1402,1416,1430,1434,1472,1486,1490,1495,1500,1512,1531,1557,1561,1568,1576,1589,1599,1609,1619,1644,1649,1654,1665,1670],{"__ignoreMap":487},[491,1393,1394,1396],{"class":493,"line":494},[491,1395,909],{"class":908},[491,1397,541],{"class":508},[491,1399,1400],{"class":493,"line":501},[491,1401,554],{"emptyLinePlaceholder":553},[491,1403,1404,1406,1409,1411,1414],{"class":493,"line":512},[491,1405,920],{"class":525},[491,1407,1408],{"class":508}," { motion } ",[491,1410,926],{"class":525},[491,1412,1413],{"class":908}," 'framer-motion'",[491,1415,541],{"class":508},[491,1417,1418,1420,1423,1425,1428],{"class":493,"line":544},[491,1419,920],{"class":525},[491,1421,1422],{"class":508}," { useReducedMotion } ",[491,1424,926],{"class":525},[491,1426,1427],{"class":908}," '.\u002FuseReducedMotion'",[491,1429,541],{"class":508},[491,1431,1432],{"class":493,"line":550},[491,1433,554],{"emptyLinePlaceholder":553},[491,1435,1436,1438,1440,1443,1446,1449,1452,1454,1457,1459,1461,1464,1466,1469],{"class":493,"line":557},[491,1437,985],{"class":525},[491,1439,988],{"class":525},[491,1441,1442],{"class":504}," Toast",[491,1444,1445],{"class":508},"({ ",[491,1447,1448],{"class":1113},"children",[491,1450,1451],{"class":508}," }",[491,1453,997],{"class":525},[491,1455,1456],{"class":508}," { ",[491,1458,1448],{"class":1113},[491,1460,997],{"class":525},[491,1462,1463],{"class":504}," React",[491,1465,1220],{"class":508},[491,1467,1468],{"class":504},"ReactNode",[491,1470,1471],{"class":508}," }) {\n",[491,1473,1474,1476,1479,1481,1483],{"class":493,"line":566},[491,1475,1007],{"class":525},[491,1477,1478],{"class":515}," reduced",[491,1480,946],{"class":525},[491,1482,991],{"class":504},[491,1484,1485],{"class":508},"();\n",[491,1487,1488],{"class":493,"line":574},[491,1489,554],{"emptyLinePlaceholder":553},[491,1491,1492],{"class":493,"line":588},[491,1493,1494],{"class":497},"  \u002F\u002F Full variant slides up; reduced variant only crossfades — feedback survives,\n",[491,1496,1497],{"class":493,"line":594},[491,1498,1499],{"class":497},"  \u002F\u002F spatial movement does not. (Honors 2.3.3 Animation from Interactions.)\n",[491,1501,1502,1504,1507,1509],{"class":493,"line":695},[491,1503,1007],{"class":525},[491,1505,1506],{"class":515}," variants",[491,1508,946],{"class":525},[491,1510,1511],{"class":508}," reduced\n",[491,1513,1514,1517,1520,1523,1526,1528],{"class":493,"line":721},[491,1515,1516],{"class":525},"    ?",[491,1518,1519],{"class":508}," { hidden: { opacity: ",[491,1521,1522],{"class":515},"0",[491,1524,1525],{"class":508}," }, visible: { opacity: ",[491,1527,806],{"class":515},[491,1529,1530],{"class":508}," } }\n",[491,1532,1533,1536,1538,1540,1543,1546,1548,1550,1552,1554],{"class":493,"line":726},[491,1534,1535],{"class":525},"    :",[491,1537,1519],{"class":508},[491,1539,1522],{"class":515},[491,1541,1542],{"class":508},", y: ",[491,1544,1545],{"class":515},"24",[491,1547,1525],{"class":508},[491,1549,806],{"class":515},[491,1551,1542],{"class":508},[491,1553,1522],{"class":515},[491,1555,1556],{"class":508}," } };\n",[491,1558,1559],{"class":493,"line":1038},[491,1560,554],{"emptyLinePlaceholder":553},[491,1562,1563,1565],{"class":493,"line":1043},[491,1564,1191],{"class":525},[491,1566,1567],{"class":508}," (\n",[491,1569,1570,1573],{"class":493,"line":1057},[491,1571,1572],{"class":508},"    \u003C",[491,1574,1575],{"class":515},"motion.div\n",[491,1577,1578,1581,1583,1586],{"class":493,"line":1063},[491,1579,1580],{"class":504},"      role",[491,1582,1024],{"class":525},[491,1584,1585],{"class":908},"\"status\"",[491,1587,1588],{"class":497},"          \u002F\u002F announce the toast to assistive tech\n",[491,1590,1591,1594,1596],{"class":493,"line":1086},[491,1592,1593],{"class":504},"      initial",[491,1595,1024],{"class":525},[491,1597,1598],{"class":908},"\"hidden\"\n",[491,1600,1601,1604,1606],{"class":493,"line":1095},[491,1602,1603],{"class":504},"      animate",[491,1605,1024],{"class":525},[491,1607,1608],{"class":908},"\"visible\"\n",[491,1610,1611,1614,1616],{"class":493,"line":1100},[491,1612,1613],{"class":504},"      variants",[491,1615,1024],{"class":525},[491,1617,1618],{"class":508},"{variants}\n",[491,1620,1621,1624,1626,1629,1632,1635,1638,1641],{"class":493,"line":1133},[491,1622,1623],{"class":504},"      transition",[491,1625,1024],{"class":525},[491,1627,1628],{"class":508},"{{ duration: reduced ",[491,1630,1631],{"class":525},"?",[491,1633,1634],{"class":515}," 0.12",[491,1636,1637],{"class":525}," :",[491,1639,1640],{"class":515}," 0.25",[491,1642,1643],{"class":508}," }}\n",[491,1645,1646],{"class":493,"line":1153},[491,1647,1648],{"class":508},"    >\n",[491,1650,1651],{"class":493,"line":1177},[491,1652,1653],{"class":508},"      {children}\n",[491,1655,1656,1659,1662],{"class":493,"line":1183},[491,1657,1658],{"class":508},"    \u003C\u002F",[491,1660,1661],{"class":515},"motion.div",[491,1663,1664],{"class":508},">\n",[491,1666,1667],{"class":493,"line":1188},[491,1668,1669],{"class":508},"  );\n",[491,1671,1672],{"class":493,"line":1197},[491,1673,547],{"class":508},[324,1675,1676,1677,1680,1681,1684],{},"The corollary: some motion is ",[333,1678,1679],{},"essential"," and may legitimately survive reduction. A progress bar that fills, a loading indicator that shows work is happening, or an animation that conveys the only available state information is exempt under ",[350,1682,1683],{},"2.3.3",". Keep those, but make them as small and non-spatial as the meaning allows.",[433,1686,1687],{},[324,1688,1689,1691],{},[340,1690,439],{}," With reduced motion on, trigger every transient UI element (toasts, modals, accordions). Each must still visibly change state—if anything appears or disappears with zero perceptible feedback, the reduced variant went too far.",[398,1693],{},[401,1695,1697],{"id":1696},"auto-playing-carousels-parallax-video","Auto-Playing Carousels, Parallax & Video",[324,1699,1700,1702],{},[350,1701,357],{}," targets content that moves on its own. Auto-advancing carousels are the canonical violation: they start automatically, loop indefinitely, and frequently lack any control to stop them. Three rules keep them compliant.",[324,1704,1705,1706,1709,1710,1713,1714,1721],{},"First, provide a visible, keyboard-operable pause\u002Fplay control with an accurate accessible name (",[350,1707,1708],{},"aria-label=\"Pause carousel\""," that flips to ",[350,1711,1712],{},"\"Play carousel\"","). Second, ",[340,1715,1716,1717,1720],{},"do not auto-advance at all when ",[350,1718,1719],{},"prefers-reduced-motion: reduce"," is set","—initialize the carousel paused and let the user step through with the controls. Third, pause on hover and on keyboard focus so a user reading a slide is never yanked to the next one.",[482,1723,1725],{"className":899,"code":1724,"language":901,"meta":487,"style":487},"'use client';\n\nimport { useEffect, useState } from 'react';\nimport { useReducedMotion } from '.\u002FuseReducedMotion';\n\nexport function useCarouselAutoplay(advance: () => void) {\n  const reduced = useReducedMotion();\n  \u002F\u002F Start paused when the user reduced motion — autoplay never begins. (2.2.2)\n  const [playing, setPlaying] = useState(!reduced);\n\n  useEffect(() => {\n    if (!playing) return;\n    const id = setInterval(advance, 6000);\n    return () => clearInterval(id);\n  }, [playing, advance]);\n\n  return { playing, setPlaying }; \u002F\u002F wire setPlaying to a labeled pause\u002Fplay button\n}\n",[350,1726,1727,1733,1737,1749,1761,1765,1791,1803,1808,1836,1840,1850,1867,1887,1901,1906,1910,1920],{"__ignoreMap":487},[491,1728,1729,1731],{"class":493,"line":494},[491,1730,909],{"class":908},[491,1732,541],{"class":508},[491,1734,1735],{"class":493,"line":501},[491,1736,554],{"emptyLinePlaceholder":553},[491,1738,1739,1741,1743,1745,1747],{"class":493,"line":512},[491,1740,920],{"class":525},[491,1742,923],{"class":508},[491,1744,926],{"class":525},[491,1746,929],{"class":908},[491,1748,541],{"class":508},[491,1750,1751,1753,1755,1757,1759],{"class":493,"line":544},[491,1752,920],{"class":525},[491,1754,1422],{"class":508},[491,1756,926],{"class":525},[491,1758,1427],{"class":908},[491,1760,541],{"class":508},[491,1762,1763],{"class":493,"line":550},[491,1764,554],{"emptyLinePlaceholder":553},[491,1766,1767,1769,1771,1774,1776,1779,1781,1783,1785,1788],{"class":493,"line":557},[491,1768,985],{"class":525},[491,1770,988],{"class":525},[491,1772,1773],{"class":504}," useCarouselAutoplay",[491,1775,706],{"class":508},[491,1777,1778],{"class":504},"advance",[491,1780,997],{"class":525},[491,1782,1159],{"class":508},[491,1784,1052],{"class":525},[491,1786,1787],{"class":515}," void",[491,1789,1790],{"class":508},") {\n",[491,1792,1793,1795,1797,1799,1801],{"class":493,"line":566},[491,1794,1007],{"class":525},[491,1796,1478],{"class":515},[491,1798,946],{"class":525},[491,1800,991],{"class":504},[491,1802,1485],{"class":508},[491,1804,1805],{"class":493,"line":574},[491,1806,1807],{"class":497},"  \u002F\u002F Start paused when the user reduced motion — autoplay never begins. (2.2.2)\n",[491,1809,1810,1812,1814,1817,1819,1822,1824,1826,1828,1830,1833],{"class":493,"line":588},[491,1811,1007],{"class":525},[491,1813,1010],{"class":508},[491,1815,1816],{"class":515},"playing",[491,1818,884],{"class":508},[491,1820,1821],{"class":515},"setPlaying",[491,1823,1021],{"class":508},[491,1825,1024],{"class":525},[491,1827,1027],{"class":504},[491,1829,706],{"class":508},[491,1831,1832],{"class":525},"!",[491,1834,1835],{"class":508},"reduced);\n",[491,1837,1838],{"class":493,"line":594},[491,1839,554],{"emptyLinePlaceholder":553},[491,1841,1842,1844,1846,1848],{"class":493,"line":695},[491,1843,1046],{"class":504},[491,1845,1049],{"class":508},[491,1847,1052],{"class":525},[491,1849,509],{"class":508},[491,1851,1852,1855,1857,1859,1862,1865],{"class":493,"line":721},[491,1853,1854],{"class":525},"    if",[491,1856,1110],{"class":508},[491,1858,1832],{"class":525},[491,1860,1861],{"class":508},"playing) ",[491,1863,1864],{"class":525},"return",[491,1866,541],{"class":508},[491,1868,1869,1871,1874,1876,1879,1882,1885],{"class":493,"line":726},[491,1870,1066],{"class":525},[491,1872,1873],{"class":515}," id",[491,1875,946],{"class":525},[491,1877,1878],{"class":504}," setInterval",[491,1880,1881],{"class":508},"(advance, ",[491,1883,1884],{"class":515},"6000",[491,1886,1035],{"class":508},[491,1888,1889,1891,1893,1895,1898],{"class":493,"line":1038},[491,1890,1156],{"class":525},[491,1892,1159],{"class":508},[491,1894,1052],{"class":525},[491,1896,1897],{"class":504}," clearInterval",[491,1899,1900],{"class":508},"(id);\n",[491,1902,1903],{"class":493,"line":1043},[491,1904,1905],{"class":508},"  }, [playing, advance]);\n",[491,1907,1908],{"class":493,"line":1057},[491,1909,554],{"emptyLinePlaceholder":553},[491,1911,1912,1914,1917],{"class":493,"line":1063},[491,1913,1191],{"class":525},[491,1915,1916],{"class":508}," { playing, setPlaying }; ",[491,1918,1919],{"class":497},"\u002F\u002F wire setPlaying to a labeled pause\u002Fplay button\n",[491,1921,1922],{"class":493,"line":1086},[491,1923,547],{"class":508},[324,1925,1926,1927,1929,1930,1933],{},"Parallax scrolling is ",[350,1928,1683],{}," territory: the differential movement of layers as the user scrolls is interaction-triggered motion, and it is rarely essential. Gate parallax entirely behind ",[350,1931,1932],{},"@media (prefers-reduced-motion: no-preference)"," so reduced-motion users get a flat, static layout. The same applies to scroll-jacking and scroll-linked scale or rotation effects.",[324,1935,1936,1937,1940],{},"Auto-playing background video combines motion with ",[350,1938,1939],{},"2.2.2"," and bandwidth concerns. Either swap to a static poster image under reduced motion or, at minimum, render a prominent pause control and never loop silently below the five-second threshold without one.",[433,1942,1943],{},[324,1944,1945,1947],{},[340,1946,439],{}," Tab through a carousel with a keyboard only. You must be able to reach a pause control, and with reduced motion emulated the carousel must not be advancing when the page loads.",[398,1949],{},[401,1951,1953],{"id":1952},"motion-in-loading-spinners-skeletons","Motion in Loading Spinners & Skeletons",[324,1955,1956,1957,1959],{},"Loading states are where motion and screen-reader semantics collide. A spinner conveys \"work is happening,\" which is ",[333,1958,1679],{}," feedback—but its rotation is still motion, and the shimmer sweep on skeleton placeholders is decorative spatial movement that should stop under reduced motion. The accessible answer is to separate the visual indicator from the announcement.",[324,1961,1962,1963,1966,1967,1970,1971,1974,1975,1978,1979,1982],{},"The visible spinner or shimmer is decorative and should carry ",[350,1964,1965],{},"aria-hidden=\"true\"",". The loading ",[333,1968,1969],{},"state"," belongs in a ",[350,1972,1973],{},"role=\"status\""," live region with ",[350,1976,1977],{},"sr-only"," text, which satisfies ",[350,1980,1981],{},"4.1.3 Status Messages"," by announcing \"Loading\" once without forcing screen-reader users to perceive any animation at all. Under reduced motion, replace the shimmer sweep with a static muted background or a gentle opacity pulse rather than a sweeping gradient.",[482,1984,1986],{"className":484,"code":1985,"language":486,"meta":487,"style":487},".skeleton {\n  background: var(--surface);\n}\n\n\u002F* Shimmer only when motion is welcome *\u002F\n@media (prefers-reduced-motion: no-preference) {\n  .skeleton {\n    background: linear-gradient(90deg, var(--surface) 25%, var(--primary-soft) 50%, var(--surface) 75%);\n    background-size: 200% 100%;\n    animation: skeleton-sweep 1.4s ease-in-out infinite;\n  }\n}\n\n@keyframes skeleton-sweep {\n  to { background-position: -200% 0; }\n}\n",[350,1987,1988,1995,2012,2016,2020,2025,2031,2038,2105,2123,2145,2149,2153,2157,2167,2190],{"__ignoreMap":487},[491,1989,1990,1993],{"class":493,"line":494},[491,1991,1992],{"class":504},".skeleton",[491,1994,509],{"class":508},[491,1996,1997,2000,2002,2005,2007,2010],{"class":493,"line":501},[491,1998,1999],{"class":515},"  background",[491,2001,580],{"class":508},[491,2003,2004],{"class":515},"var",[491,2006,706],{"class":508},[491,2008,2009],{"class":1113},"--surface",[491,2011,1035],{"class":508},[491,2013,2014],{"class":493,"line":512},[491,2015,547],{"class":508},[491,2017,2018],{"class":493,"line":544},[491,2019,554],{"emptyLinePlaceholder":553},[491,2021,2022],{"class":493,"line":550},[491,2023,2024],{"class":497},"\u002F* Shimmer only when motion is welcome *\u002F\n",[491,2026,2027,2029],{"class":493,"line":557},[491,2028,560],{"class":525},[491,2030,653],{"class":508},[491,2032,2033,2036],{"class":493,"line":566},[491,2034,2035],{"class":504},"  .skeleton",[491,2037,509],{"class":508},[491,2039,2040,2043,2045,2048,2050,2053,2056,2058,2060,2062,2064,2066,2069,2072,2074,2076,2078,2081,2083,2086,2088,2090,2092,2094,2096,2098,2101,2103],{"class":493,"line":574},[491,2041,2042],{"class":515},"    background",[491,2044,580],{"class":508},[491,2046,2047],{"class":515},"linear-gradient",[491,2049,706],{"class":508},[491,2051,2052],{"class":515},"90",[491,2054,2055],{"class":525},"deg",[491,2057,884],{"class":508},[491,2059,2004],{"class":515},[491,2061,706],{"class":508},[491,2063,2009],{"class":1113},[491,2065,1122],{"class":508},[491,2067,2068],{"class":515},"25",[491,2070,2071],{"class":525},"%",[491,2073,884],{"class":508},[491,2075,2004],{"class":515},[491,2077,706],{"class":508},[491,2079,2080],{"class":1113},"--primary-soft",[491,2082,1122],{"class":508},[491,2084,2085],{"class":515},"50",[491,2087,2071],{"class":525},[491,2089,884],{"class":508},[491,2091,2004],{"class":515},[491,2093,706],{"class":508},[491,2095,2009],{"class":1113},[491,2097,1122],{"class":508},[491,2099,2100],{"class":515},"75",[491,2102,2071],{"class":525},[491,2104,1035],{"class":508},[491,2106,2107,2110,2112,2114,2116,2119,2121],{"class":493,"line":588},[491,2108,2109],{"class":515},"    background-size",[491,2111,580],{"class":508},[491,2113,522],{"class":515},[491,2115,2071],{"class":525},[491,2117,2118],{"class":515}," 100",[491,2120,2071],{"class":525},[491,2122,541],{"class":508},[491,2124,2125,2128,2131,2134,2137,2140,2143],{"class":493,"line":594},[491,2126,2127],{"class":515},"    animation",[491,2129,2130],{"class":508},": skeleton-sweep ",[491,2132,2133],{"class":515},"1.4",[491,2135,2136],{"class":525},"s",[491,2138,2139],{"class":515}," ease-in-out",[491,2141,2142],{"class":515}," infinite",[491,2144,541],{"class":508},[491,2146,2147],{"class":493,"line":695},[491,2148,591],{"class":508},[491,2150,2151],{"class":493,"line":721},[491,2152,547],{"class":508},[491,2154,2155],{"class":493,"line":726},[491,2156,554],{"emptyLinePlaceholder":553},[491,2158,2159,2162,2165],{"class":493,"line":1038},[491,2160,2161],{"class":525},"@keyframes",[491,2163,2164],{"class":1113}," skeleton-sweep",[491,2166,509],{"class":508},[491,2168,2169,2172,2174,2177,2179,2182,2184,2187],{"class":493,"line":1043},[491,2170,2171],{"class":504},"  to",[491,2173,1456],{"class":508},[491,2175,2176],{"class":515},"background-position",[491,2178,580],{"class":508},[491,2180,2181],{"class":515},"-200",[491,2183,2071],{"class":525},[491,2185,2186],{"class":515}," 0",[491,2188,2189],{"class":508},"; }\n",[491,2191,2192],{"class":493,"line":1057},[491,2193,547],{"class":508},[324,2195,2196,2197,884,2199,2202,2203,2205,2206,1220],{},"The full pattern—",[350,2198,1973],{},[350,2200,2201],{},"aria-busy",", the ",[350,2204,1977],{}," text, the decorative spinner, and not trapping focus while content loads—is detailed in ",[328,2207,55],{"href":2208},"\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Faccessible-loading-skeletons-and-spinners\u002F",[433,2210,2211],{},[324,2212,2213,2215,2216,2218,2219,1220],{},[340,2214,439],{}," With reduced motion on, trigger a loading state. The shimmer sweep must stop, and a screen reader should announce \"Loading\" via the ",[350,2217,1973],{}," region while the visual indicator stays silent under ",[350,2220,2221],{},"aria-hidden",[398,2223],{},[401,2225,2227],{"id":2226},"common-a11y-mistakes","Common a11y Mistakes",[344,2229,2230,2240,2250,2260,2276,2288],{},[347,2231,2232,2235,2236,2239],{},[340,2233,2234],{},"Kill-switch reduction:"," Setting ",[350,2237,2238],{},"transition: none"," everywhere removes the feedback the animation carried. Crossfade instead of deleting—opacity is vestibular-safe.",[347,2241,2242,2245,2246,2249],{},[340,2243,2244],{},"Forgetting the change listener:"," Reading ",[350,2247,2248],{},"matchMedia().matches"," once and never subscribing means a user who toggles the OS setting mid-session is ignored until reload.",[347,2251,2252,2255,2256,2259],{},[340,2253,2254],{},"SSR flash of motion:"," Defaulting the hook to ",[350,2257,2258],{},"false"," (motion on) ships a large animation on first paint before hydration corrects it. Default to reduced.",[347,2261,2262,2265,2266,2268,2269,2272,2273,2275],{},[340,2263,2264],{},"Auto-advancing under reduce:"," A carousel that respects ",[350,2267,386],{}," for its slide ",[333,2270,2271],{},"transition"," but still auto-rotates still violates ",[350,2274,1939],{},". Start paused.",[347,2277,2278,2281,2282,2284,2285,2287],{},[340,2279,2280],{},"Animated spinner without a live region:"," Decorative motion with no ",[350,2283,1973],{}," leaves screen-reader users unaware that loading is happening; the inverse—announcing without ",[350,2286,2221],{}," on the visual—causes double perception.",[347,2289,2290,2293,2294,2296,2297,2299,2300,1220],{},[340,2291,2292],{},"Gating only CSS, not JS:"," CSS transitions respect the media query but a ",[350,2295,883],{}," spring or ",[350,2298,891],{}," loop running in parallel does not, unless you gate it on the hook or ",[350,2301,1232],{},[398,2303],{},[401,2305,2307],{"id":2306},"frequently-asked-questions","Frequently Asked Questions",[324,2309,2310,2316,2317,2319],{},[340,2311,2312,2313,2315],{},"Does ",[350,2314,386],{}," mean I have to remove all animation?","\nNo. It means the user wants ",[333,2318,1383],{}," motion, specifically large spatial movement like slides, scale, parallax, and rotation. Opacity crossfades and essential indicators (progress, loading) are generally acceptable. Design a reduced variant that preserves feedback rather than stripping animation entirely.",[324,2321,2322,2325,2326,2329,2330,2332,2333,884,2335,2337,2338,2340],{},[340,2323,2324],{},"Should I detect motion preference in CSS or JavaScript?","\nBoth, for different layers. Use ",[350,2327,2328],{},"@media (prefers-reduced-motion)"," for declarative CSS transitions and keyframes, and read ",[350,2331,895],{}," in JavaScript to gate framework animations like ",[350,2334,883],{},[350,2336,887],{},", or GSAP that CSS cannot reach. A motion-safe default—animation only inside ",[350,2339,457],{},"—is the most resilient strategy.",[324,2342,2343,2346,2348,2349,2351,2352,2354],{},[340,2344,2345],{},"What WCAG criteria govern animation and motion?",[350,2347,357],{}," (Level A) requires controls for auto-starting motion lasting over five seconds. ",[350,2350,352],{}," (Level AAA) requires interaction-triggered motion be disablable unless essential. ",[350,2353,362],{}," (Level A) limits flashing that can trigger seizures.",[324,2356,2357,2360,2361,2363],{},[340,2358,2359],{},"Why default my React hook to reduced motion instead of full motion?","\nBecause the server has no ",[350,2362,1208],{},", the first render must assume a value. Assuming reduced motion means the worst-case first paint is a missing flourish; assuming full motion means a vestibular user can experience an unwanted lurch before hydration corrects it. Fail safe toward less motion.",[324,2365,2366,2369,2370,2372,2373,2375,2376,2378],{},[340,2367,2368],{},"Can a carousel auto-advance if the slide transition is just a fade?","\nA fade addresses the ",[333,2371,2271],{}," motion, but auto-advancing content that loops still falls under ",[350,2374,357],{}," and competes for attention. Provide a keyboard-operable pause control, and start the carousel paused when ",[350,2377,1719],{}," is set.",[398,2380],{},[401,2382,2384],{"id":2383},"related-guides","Related guides",[344,2386,2387,2391,2396,2401,2406,2410],{},[347,2388,2389],{},[328,2390,10],{"href":330},[347,2392,2393],{},[328,2394,25],{"href":2395},"\u002Fcore-accessibility-principles-for-modern-frameworks\u002Ffocus-management-strategies-for-spas\u002F",[347,2397,2398],{},[328,2399,13],{"href":2400},"\u002Fcore-accessibility-principles-for-modern-frameworks\u002Faccessible-color-contrast-theming\u002F",[347,2402,2403],{},[328,2404,67],{"href":2405},"\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing\u002F",[347,2407,2408],{},[328,2409,61],{"href":1219},[347,2411,2412],{},[328,2413,55],{"href":2208},[2415,2416,2417],"style",{},"html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":487,"searchDepth":501,"depth":501,"links":2419},[2420,2421,2423,2424,2425,2426,2427,2428,2429],{"id":403,"depth":501,"text":404},{"id":445,"depth":501,"text":2422},"The prefers-reduced-motion Media Query",{"id":876,"depth":501,"text":877},{"id":1253,"depth":501,"text":1254},{"id":1696,"depth":501,"text":1697},{"id":1952,"depth":501,"text":1953},{"id":2226,"depth":501,"text":2227},{"id":2306,"depth":501,"text":2307},{"id":2383,"depth":501,"text":2384},null,"Respect vestibular and cognitive needs in animated framework UIs—prefers-reduced-motion in CSS and React, accessible transitions, and motion-safe loading states.","md",{},false,{"title":49,"description":2431},"8PxNb3GJxDdIkP17eFqd8ZkcmM_WmqpjTx5fLdUBAVI",[2438,2477,2478,2541],{"title":5,"path":6,"stem":7,"children":2439},[2440,2441,2444,2447,2453,2459,2468,2474],{"title":10,"path":6,"stem":11},{"title":13,"path":14,"stem":15,"children":2442},[2443],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":2445},[2446],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":2448},[2449,2450],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":2451},[2452],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":2454},[2455,2456],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":2457},[2458],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":2460},[2461,2462,2465],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":2463},[2464],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":2466},[2467],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":2469},[2470,2471],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":2472},[2473],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":2475},[2476],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87},{"title":89,"path":90,"stem":91,"children":2479},[2480,2481,2487,2499,2511,2514,2523,2535],{"title":94,"path":90,"stem":95},{"title":97,"path":98,"stem":99,"children":2482},[2483,2484],{"title":97,"path":98,"stem":99},{"title":103,"path":104,"stem":105,"children":2485},[2486],{"title":103,"path":104,"stem":105},{"title":109,"path":110,"stem":111,"children":2488},[2489,2490,2493,2496],{"title":109,"path":110,"stem":111},{"title":115,"path":116,"stem":117,"children":2491},[2492],{"title":115,"path":116,"stem":117},{"title":121,"path":122,"stem":123,"children":2494},[2495],{"title":121,"path":122,"stem":123},{"title":127,"path":128,"stem":129,"children":2497},[2498],{"title":127,"path":128,"stem":129},{"title":133,"path":134,"stem":135,"children":2500},[2501,2502,2505,2508],{"title":133,"path":134,"stem":135},{"title":139,"path":140,"stem":141,"children":2503},[2504],{"title":139,"path":140,"stem":141},{"title":145,"path":146,"stem":147,"children":2506},[2507],{"title":145,"path":146,"stem":147},{"title":151,"path":152,"stem":153,"children":2509},[2510],{"title":151,"path":152,"stem":153},{"title":157,"path":158,"stem":159,"children":2512},[2513],{"title":157,"path":158,"stem":159},{"title":163,"path":164,"stem":165,"children":2515},[2516,2517,2520],{"title":163,"path":164,"stem":165},{"title":169,"path":170,"stem":171,"children":2518},[2519],{"title":169,"path":170,"stem":171},{"title":175,"path":176,"stem":177,"children":2521},[2522],{"title":175,"path":176,"stem":177},{"title":181,"path":182,"stem":183,"children":2524},[2525,2526,2529,2532],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189,"children":2527},[2528],{"title":187,"path":188,"stem":189},{"title":193,"path":194,"stem":195,"children":2530},[2531],{"title":193,"path":194,"stem":195},{"title":199,"path":200,"stem":201,"children":2533},[2534],{"title":199,"path":200,"stem":201},{"title":205,"path":206,"stem":207,"children":2536},[2537,2538],{"title":205,"path":206,"stem":207},{"title":211,"path":212,"stem":213,"children":2539},[2540],{"title":211,"path":212,"stem":213},{"title":217,"path":218,"stem":219,"children":2542},[2543,2544,2553,2562,2571,2580],{"title":222,"path":218,"stem":223},{"title":225,"path":226,"stem":227,"children":2545},[2546,2547,2550],{"title":225,"path":226,"stem":227},{"title":231,"path":232,"stem":233,"children":2548},[2549],{"title":231,"path":232,"stem":233},{"title":237,"path":238,"stem":239,"children":2551},[2552],{"title":237,"path":238,"stem":239},{"title":243,"path":244,"stem":245,"children":2554},[2555,2556,2559],{"title":243,"path":244,"stem":245},{"title":249,"path":250,"stem":251,"children":2557},[2558],{"title":249,"path":250,"stem":251},{"title":255,"path":256,"stem":257,"children":2560},[2561],{"title":255,"path":256,"stem":257},{"title":261,"path":262,"stem":263,"children":2563},[2564,2565,2568],{"title":261,"path":262,"stem":263},{"title":267,"path":268,"stem":269,"children":2566},[2567],{"title":267,"path":268,"stem":269},{"title":273,"path":274,"stem":275,"children":2569},[2570],{"title":273,"path":274,"stem":275},{"title":279,"path":280,"stem":281,"children":2572},[2573,2574,2577],{"title":279,"path":280,"stem":281},{"title":285,"path":286,"stem":287,"children":2575},[2576],{"title":285,"path":286,"stem":287},{"title":291,"path":292,"stem":293,"children":2578},[2579],{"title":291,"path":292,"stem":293},{"title":297,"path":298,"stem":299,"children":2581},[2582,2583,2586],{"title":297,"path":298,"stem":299},{"title":303,"path":304,"stem":305,"children":2584},[2585],{"title":303,"path":304,"stem":305},{"title":309,"path":310,"stem":311,"children":2587},[2588],{"title":309,"path":310,"stem":311},1781785523473]