# How Diagram-Design Handles Reduced Motion for Accessibility: A Technical Deep Dive

> Learn how Diagram-Design uses CSS media queries, JavaScript, and automated verification for reduced motion accessibility. Discover static frame rendering and hidden controls.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: deep-dive
- Published: 2026-08-17

---

**Diagram-Design implements a three-layer reduced motion strategy that combines CSS media queries, JavaScript state management, and automated verification to ensure static frame rendering and hidden playback controls when users request motion reduction.**

The `cathrynlavery/diagram-design` repository provides a comprehensive accessibility implementation for users with vestibular disorders or motion sensitivity. By combining defensive CSS patterns with reactive JavaScript state handlers, the system ensures diagrams remain fully functional and understandable without relying on animation.

## CSS Fallback Layer for Static Frame Rendering

The foundation of diagram-design reduced motion accessibility lies in a defensive CSS block that guarantees static rendering even when JavaScript fails to load. In [`skills/diagram-design/assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/template-motion.html) (lines 51-61), the `@media (prefers-reduced-motion: reduce)` query strips all animation capabilities and forces immediate visibility of content.

The system applies three critical CSS overrides:

- **Animation nullification** – Sets `animation-duration: .001ms !important` and `transition-duration: .001ms !important` to effectively eliminate motion while maintaining layout calculations
- **Decorative element removal** – Hides `[data-motion-decorative]` elements that serve no informational purpose
- **Control suppression** – Removes `[data-motion-controls]` from the accessibility tree to prevent keyboard users from navigating non-functional buttons

```html
<style>
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: .001ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: .001ms !important;
    }
    [data-motion-item] { 
      opacity: 1 !important; 
      transform: none !important; 
    }
    [data-motion-decorative] { 
      display: none !important; 
    }
    [data-motion-controls] { 
      display: none !important; 
    }
  }
</style>

```

This CSS ensures that diagrams render as complete static frames immediately upon parsing, satisfying accessibility requirements before JavaScript initialization occurs.

## JavaScript Detection and Dynamic State Management

Beyond the CSS fallback, the repository implements sophisticated runtime detection in [`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html) (lines 56-68) using the `matchMedia('(prefers-reduced-motion: reduce)')` API. This handler responds to system preference changes in real-time, enabling seamless transitions between motion and static modes without page reloads.

When the media query matches, the system executes a precise shutdown sequence:

1. **Playback interruption** – Immediately pauses any ongoing animation sequences
2. **State preservation** – Stores the current diagram step (`stepBeforeReduce`) and playback status (`resumeAfterReduce`) in memory for potential restoration
3. **DOM attribute updates** – Sets `data-motion-state="reduced"` on the root element for CSS targeting
4. **Static frame rendering** – Invokes `render(count, false)` to display the final completed diagram state
5. **UI disabling** – Calls `setControlsAvailable(false)` to remove interactive controls from the accessibility tree

```javascript
const reduce = matchMedia('(prefers-reduced-motion: reduce)');

reduce.addEventListener('change', (event) => {
  if (event.matches && root.dataset.motionState !== 'reduced') {
    stepBeforeReduce = step;      // remember current position
    resumeAfterReduce = playing;  // remember playback state
  }
  
  pause(false); // stop all animation immediately

  if (event.matches) {
    setControlsAvailable(false);
    render(count, false);         // force complete static frame
    root.dataset.motionState = 'reduced';
    status.textContent = 'Reduced motion · complete static frame · playback controls unavailable';
  }
});

```

### ARIA Live Region Updates for Screen Reader Users

The JavaScript implementation updates an ARIA live region (`status` element) with explicit messaging when reduced motion activates. This notification pattern ensures that screen reader users receive immediate feedback about the mode change and understand that playback controls are unavailable, preventing confusion when interactive elements disappear from the interface.

## Automated Verification and Compliance Testing

To maintain accessibility standards across generated diagrams, the repository includes rigorous validation tooling. The [`scripts/verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-motion.py) file (lines 389-405) functions as a linter that checks generated HTML for the presence of the reduced-motion CSS block and validates that controls remain hidden when the media query matches.

Complementing this, [`scripts/test-verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/test-verify-motion.py) (lines 160-180) contains a test suite that simulates incorrect reduced-motion implementations to ensure the validator catches missing accessibility features. This automated enforcement guarantees that all diagrams exported from the system meet WCAG guidelines regarding motion and animation.

## Summary

Diagram-design reduced motion accessibility relies on a defense-in-depth architecture:

- **Immediate CSS protection** eliminates animation at the stylesheet level before JavaScript executes
- **Runtime JavaScript handling** detects preference changes and renders complete static frames via `render(count, false)`
- **State preservation** maintains user context through `stepBeforeReduce` variables should users toggle preferences back
- **ARIA live notifications** communicate mode changes to assistive technologies with specific status text
- **Automated validation** through [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py) ensures compliance across all generated diagram assets

## Frequently Asked Questions

### How does diagram-design detect reduced motion preferences?

The system uses the `matchMedia('(prefers-reduced-motion: reduce)')` API in [`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html) (lines 56-68) to listen for system preference changes. This JavaScript hook detects both initial page load states and real-time toggles in the user's operating system accessibility settings, allowing the diagram to respond immediately without requiring page refreshes.

### What happens to playback controls when reduced motion is enabled?

When the reduced motion media query matches, the system invokes `setControlsAvailable(false)` to disable the controls UI and hides them via CSS `[data-motion-controls] { display: none !important; }`. This dual approach prevents keyboard-only users from tabbing to non-functional buttons while maintaining visual cleanliness. The ARIA live region simultaneously announces "playback controls unavailable" to inform screen reader users of the change.

### How does the repository ensure reduced motion compliance across all diagrams?

The [`scripts/verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-motion.py) validator (lines 389-405) automatically checks generated HTML files for the presence of the `@media (prefers-reduced-motion: reduce)` CSS block and validates that motion controls remain hidden when the preference is active. The accompanying [`scripts/test-verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/test-verify-motion.py) suite (lines 160-180) tests these validation rules by injecting non-compliant code and verifying that the linter correctly identifies violations.

### Can users resume animation if they disable reduced motion after enabling it?

Yes, the JavaScript implementation preserves the user's state before entering reduced motion mode. The variables `stepBeforeReduce` and `resumeAfterReduce` store the current diagram step and playback status, allowing the system to restore the exact viewing context if the user subsequently disables reduced motion preferences through their system settings.