How Optional Animation Works in Diagram-Design: Understanding Motion Modes
Diagram-Design implements optional animation through a canonical controller in template-motion.html that supports four distinct motion modes—none, reveal, step, and loop—activated via the data-motion-mode attribute while preserving static fallbacks and full accessibility support.
The cathrynlavery/diagram-design repository treats animation as a strictly opt-in enhancement rather than a default behavior. Every diagram begins as a completely static asset, with optional animation layered on only when authors explicitly include the canonical controller and declare a motion mode. This architecture ensures diagrams remain functional for print, screenshots, and users with motion sensitivity while providing rich interactive experiences when desired.
Understanding the Optional Animation Architecture
The optional animation system centers on a single, byte-for-byte canonical controller located at skills/diagram-design/assets/template-motion.html. Authors must include this controller verbatim to enable animation; the repository enforces this through linting and verification scripts that reject modified controllers.
The animation contract is formally defined in skills/diagram-design/references/animation.md. This document specifies that animation activates only when the data-motion-mode attribute is present on the root element, which must also carry data-motion-root. Without these attributes, the diagram remains script-free and static.
The Four Motion Modes in Diagram-Design
The repository supports four mutually exclusive motion modes, each designed for specific use cases and technical constraints.
none (Static Fallback)
The none mode produces a completely static figure with no JavaScript execution. When data-motion-mode="none" is set, authors omit the <script data-diagram-controls> block entirely, creating a script-free page suitable for print media, screenshots, and environments where JavaScript is disabled. This mode serves as the default state and the reduced-motion fallback.
reveal (Single Autoplay)
The reveal mode executes a single deterministic autoplay sequence that concludes in a complete static frame. For sequences shorter than five seconds, the implementation uses CSS-only animations. Longer sequences utilize the scoped controller from template-motion.html to manage timing. This mode is ideal for short ordered explanations such as quick flowcharts or process overviews where the animation supplements but does not replace the final diagram.
step (Interactive Stepping)
The step mode presents paused semantic states that users navigate via play, pause, next, and previous controls. The controller maintains state variables—including step, count, playing, timer, stepBeforeReduce, and resumeAfterReduce—to track position and timing. Minimal inline JavaScript handles step counters, timers, and ARIA live-region announcements for screen reader compatibility. This mode suits teaching scenarios, policy-trace walkthroughs, and complex comparison diagrams requiring user-paced exploration.
loop (Decorative Motion)
The loop mode applies to decorative elements that repeat indefinitely without conveying semantic meaning. Implementation uses pure CSS animation scoped specifically to data-motion-mode="loop" elements, while the controller remains present primarily to expose the static frame when needed. Typical applications include quiet visual hints for long-running processes with cycles of three seconds or more, such as loading indicators or background activity tokens.
How the Animation Controller Works
The controller embedded in template-motion.html serves as the JavaScript engine powering all interactive motion modes. It exposes query parameter parsing for motion=static|step|… and respects the prefers-reduced-motion media query.
Core functionality resides in several key functions located between lines 74 and 124: render() updates the visual state, play() initiates sequences, pause() halts execution, tick() advances frames, and finish() completes animations. Lines 56 through 70 implement reduced-motion handling by detecting prefers-reduced-motion: reduce, disabling playback, rendering the final static frame, and applying the data-motion-state="reduced" attribute.
The controller handles multiple input methods: click events on [data-motion-action] buttons, keyboard navigation including ArrowLeft/Right, Home/End, Space, and R keys, and the visibilitychange event to pause animations when the user switches browser tabs.
Implementing Motion Modes in Your Diagrams
To create a static diagram without animation:
<link rel="stylesheet" href="style.css">
<main data-motion-root data-motion-mode="none">
<!-- SVG content here – complete static source -->
</main>
To implement a stepped, interactive diagram with controls:
<link rel="stylesheet" href="style.css">
<!-- Copy the controller verbatim from template-motion.html -->
<script data-diagram-controls src="template-motion.html"></script>
<main data-motion-root data-motion-mode="step"
data-step-count="5" data-step-current="0" data-frame="static">
<!-- SVG with data-motion-item elements for each step -->
</main>
For a reveal autoplay that runs once and stops:
<main data-motion-root data-motion-mode="reveal">
<!-- SVG items marked with data-motion-item; controller is included -->
</main>
For a decorative looping element:
<main data-motion-root data-motion-mode="loop">
<!-- SVG includes a decorative element with class="flow-token"
and data-motion-decorative; controller is present -->
</main>
Verification and Quality Assurance
The repository enforces animation consistency through scripts/verify-motion.py and scripts/test-verify-motion.py. These scripts validate that:
- Only one
<script data-diagram-controls>block exists per diagram - The controller body exactly matches the canonical
template-motion.htmlsource - The
data-motion-modeattribute contains one of the four allowed values:none,reveal,step, orloop - Infinite CSS animations are scoped exclusively to
data-motion-mode="loop"elements
This verification ensures that optional animation remains deterministic, accessible, and strictly controlled across all diagrams in the project.
Summary
- Diagram-Design uses a canonical controller in
template-motion.htmlthat must be included verbatim to enable animation. - Four motion modes control behavior:
none(static),reveal(autoplay once),step(interactive), andloop(decorative infinite). - The system is opt-in by default; diagrams remain completely static unless
data-motion-modeis explicitly declared. - Accessibility features include
prefers-reduced-motionsupport, ARIA live regions, and full keyboard navigation. - Verification scripts enforce controller integrity and mode validity through automated linting.
Frequently Asked Questions
What happens if a user has reduced motion preferences enabled?
When the browser reports prefers-reduced-motion: reduce, the controller detects this state (lines 56-70) and immediately disables playback, renders the final static frame, and applies data-motion-state="reduced". This ensures users with vestibular disorders or motion sensitivity receive equivalent information without animation.
Can I modify the animation controller to customize behavior?
No. The verify-motion.py script enforces byte-for-byte equality between your controller and the canonical template-motion.html. Any modification causes verification to fail. This strict policy ensures consistency, predictability, and accessibility compliance across all diagrams in the repository.
Which motion mode should I use for a tutorial diagram with multiple stages?
Use the step mode. This mode provides play/pause/next/previous controls and maintains semantic state through the step and count variables. It supports keyboard navigation and screen reader announcements via ARIA live regions, making it ideal for educational content where users control the pacing.
How does the loop mode differ from reveal mode?
The loop mode uses pure CSS animation on decorative elements that repeat indefinitely without changing meaning, while reveal runs once and stops at a complete static frame. Additionally, reveal may use JavaScript timing for longer sequences, whereas loop relies on CSS alone and is restricted to non-semantic decorative tokens.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →