How to Add Optional Accessible Motion to Diagrams in Diagram-Design

Diagram-Design supports optional accessible motion through a lightweight HTML contract that progressively enhances SVG-based diagrams with CSS animations and JavaScript controls while maintaining a fully static fallback for users who prefer reduced motion.

The Diagram-Design repository provides a declarative system for adding motion to diagrams without sacrificing accessibility standards. By implementing a specific data-attribute contract defined in skills/diagram-design/assets/template-motion.html, you can create animated visualizations that automatically degrade to accessible, static alternatives when users have motion sensitivities or disabled JavaScript.

Understanding the Motion Contract Architecture

The motion system centers on a single root controller coordinating multiple motion items. This architecture is enforced by continuous integration validators to ensure consistent accessibility across all diagrams.

The Root Controller (data-motion-root)

Every motion-enabled diagram requires exactly one container element marked with data-motion-root. As defined in the template source, this element accepts configuration attributes that determine animation behavior:

  • data-motion-mode: Set to "step" for step-through animations or "loop" for infinite cycling
  • data-step-count: Integer defining total steps (maximum 12, strictly enforced by CI)
  • data-frame="static": Indicates the initial static fallback state before JavaScript activation

The root element acts as the central controller that manages visibility states and synchronization across all child motion items.

Motion Items and Semantic Structure

Individual animation steps are marked with data-motion-item attributes and indexed using data-step="N" (1-based indexing). The contract enforces strict separation between semantic and decorative content:

Semantic items convey meaning and must include descriptive aria-label attributes. Decorative items—such as connector arrows or background fills—require data-motion-decorative combined with aria-hidden="true" and focusable="false" to ensure assistive technology ignores them.

The validator scripts/verify-semantic-motion.py specifically checks that non-decorative items carry meaningful labels and that decorative elements are properly hidden from the accessibility tree.

Playback Controls and Live Regions

User interface controls reside within a container marked data-motion-controls. Individual buttons use data-motion-action attributes accepting values including "play", "pause", "prev", "next", and "replay".

For screen reader users, a live region with role="status" and aria-live="polite" provides immediate spoken updates about the current step. This element uses data-motion-status and remains visually hidden while announcing changes to assistive technology.

Implementing the Motion Contract

To add optional accessible motion, import the motion template and implement the data-attribute contract within your SVG markup. The following example demonstrates a three-step process flow with decorative connectors and full accessibility support:

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

<main
  data-motion-root
  data-motion-mode="step"
  data-step-count="3"
  data-step-current="3"
  data-frame="static"
  data-static-frame="complete"
>
  <h1>Sample Process Flow</h1>

  <svg viewBox="0 0 600 200" role="img" aria-labelledby="sample-title sample-desc">
    <title id="sample-title">Sample Process Flow</title>
    <desc id="sample-desc">
      A three-step process that shows request → validation → completion.
    </desc>

    <g data-motion-item data-step="1" aria-label="Step 1: Request received">
      <rect x="20" y="50" width="150" height="80" fill="#f5f5f5" stroke="#2d3142"/>
      <text x="95" y="100" text-anchor="middle" fill="#2d3142">Request</text>
    </g>

    <path
      data-motion-item
      data-motion-decorative
      aria-hidden="true"
      focusable="false"
      d="M170 90 H380"
      stroke="#4f5d75"
    />

    <g data-motion-item data-step="2" aria-label="Step 2: Validation passed">
      <rect x="380" y="50" width="150" height="80" fill="rgba(235,108,36,0.08)" stroke="#eb6c36"/>
      <text x="455" y="100" text-anchor="middle" fill="#eb6c36">Validate</text>
    </g>

    <path
      data-motion-item
      data-motion-decorative
      aria-hidden="true"
      focusable="false"
      d="M530 90 H590"
      stroke="#4f5d75"
    />

    <g data-motion-item data-step="3" aria-label="Step 3: Process complete">
      <rect x="590" y="50" width="150" height="80" fill="#f5f5f5" stroke="#2d3142"/>
      <text x="665" y="100" text-anchor="middle" fill="#2d3142">Done</text>
    </g>
  </svg>

  <div data-motion-controls role="group" aria-label="Diagram playback controls">
    <button type="button" data-motion-action="prev">Previous</button>
    <button type="button" data-motion-action="play" aria-pressed="false">Play</button>
    <button type="button" data-motion-action="pause" aria-pressed="true">Pause</button>
    <button type="button" data-motion-action="next">Next</button>
    <button type="button" data-motion-action="replay">Replay</button>
    <span data-motion-status-visible aria-hidden="true">
      Step <span data-motion-step-label>3</span> of 3
    </span>
  </div>

  <p class="sr-only" data-motion-status role="status" aria-live="polite" aria-atomic="true">
    Step 3 of 3 – Request received → Validation passed → Process complete.
  </p>
