# Motion Controller Security Contract and Rejected Patterns in Diagram Design

> Explore motion controller security contracts and rejected patterns in diagram design. Learn how static-first security ensures accessibility and prevents vulnerabilities.

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

---

**The Diagram Design skill enforces a strict static-first security contract requiring complete HTML/SVG source availability before animation, permitting only the vetted controller found in [`assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/assets/template-motion.html), and explicitly rejecting patterns like unscoped hiding or infinite animations that compromise accessibility.**

The `cathrynlavery/diagram-design` repository governs optional animated diagrams through a rigorous motion controller security contract designed to ensure deterministic rendering and universal accessibility. This contract mandates that every semantic node, label, and connector exist in the static markup before JavaScript enhancement, while the verification system in [`scripts/verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-motion.py) actively rejects implementations that violate timing budgets, controller uniqueness, or no-JS fallback requirements.

## Static-First Enhancement Contract

The contract defines seven mandatory requirements that every motion diagram must satisfy, ensuring the source remains complete and accessible regardless of JavaScript state.

### Source Completeness and Deterministic Capture

Every semantic node, label, connector, and outcome must be present in the HTML/SVG before any motion is applied. Only selectors under `.motion-ready` may hide or transform items. The final static frame must be exposed via `data-frame="static"` (or query parameter `?motion=static`), and this frame must be pixel-identical on repeated captures without arbitrary delays, as specified in [`references/animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/references/animation.md)【animation.md†L20-L24】【animation.md†L23-L24】.

### CSS-Only Presentation and Timing Budget

Appearance and travel must be expressed exclusively with CSS transitions and keyframes. JavaScript is limited to binding controls, updating step attributes, and managing a single timeout chain. The timing budget enforces specific CSS variables: `--motion-fast: 160ms`, `--motion-step: 480ms`, `--motion-hold: 720ms`, with a total animation time not exceeding 8000ms. All delays derive from integer steps; randomness and spring physics are prohibited【animation.md†L25-L30】【animation.md†L25-L26】.

### Explicit Ordering and Stable End State

Items must be marked with `data-motion-item data-step="N"` for integer steps 1 through 8, with no more than two semantic items sharing a step. Upon completion, all items must become visible (`data-frame="end"`), and replay functionality must reset to step 0 before playing again【animation.md†L26-L28】.

### Scoped State and Fail-Safe Startup

Controls must operate on the nearest `[data-motion-root]`; IDs, timers, and live-status regions must never cross diagram boundaries. The script adds `.motion-ready` only after controls are bound and the initial render succeeds, ensuring a script error leaves the complete source visible rather than a broken partial state【animation.md†L28-L30】.

## Verified Controller Contract

The only permissible JavaScript controller is the exact script embedded in [`assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/assets/template-motion.html). The verifier ([`scripts/verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-motion.py)) extracts the script body from candidate diagrams and compares it—after normalizing line endings—to the canonical controller:

```python
canonical = normalized_controller(open(MOTION_TEMPLATE).read())
if normalized_controller(candidate_body) != canonical:
    errors.append("script must exactly match the controller in template-motion.html")

```

This strict equality check appears in [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py) lines 56-86 and 82-85【verify-motion.py†L56-L86】【verify-motion.py†L82-L85】. The contract enforces three specific constraints:

- **Script count**: At most one `<script>` element is allowed in the document.
- **Attribute check**: The script must carry exactly `data-diagram-controls=""`.
- **Controller equality**: The script body must be identical to the canonical controller byte-for-byte after normalization.

## Rejected Patterns That Violate the Contract

The verifier rejects specific anti-patterns that would break the static-first guarantee or accessibility requirements:

- **Unscoped hiding**: Selectors that hide `data-motion-item` before `.motion-ready` is applied break the no-JS fallback. Detected by the `hidden_unscoped_motion_selectors` function.
- **Infinite animation outside loop mode**: Prevents endless motion that could alter semantic meaning, detected by `infinite_unscoped_selectors`.
- **Non-standard playback controls**: In `step` mode, the diagram must expose exactly one control group with actions `play`, `pause`, `replay`, `prev`, and `next`.
- **Missing ARIA live region**: Motion status must exist as a live region outside the controls to remain visible in reduced-motion mode.
- **Missing reduced-motion or print CSS**: The contract requires CSS fallbacks for `prefers-reduced-motion` and `@media print`.
- **Incorrect SVG accessibility**: SVG elements must have `role="img"` and a first-child `<title>` followed by `<desc>` linked via `aria-labelledby`.
- **Budget violations**: Diagrams with more than 12 motion items, steps exceeding 8, or more than two items per step fail verification checks on `parser.items` and step continuity.

These validations ensure that motion enhancements never obscure the underlying information architecture.

## Implementing Motion Diagrams Safely

Developers must follow a four-step workflow to comply with the security contract:

1. **Copy the canonical template**: `cp skills/diagram-design/assets/template-motion.html my-diagram.html` supplies the required controller, CSS variables, and baseline structure.
2. **Mark semantic items**: Add `data-motion-item data-step="N"` and descriptive `aria-label` attributes to each animated element.
3. **Declare the motion root**: Wrap content in `<main data-motion-root data-motion-mode="step" data-step-count="5">…</main>` to establish scope and mode.
4. **Run verification**: Execute `python3 scripts/verify-motion.py path/to/my-diagram.html` to validate contract compliance before deployment.

The following example demonstrates a compliant step-mode diagram:

```html
<!-- 1️⃣ Start from the canonical template -->
<link rel="stylesheet" href="template-motion.html">

<!-- 2️⃣ Define the motion root with step count -->
<main data-motion-root data-motion-mode="step"
      data-step-count="3" data-frame="static">
  <!-- 3️⃣ Semantic items – each must have an aria-label -->
  <g data-motion-item data-step="1" aria-label="Request received">
    …
  </g>
  <g data-motion-item data-step="2" aria-label="Policy passed">
    …
  </g>
  <g data-motion-item data-step="3" aria-label="Audit logged">
    …
  </g>

  <!-- 4️⃣ Playback controls (required for step mode) -->
  <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>
  </div>

  <!-- 5️⃣ Live status region (must be outside controls) -->
  <p class="sr-only" data-motion-status role="status"
     aria-live="polite" aria-atomic="true"></p>
</main>

```

Validate the implementation using:

```bash

# Verify the diagram against the contract

python3 scripts/verify-motion.py my-diagram.html

# Lint the skin (checks CSS, SVG, and controller identity)

python3 scripts/lint-skin.py my-diagram.html

```

## Summary

- The **static-first contract** requires complete HTML/SVG source visibility before JavaScript activation, ensuring diagrams remain understandable without scripting.
- **Controller verification** enforces byte-for-byte identity with [`assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/assets/template-motion.html), rejecting any unauthorized scripts or modifications.
- **Rejected patterns** include unscoped hiding, infinite animations, incorrect ARIA implementations, and budget overruns (max 8 steps, 12 items, 8000ms total).
- **Implementation** requires using the canonical template, marking items with `data-motion-item`, and passing [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py) before shipping.
- The contract guarantees accessibility compliance across reduced-motion preferences, print media, and no-JS environments.

## Frequently Asked Questions

### What happens if JavaScript fails to load in a motion diagram?

The **fail-safe startup** requirement ensures that the `.motion-ready` class— which controls visibility transitions—is added only after successful controller initialization and binding. If the script errors or fails to load, the class is never applied, leaving all semantic content fully visible in its initial static state (`data-frame="static"`), preserving the diagram's informational integrity.

### Why does the verifier require an exact byte-for-byte match with the canonical controller?

The **verified controller contract** treats [`assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/assets/template-motion.html) as the single source of truth to prevent injection of unauthorized logic, tracking code, or performance-harming animation techniques. By normalizing line endings and comparing the candidate script body against the canonical version in [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py), the system guarantees that only audited, accessibility-compliant code executes within animated diagrams.

### What are the hard limits for motion timing and complexity?

The contract enforces a **timing budget** with CSS variables defining `--motion-fast: 160ms`, `--motion-step: 480ms`, and `--motion-hold: 720ms`, requiring total animation duration to remain under 8000ms. Complexity is constrained to a maximum of 8 steps, 12 total motion items, and no more than two semantic items per step, as validated by the `parser.items` checks in [`verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/verify-motion.py).

### How does the contract support users with motion sensitivity or print requirements?

The **rejected patterns** explicitly include missing `prefers-reduced-motion` and `@media print` CSS fallbacks. Additionally, diagrams must include an ARIA live region outside the control set to announce status changes, ensuring that users who disable animations or print the page receive the complete semantic content without relying on the motion controller.