# How the Motion Controller Works in diagram-design and Why It Uses a Pinned Implementation

> Learn how the diagram-design motion controller works and why its pinned implementation ensures consistent animation accessibility and security. Discover the technical details.

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

---

**The diagram-design motion controller is a single, static-by-default JavaScript implementation housed in [`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html) that enforces strict markup constraints through [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py) to guarantee consistent animation behavior, accessibility, and security across all diagrams.**

The `cathrynlavery/diagram-design` repository provides a specialized skill for creating animated technical diagrams with controlled motion. At its foundation lies a **motion controller** that is intentionally pinned to a specific implementation, preventing fragmentation of animation logic and ensuring every diagram meets strict standards for performance and accessibility.

## Architecture of the Pinned Motion Controller

The motion system relies on a centralized controller file rather than distributed scripts. This architecture mandates that all motion-related logic emanates from a single source.

### The Controller Implementation in template-motion.html

The sole JavaScript controller resides in [[`skills/diagram-design/assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/template-motion.html)](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/template-motion.html). This file contains the complete logic for parsing motion attributes, managing animation timing, and generating playback controls. When a diagram includes motion markup, the `DiagramParser` builds a tree structure of roots, items, scripts, and controls, but defers all manipulation to the pinned controller.

The controller exposes standardized CSS custom properties such as `--motion-fast` and `--motion-step` to ensure consistent timing across all diagram instances. It also injects the interactive control layer—play, pause, replay, and step navigation—dynamically into the DOM.

### Validation Through verify-motion.py

Before any diagram deploys, the [[`scripts/verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-motion.py)](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-motion.py) linter validates the markup against a strict contract. The script enforces:

- **Exactly one root element**: The markup must contain precisely one `data-motion-root` attribute, establishing the pinned controller anchor.
- **Hierarchy constraints**: All `data-motion-item` elements must be children of the root.
- **Numerical rigor**: Steps must be numbered with integer `data-step` values.
- **Budget limits**: No more than 12 motion items and 8 steps per diagram.
- **Script isolation**: Any additional or modified controller scripts cause the linter to fail, preserving the single-implementation constraint.

## Why the Motion Controller Is Pinned to a Specific Implementation

Pinning the controller to the [`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html) implementation is an intentional architectural decision documented in [ADR 0001](https://github.com/cathrynlavery/diagram-design/blob/main/docs/adr/0001-static-by-default-single-pinned-controller.md). This design delivers three critical guarantees.

### Predictable Behavior and Timing Variables

By restricting motion logic to one controller, the system ensures that `--motion-fast`, `--motion-step`, and interaction models (reveal, play, pause) behave identically across every diagram. Developers cannot introduce conflicting timing functions or asynchronous scripts that desynchronize animations.

### Accessibility Guarantees

The pinned controller emits a dedicated status region with `role="status"` and `aria-live="polite"` to screen readers, announcing step changes without requiring additional markup from authors. It also detects `prefers-reduced-motion` and automatically degrades to a complete static fallback, ensuring the diagram remains comprehensible when animation is disabled or when generating PNG exports.

### Security and Consistency

The skin linter actively rejects any extra scripts or attempts to override the controller. This prevents injection of unsafe JavaScript and eliminates "controller drift" where different diagrams might implement incompatible animation patterns. The single-source truth in [`template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/template-motion.html) acts as a security boundary and a consistency enforcement mechanism.

## Implementation Details and Code Examples

To enable motion, authors add specific data attributes to their HTML. The controller attaches automatically during the parsing phase.

### Enabling Motion for a Diagram

```html
<div data-motion-root data-motion-mode="reveal">
  <div data-motion-item data-step="1">First step</div>
  <div data-motion-item data-step="2">Second step</div>
  <!-- controls are auto-generated by the pinned controller -->
</div>

<script src="template-motion.html"></script>

```

### Static Fallback for Export

When `prefers-reduced-motion` is active or for static file generation, the markup degrades gracefully:

```html
<div data-motion-root data-frame="static" data-motion-mode="none">
  …complete diagram markup…
</div>

```

This fallback ensures that export pipelines and accessibility tools receive a fully rendered diagram without requiring JavaScript execution.

## Summary

- The **motion controller** lives exclusively in [`skills/diagram-design/assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/template-motion.html) and is the only permitted source of animation logic.
- **[`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py)** enforces a strict contract: one root, ≤12 items, ≤8 steps, and no extraneous scripts.
- Pinning the implementation ensures **predictable timing**, **accessibility compliance** (including reduced-motion support), and **security** against script injection.
- The system provides a **static-by-default** architecture where motion is opt-in (`?motion=run`) and always offers a complete fallback for non-JavaScript contexts.

## Frequently Asked Questions

### What file contains the motion controller logic?

All motion controller logic resides in [[`skills/diagram-design/assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/template-motion.html)](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/template-motion.html). This single file handles parsing, timing, and interaction controls.

### Why can't I use a custom JavaScript animation library?

The architecture explicitly forbids external controllers to guarantee consistent behavior and security. According to [ADR 0001](https://github.com/cathrynlavery/diagram-design/blob/main/docs/adr/0001-static-by-default-single-pinned-controller.md), allowing multiple implementations would fragment accessibility guarantees and violate the static-by-default principle. The [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py) linter will fail any build that includes unauthorized scripts.

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

The pinned controller monitors `prefers-reduced-motion` and immediately degrades to a static representation. It also ensures a complete markup fallback is always available during initial render, satisfying both accessibility requirements and static export pipelines without requiring user-side JavaScript.

### What are the limits on animation steps and items?

The validation script enforces a hard limit of **12 motion items** and **8 steps** per diagram. These constraints prevent performance degradation on low-power devices and ensure that complex animations remain cognitively manageable for users navigating via keyboard or assistive technology.