</main>

The template-motion.html asset provides CSS animations and a @media (prefers-reduced-motion: reduce) media query that automatically disables animations, forces all items to full visibility, and hides playback controls when users prefer reduced motion.

Automated CI Validation

The repository enforces accessibility compliance through two Python validators that run in continuous integration:

scripts/verify-motion.py performs structural validation, ensuring:

  • Exactly one data-motion-root exists per diagram
  • No more than 12 motion items are present
  • The reduced-motion CSS media query is implemented
  • A <noscript> fallback element is provided for non-JavaScript environments

scripts/verify-semantic-motion.py checks accessibility semantics, verifying:

  • Non-decorative items have meaningful aria-label attributes
  • Decorative items use aria-hidden="true" and focusable="false"
  • Status regions implement role="status" and aria-live="polite" correctly

Both scripts prevent merging code that violates the motion contract, ensuring all diagrams remain accessible by default.

Summary

  • Optional accessible motion relies on a declarative HTML contract using data-motion-root and data-motion-item attributes
  • The system supports step-through (data-motion-mode="step") or loop modes with a maximum of 12 steps enforced by verify-motion.py
  • Decorative elements must use data-motion-decorative with aria-hidden="true" to remain invisible to assistive technology
  • Reduced-motion fallback activates automatically via CSS media queries in template-motion.html, displaying a static version of the diagram
  • CI validation through verify-motion.py and verify-semantic-motion.py guarantees compliance with WCAG guidelines

Frequently Asked Questions

How does the motion system handle users who prefer reduced motion?

The system uses a @media (prefers-reduced-motion: reduce) media query defined in template-motion.html that forces all data-motion-item elements to full visibility, disables CSS transitions and animations, and hides the data-motion-controls container. This happens automatically at the browser level without JavaScript detection, ensuring users with vestibular disorders receive a static, readable diagram immediately upon page load.

What is the maximum number of animation steps supported?

The contract enforces a maximum of 12 steps per diagram, as validated by scripts/verify-motion.py. This limit prevents cognitive overload and ensures step-through interfaces remain manageable. The validator checks the data-step-count attribute against the actual number of data-motion-item elements with unique data-step values. If your diagram requires more than 12 steps, refactor it into multiple linked diagrams or consolidate conceptual steps.

How do I mark decorative elements versus semantic content?

Semantic content—such as process steps or data points—requires a descriptive aria-label attribute on the data-motion-item element. Purely visual decorations like arrows or backgrounds must include data-motion-decorative, aria-hidden="true", and focusable="false" attributes. The verify-semantic-motion.py script validates this distinction, failing builds where decorative elements lack proper hiding attributes or where semantic items lack labels.

Is JavaScript required to view motion-enabled diagrams?

No. Optional accessible motion is implemented as progressive enhancement. When JavaScript is unavailable, the data-frame="static" attribute ensures the diagram renders in its complete state, and the required <noscript> element provides a text description of the full process. Motion controls and step-by-step animations only activate when JavaScript successfully loads, ensuring the core informational content remains accessible in all browsing contexts.

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 →