How `prefers-reduced-motion` Affects Animated Diagrams: Complete Implementation Guide

When prefers-reduced-motion: reduce is active, animated diagrams render as fully-static, semantically-complete frames with all motion controls hidden and decorative animation elements removed.

The Diagram Design system implements robust accessibility support for users with motion sensitivity. Rather than simply slowing animations, it presents a complete alternative experience that preserves all informational content while eliminating potentially harmful motion. This aligns with CSS Working Group recommendations and Web Content Accessibility Guidelines (WCAG) 2.1 requirements for motion reduction.

What prefers-reduced-motion Controls in Diagrams

The Diagram Design skill treats prefers-reduced-motion as a strict rendering contract rather than a cosmetic preference. According to SKILL.md, the system guarantees: "under prefers-reduced-motion: reduce it shows the complete static frame and hides/disables playback controls"【/cache/repos/github.com/cathrynlavery/diagram-design/main/skills/diagram-design/SKILL.md#L547-L549】.

This contract covers five specific behaviors:

  • Static initialization — Diagrams render immediately at the final completed state
  • Control suppression — All playback UI is hidden and disabled via CSS
  • Decorative removal — Non-essential animated elements are removed from the DOM
  • State exposure — Machine-readable attributes signal reduced-motion mode
  • Print parity — Exports always use the same static representation

CSS Implementation Details

The core logic lives in skills/diagram-design/references/animation.md, specifically within the @media (prefers-reduced-motion: reduce) block【/cache/repos/github.com/cathrynlavery/diagram-design/main/skills/diagram-design/references/animation.md#L68-L78】.

Force-Completing All Animations

@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;
  }
  [data-motion-item] { opacity: 1 !important; transform: none !important; }
  [data-motion-decorative] { display: none !important; }
  [data-motion-controls] { display: none !important; }
}

This CSS performs three critical functions:

  1. Zero-duration animations — 0.001ms forces immediate completion without breaking animation event detection
  2. Motion item revelation — [data-motion-item] elements become fully visible with no transforms
  3. Element suppression — Decorative and control elements are removed via display:none

DOM Structure and Data Attributes

The HTML scaffold uses semantic attributes to distinguish motion-critical from motion-decorative content:

<div class="motion-root" data-motion-state="reduced">
  <!-- SVG content with semantic items -->
  <svg>
    <g data-motion-item="node1">…</g>
    <g data-motion-item="node2">…</g>
  </svg>

  <!-- Controls present in markup but hidden when reduced motion is active -->
  <div data-motion-controls>
    <button data-motion-action="play">Play</button>
    <button data-motion-action="pause">Pause</button>
    <button data-motion-action="next">Next</button>
    <button data-motion-action="previous">Previous</button>
  </div>

  <!-- Accessible status for screen readers -->
  <div role="status" aria-live="polite">
    Playback unavailable (reduced-motion preference)
  </div>
</div>

The data-motion-state="reduced" attribute on the root element provides a JavaScript hook and styling target. Screen readers receive immediate notification through the aria-live region announcing that playback is unavailable.

Accessibility Guarantees

Semantic Preservation

The <title> and <desc> elements within SVG diagrams describe the complete meaning regardless of motion state. As noted in README.md, screen readers announce the diagram correctly even when prefers-reduced-motion shows the static frame【/cache/repos/github.com/cathrynlavery/diagram-design/main/README.md#L42-L44】.

No Partial State Exposure

The architectural decision documented in docs/adr/0003-reveal-is-the-only-sanctioned-autoplay.md ensures that "without JavaScript, all modes collapse to the static frame under reduced-motion"【/cache/repos/github.com/cathrynlavery/diagram-design/main/docs/adr/0003-reveal-is-the-only-sanctioned-autoplay.md#L11-L14】. This prevents users from encountering incomplete or transitional diagram states.

Multi-Context Application

The same CSS rules triggering on prefers-reduced-motion: reduce also apply to @media print. This guarantees that PDF exports and rasterized outputs (PNG/SVG) contain the finalized static frame rather than capturing mid-animation states.

Verification Testing

Two Python scripts validate the implementation:


# Verify a specific diagram file

python3 scripts/verify-motion.py path/to/animated-diagram.html

# Run complete test suite

python3 scripts/test-verify-motion.py

These scripts confirm that prefers-reduced-motion correctly:

  • Hides control interfaces
  • Disables decorative animation elements
  • Exposes the static final frame【/cache/repos/github.com/cathrynlavery/diagram-design/main/skills/diagram-design/references/animation.md#L95-L103】

Key Implementation Files

File Purpose
skills/diagram-design/references/animation.md CSS rules implementing reduced-motion handling; lines 68-78 define the media query block
SKILL.md Contract documentation specifying static-frame rendering and control hiding
README.md User-facing documentation of screen-reader and reduced-motion behavior
docs/adr/0003-reveal-is-the-only-sanctioned-autoplay.md Architectural decision record on JavaScript-free fallback behavior

Summary

  • prefers-reduced-motion: reduce triggers immediate static-frame rendering with all motion controls hidden
  • CSS in animation.md forces zero-duration animations and reveals all data-motion-item elements at full opacity
  • Decorative elements marked data-motion-decorative are removed from the visual DOM
  • Screen readers receive complete semantic content via title/desc elements and live-region status announcements
  • Print exports inherit the same static representation through shared @media rules
  • Verification scripts provide automated testing of motion-reduction compliance

Frequently Asked Questions

What happens if JavaScript is disabled?

The diagram falls back to the static frame automatically. Per the architectural decision record, the reduced-motion preference and no-JavaScript scenarios converge to identical behavior: a fully-rendered, non-animated diagram.

Do users see any animation at all with prefers-reduced-motion?

No. The 0.001ms animation duration forces immediate completion, and the data-motion-item override ensures all elements appear in their final state. There is no perceptible motion.

How do assistive technologies know playback is unavailable?

A role="status" element with aria-live="polite" announces "Playback unavailable (reduced-motion preference)" when the diagram initializes. This supplements the complete title and desc content already present in the SVG.

Can authors override this behavior for specific diagrams?

The !important declarations in the CSS rules prevent override, maintaining consistent accessibility compliance. Authors should design diagrams where the static frame conveys complete meaning without requiring animation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →