# How to Ensure Diagrams Are Accessible with Animation Constraints

> Ensure diagram accessibility with animation constraints. Isolate motion as an optional layer and enforce strict contracts using markdown and canonical controllers for verified semantic motion.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: best-practices
- Published: 2026-09-08

---

**Diagrams remain accessible under animation constraints by isolating motion as an optional layer that preserves complete semantic information in the static SVG, enforcing strict contracts via [`animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/animation.md), and using the canonical motion controller from [`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html) with automated verification through [`verify-semantic-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-semantic-motion.py) and [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py).**

The cathrynlavery/diagram-design repository treats animation as a progressive enhancement rather than a core requirement, ensuring that diagrams are accessible to screen readers, printable without artifacts, and compatible with reduced-motion preferences. By adhering to the **static-first enhancement contract** defined in [`skills/diagram-design/references/animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/animation.md) and utilizing the four sanctioned motion modes, you can ensure diagrams are accessible with animation constraints while still offering rich visual experiences.

## Understand the Animation Contract Architecture

The repository enforces accessibility through a three-layer contract system that separates reference documentation, implementation, and verification.

### Reference Contract ([`animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/animation.md))

The canonical contract resides in [`skills/diagram-design/references/animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/animation.md). This file defines the four allowed motion modes (`none`, `reveal`, `step`, `loop`) and mandates specific ARIA attributes, reduced-motion fallbacks, and keyboard controls. According to [`SKILL.md`](https://github.com/cathrynlavery/diagram-design/blob/main/SKILL.md) (lines 80‑132), the routing table loads this contract only when the user explicitly requests motion or when changes in order, accumulation, or containment cannot be explained statically.

### Canonical Implementation ([`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html))

The executable contract lives in [`skills/diagram-design/assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/template-motion.html). All animated diagrams must import this file verbatim, preserving its script, IDs, and data-attributes. The README explicitly states this requirement (line 281), and any deviation triggers rejection by the linter.

### Automated Verification

Two scripts enforce compliance:

- **[`scripts/verify-semantic-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-semantic-motion.py)** — Validates that every mode, primitive, and contract term from [`animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/animation.md) appears in the implementation.
- **[`scripts/verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-motion.py)** — Scans generated HTML for illegal infinite animations or missing scoped attributes.

Both scripts must exit with "OK" before merging, as enforced by the CI pipeline in [`.github/workflows/ci.yml`](https://github.com/cathrynlavery/diagram-design/blob/main/.github/workflows/ci.yml).

## Implement the Four Sanctioned Motion Modes

The contract restricts animation to four specific modes, each with distinct accessibility implications.

### Mode `none` (Default)

By default, diagrams render with `data-motion-mode="none"`. No JavaScript is shipped, and the figure remains fully accessible to screen readers, print layouts, and assistive technologies.

### Mode `reveal`

Use `reveal` for short ordered explanations such as policy traces or flows. Autoplay runs **once** on load, then the diagram stays static. The complete figure remains present in the accessibility tree; only the visual progression is animated.

### Mode `step`

Use `step` for teaching scenarios or multi-step policy traces. This mode requires explicit play/pause controls with the following ARIA implementation:

- Native buttons sized ≥ 44 × 44 px with `aria-pressed` for play/pause state
- A live region with `role="status"` and `aria-live="polite"` to announce step changes
- Keyboard shortcuts (`ArrowRight`, `ArrowLeft`) defined in [`animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/animation.md) (lines 85‑90)

### Mode `loop`

Reserve `loop` for purely decorative tokens (e.g., a moving indicator on a flow line) that do not change semantic meaning. The `prefers-reduced-motion` media query automatically disables these animations.

## Enforce Static-First Rendering

Every semantic node, label, connector, and status must exist in the SVG before any CSS or JavaScript executes. The **static-first enhancement contract** requires capturing the static frame with `data-frame="static"` to ensure the diagram is printable and exportable without animation artifacts ([`animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/animation.md), lines 22‑24).

## Configure Reduced-Motion Fallbacks

