{
    "slug": "reduced_motion",
    "term": "prefers-reduced-motion — Accessible Animations",
    "category": "accessibility",
    "difficulty": "beginner",
    "short": "A CSS media query and JavaScript API that detects when a user has requested reduced motion in their OS settings, allowing you to disable or simplify animations that can trigger vestibular disorders.",
    "long": "Motion on screen — parallax, auto-playing animations, rapid transitions — can trigger dizziness, nausea, and migraines in users with vestibular disorders (affecting up to 35% of adults over 40). The `prefers-reduced-motion: reduce` media query fires when the user enables 'Reduce Motion' in macOS/iOS, Windows 'Show animations', or Android accessibility settings. The correct approach is to retain meaningful transitions (opacity fades, simple slides) but remove spinning, bouncing, zooming, and parallax effects. JavaScript can read the preference via `window.matchMedia('(prefers-reduced-motion: reduce)')`. WCAG 2.1 Success Criterion 2.3.3 (Animation from Interactions) at AAA level requires an option to disable motion; at AA level, WCAG 2.3.1 restricts content that flashes more than 3 times per second.",
    "aliases": [
        "prefers-reduced-motion",
        "reduce motion",
        "vestibular accessibility",
        "motion sensitivity"
    ],
    "tags": [
        "accessibility",
        "css",
        "animation",
        "wcag",
        "media-query"
    ],
    "misconception": "Reduced motion means removing all animations — users requesting reduced motion still benefit from subtle transitions like simple fades and colour changes that communicate state. The goal is removing vestibular-triggering motion (spinning, bouncing, zooming, parallax), not all visual feedback.",
    "why_it_matters": "Ignoring motion preferences can make your site physically uncomfortable or unusable for users with vestibular disorders, migraines, or epilepsy. It is also increasingly a WCAG compliance requirement and covered by accessibility laws in many jurisdictions.",
    "common_mistakes": [
        "Setting `animation: none` globally under `prefers-reduced-motion` — removes useful feedback like loading spinners and state transitions.",
        "Only disabling CSS animations but not JavaScript-driven animations (GSAP, Framer Motion, custom `requestAnimationFrame` loops).",
        "Not pausing auto-playing carousels and video backgrounds — these are often more problematic than CSS animations.",
        "Testing only in a browser without actually enabling the OS-level reduced motion setting — the media query won't fire in browser devtools alone on some platforms."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "wcag_guidelines",
        "css_animations",
        "keyboard_navigation",
        "focus_management",
        "accessible_forms"
    ],
    "prerequisites": [
        "wcag_guidelines"
    ],
    "refs": [
        "https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion",
        "https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html"
    ],
    "bad_code": "/* No motion consideration — spins indefinitely */\n.loader {\n    animation: spin 1s linear infinite;\n}\n.hero {\n    animation: parallax-float 4s ease-in-out infinite;\n}\n\n@keyframes spin { to { transform: rotate(360deg); } }\n@keyframes parallax-float { 50% { transform: translateY(-20px); } }",
    "good_code": "/* Default: full animation */\n.loader {\n    animation: spin 1s linear infinite;\n}\n.hero {\n    animation: parallax-float 4s ease-in-out infinite;\n}\n\n/* Reduced motion: keep functional feedback, remove vestibular triggers */\n@media (prefers-reduced-motion: reduce) {\n    .loader {\n        animation: fade-pulse 2s ease-in-out infinite; /* subtle opacity, no spin */\n    }\n    .hero {\n        animation: none; /* parallax removed entirely */\n    }\n    * {\n        transition-duration: 0.01ms !important; /* near-instant transitions */\n        scroll-behavior: auto !important;\n    }\n}\n\n@keyframes fade-pulse { 50% { opacity: 0.5; } }\n\n// JavaScript: respond to preference\nconst prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)');\nif (prefersReduced.matches) pauseCarousel();",
    "example_note": "Replacing the spin animation with a fade-pulse keeps the loader meaningful without triggering vestibular symptoms.",
    "quick_fix": "Wrap all CSS animations in `@media (prefers-reduced-motion: no-preference)` or add a `@media (prefers-reduced-motion: reduce)` block that disables spinning/bouncing/parallax",
    "severity": "high",
    "effort": "low",
    "created": "2026-03-24",
    "updated": "2026-03-24",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/reduced_motion",
        "html_url": "https://codeclaritylab.com/glossary/reduced_motion",
        "json_url": "https://codeclaritylab.com/glossary/reduced_motion.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[prefers-reduced-motion — Accessible Animations](https://codeclaritylab.com/glossary/reduced_motion) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/reduced_motion"
            }
        }
    }
}