# How to Export Motion-Enabled HTML Diagrams in a Static State

> Export motion-enabled HTML diagrams as static SVGs or PNGs by appending ?motion=static to your diagram URL. Freeze animations for clean exports with all semantic elements.

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

---

**Appending `?motion=static` to the diagram URL freezes the animation before rasterization, ensuring the exported SVG or PNG contains all semantic elements while hiding motion controls.**

The `cathrynlavery/diagram-design` repository treats diagrams as self-contained HTML files that may include optional motion controllers. When you need to **export motion-enabled HTML diagrams in a static state** for slides, Figma, or documentation, the repository enforces a strict contract that guarantees pixel-perfect fidelity and accessibility compliance.

## Understanding the Motion Controller Architecture

Motion-enabled diagrams in this system rely on declarative attributes rather than imperative JavaScript playback.

### The Template Structure

Every motion diagram starts from [`skills/diagram-design/assets/template-motion.html`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/assets/template-motion.html). This canonical controller must be copied verbatim into any diagram requiring animation support. The template establishes the root element with `data-motion-mode` attributes that define temporal behavior.

### Motion Modes and Data Attributes

The **Animation** reference at [`skills/diagram-design/references/animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/animation.md) defines three motion modes:

- **step**: Advance through states via discrete increments
- **reveal**: Progressive disclosure of diagram layers
- **loop**: Continuous cyclic animation

To freeze these animations for static export, the system asserts that the motion root carries `data-frame="static"` after loading the URL with `?motion=static`. This attribute signals that all semantic elements are visible while decorative motion curves and control chrome are hidden.

## Exporting Static SVG from Motion-Enabled HTML

The SVG export procedure follows the contract outlined in [`skills/diagram-design/references/export.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/export.md):

1. Extract the **first** `<svg>` element using a multiline regex, deliberately dropping surrounding editorial markup (cards, headers, navigation)
2. Ensure standalone XML compliance by injecting `xmlns="http://www.w3.org/2000/svg"` and preserving the original `viewBox`
3. Maintain accessibility metadata by keeping `<title>` and `<desc>` elements
4. Inject a `<defs><style>` block that imports Google Fonts with XML-escaped entities (`&amp;` instead of `&`)
5. Prepend the XML declaration `<?xml version="1.0" encoding="UTF-8"?>`
6. Write the output adjacent to the source ([`diagram.html`](https://github.com/cathrynlavery/diagram-design/blob/main/diagram.html) → `diagram.svg`)

This extraction targets only the diagram itself, ensuring clean reusability across design tools.

## Generating Static PNG with Playwright

For raster output, the exporter renders the **original HTML** rather than the extracted SVG to preserve exact font loading and CSS inheritance:

```python
from playwright.sync_api import sync_playwright
import sys, pathlib

src, out = sys.argv[1], sys.argv[2]
scale = int(sys.argv[3]) if len(sys.argv) > 3 else 2

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(device_scale_factor=scale)
    page.goto(f"file://{pathlib.Path(src).resolve()}?motion=static")
    page.wait_for_load_state("networkidle")
    page.locator("svg").first.screenshot(path=out, omit_background=True)
    browser.close()

```

Key implementation details:
- The `?motion=static` query parameter triggers the static frame mode before `document.fonts.ready` resolves
- `omit_background=True` ensures transparent backgrounds for overlay use
- `device_scale_factor` supports `--scale=1|2|3` for 1x, 2x, or 3x resolution outputs

## Command-Line Interface

Invoke the export workflow via the slash command defined in [`commands/export-diagram.md`](https://github.com/cathrynlavery/diagram-design/blob/main/commands/export-diagram.md):

```bash

# Export both SVG and PNG (default behavior)

/diagram-design:export-diagram path/to/diagram.html

# Export only SVG

/diagram-design:export-diagram path/to/diagram.html --svg-only

# Export high-resolution PNG only

/diagram-design:export-diagram path/to/diagram.html --png-only --scale=3

# Generate registry sidecar metadata

/diagram-design:export-diagram path/to/diagram.html --registry

```

The command validates prerequisites before execution. For PNG generation, it verifies Playwright installation and surfaces exact install instructions if missing (`pip install playwright` followed by `playwright install chromium`).

## Verification and Quality Assurance

Before exporting motion-enabled diagrams, run the verification script to ensure static compliance:

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

```

This script checks that:
- The `data-motion-mode` attribute exists on the root element
- Loading `?motion=static` correctly sets `data-frame="static"`
- The reduced-motion fallback is properly declared

The verification guarantees that exported artifacts match the repository's **accessible-SVG contract**, preventing animated states from leaking into documentation assets.

## Edge Cases and Constraints

The exporter refuses to process files that violate these constraints:

- **Gallery files**: [`assets/index.html`](https://github.com/cathrynlavery/diagram-design/blob/main/assets/index.html) contains multiple diagrams and is explicitly excluded
- **Missing SVG blocks**: Files lacking an `<svg>` element trigger an error
- **Registry-only exports without IDs**: Requests for `--registry` require `data-block-id` attributes on diagram elements to generate valid metadata sidecars

## Summary

- Append `?motion=static` to freeze animations and assert `data-frame="static"` before capture
- SVG extraction isolates the first `<svg>` block, injects XML namespaces, and preserves Google Fonts with escaped entities
- PNG rasterization uses Playwright with `device_scale_factor` scaling and `omit_background=True` for transparency
- Run [`scripts/verify-motion.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-motion.py) to validate static compliance before batch exports
- The export workflow is defined in [`skills/diagram-design/references/export.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/export.md) and [`commands/export-diagram.md`](https://github.com/cathrynlavery/diagram-design/blob/main/commands/export-diagram.md)

## Frequently Asked Questions

### How does the exporter handle fonts in static SVG output?

The extractor parses the HTML's Google Fonts imports and injects them into a `<defs><style>` block within the SVG, ensuring text renders correctly even offline. Ampersands in font URLs are XML-escaped to `&amp;` to prevent parsing errors.

### Why does PNG export load the HTML instead of the extracted SVG?

Rendering the original HTML guarantees that `@font-face` declarations and CSS custom properties resolve exactly as the author intended. This method waits for `document.fonts.ready` and `networkidle`, ensuring pixel-perfect fidelity that direct SVG rasterization might miss due to missing font contexts.

### What motion modes support static export?

The **step**, **reveal**, and **loop** modes defined in [`skills/diagram-design/references/animation.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/animation.md) all support static export. When `?motion=static` is present, the motion controller collapses temporal states into a single comprehensive frame marked by `data-frame="static"`.

### Can I export multiple diagrams from a gallery page?

No. The exporter explicitly refuses to operate on [`assets/index.html`](https://github.com/cathrynlavery/diagram-design/blob/main/assets/index.html) or any file containing multiple diagram roots. You must target individual HTML files containing single `<svg>` elements to ensure clean, isolated static assets.