When `prefers-reduced-motion: reduce` is active, the CSS block in [`animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/animation.md) (lines 68‑74) overrides all animation and transition durations to `0.001ms`. This forces:

```css
[data-motion-item] { opacity: 1 !important; transform: none !important; }
[data-motion-decorative] { display: none !important; }
[data-motion-controls] { display: none !important; }

```

Users see the complete static frame instantly, satisfying WCAG 1.4.3 (Contrast) and 2.2.2 (Pause, Stop, Hide).

## Integrate the Canonical Motion Controller

Import [`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html) verbatim into animated diagrams:

```html
<link rel="import" href="../assets/template-motion.html">

<svg width="600" height="400"
     data-motion-mode="step"
     data-motion-root>
  <title>Policy evaluation trace</title>
  <desc>Three rules evaluated in order.</desc>

  <g data-motion-item data-step="1">Rule 1 …</g>
  <g data-motion-item data-step="2">Rule 2 …</g>
  <g data-motion-item data-step="3">Rule 3 …</g>

  <!-- Controls generated by the template -->
  <div data-motion-controls>
    <button data-motion-action="play" aria-pressed="false">Play</button>
    <button data-motion-action="pause">Pause</button>
    <button data-motion-action="prev">Prev</button>
    <button data-motion-action="next">Next</button>
    <button data-motion-action="replay">Replay</button>
    <div role="status" aria-live="polite" aria-atomic="true"></div>
  </div>
</svg>

```

The controller automatically generates ARIA-friendly controls and implements the keyboard navigation contract.

## Avoid Forbidden Animation Patterns

The contract explicitly prohibits animating layout coordinates, viewBox properties, node dimensions, or using infinite loops outside `loop` mode ([`animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/animation.md), line 46). The [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py) script parses CSS for unscoped `animation-iteration-count: infinite` declarations and fails the build if detected.

## Verify Accessibility Compliance Locally

Before submitting changes, run both verification scripts:

```bash

# Check contract term coverage

python3 scripts/verify-semantic-motion.py

# Validate motion rules and ARIA implementation

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

```

Both commands must output "OK" to pass the CI pipeline.

## Summary

- **Isolate animation** as an optional layer using the static-first contract in [`skills/diagram-design/references/animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/animation.md).
- **Restrict motion** to four sanctioned modes (`none`, `reveal`, `step`, `loop`) with explicit accessibility impacts and ARIA requirements.
- **Import the canonical controller** from [`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html) verbatim to ensure keyboard navigation and control accessibility.
- **Implement reduced-motion fallbacks** via `prefers-reduced-motion` media queries that force immediate static rendering.
- **Validate all diagrams** using [`verify-semantic-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-semantic-motion.py) and [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py) to detect illegal infinite animations or missing semantic terms.

## Frequently Asked Questions

### What happens if I modify the motion controller instead of using [`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html) verbatim?

Any deviation from the canonical controller triggers the [`verify-semantic-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-semantic-motion.py) linter, which rejects the diagram. The README (line 281) explicitly requires verbatim import because the controller serves as the executable accessibility contract; modifications risk breaking ARIA attributes, keyboard controls, or reduced-motion fallbacks required by the specification.

### How does the system handle users with vestibular disorders or reduced-motion preferences?

The CSS in [`animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/animation.md) detects `prefers-reduced-motion: reduce` and immediately sets all animation durations to `0.001ms`, displaying the complete static frame without playback controls. This ensures users see fully rendered diagrams instantly, satisfying WCAG guidelines while maintaining semantic integrity.

### Can I use infinite animations for loading indicators or decorative elements?

Infinite animations are permitted only within `data-motion-mode="loop"` for purely decorative tokens that do not alter semantic meaning. The [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py) script scans for unscoped `animation-iteration-count: infinite` CSS rules and fails the build if found outside loop mode, preventing distracting or inaccessible motion patterns.

### Which verification script checks for missing ARIA attributes in step animations?

The [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py) script inspects generated HTML for proper scoping and ARIA implementation, while [`verify-semantic-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-semantic-motion.py) ensures all contract terms from [`animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/animation.md)—including required ARIA attributes like `aria-pressed` and `role="status"`—appear in the implementation. Together they validate that step-mode controls meet accessibility standards for screen readers and keyboard navigation.