# Getting started pele-animate makes a [Pele](https://pele.run) diagram explain itself one step at a time. You choose the order things appear in, readers step through it with buttons or the keyboard, and any node can carry an icon from a large set. It is an unofficial add-on for Pele and does not change how Pele lays out or draws diagrams. ## Install ```sh npm i pele-animate pele # or pnpm add pele-animate pele yarn add pele-animate pele bun add pele-animate pele ``` `pele` is a peer dependency (`^1.7.0`): install it alongside. Both run in the browser and need the DOM, so mount them on the client (see [recipes](./recipes.md) for frameworks). The packages are ESM only. ## First diagram Put the Mermaid source in an element, then mount it with Pele and hand it to `watch()`: ```js import { mount } from 'pele'; import { watch, prepare } from 'pele-animate'; import { icons } from 'pele-animate/glyphs'; import 'pele-animate/tabler/computers'; // icons: one Tabler category, import as many as you need import 'pele-animate/style.css'; const el = document.querySelector('pre.mermaid'); const src = el.textContent; mount(el, prepare(src), { icons }); // Pele draws the SVG watch(el, src); // pele-animate animates it, and re-applies when Pele re-lays-out on resize ``` The Mermaid source: ```mermaid-icons live title="Icons: request, load balancer and cloud service" flowchart LR %% pa: 1=C 2=C>L 3=L>S 4=S>L 5=L>C C@{ icon: "http-get", form: "square", h: 88, label: "Request" } L@{ icon: "load-balancer", form: "square", h: 88, label: "Load balancer" } S@{ icon: "cloud-computing", form: "square", h: 88, label: "Cloud service" } C -->|request| L L -->|forward| S S -->|response| L L -->|reply| C ``` What you get: - The intro plays once when the diagram scrolls into view: nodes appear in order, edges draw in, labels fade in. - Hover or focus: small tokens travel along the edges. - Click, or focus and press Enter: **step mode**. Previous, play/pause and next buttons appear, and Space, ←, → and Esc work. - With `prefers-reduced-motion`, the motion is off and the finished picture shows. The `%% pa: 1=C 2=C>L 3=L>S 4=S>L 5=L>C` comment pins the order: the request first, then each leg of the round trip. Without it, the order follows the edges. See [step order](./step-order.md). ## Next - Pick icons: [icons](./icons.md). - Control the order: [step order](./step-order.md). - Match your site's colors: [theming](./theming.md). - Upgrading from 0.12: [migrating](./migrating.md). --- # How it works Two layers, one diagram: 1. **Pele** parses the Mermaid, lays the diagram out and returns an SVG. That is all Pele's job; pele-animate does not touch layout, sizing or syntax. 2. **pele-animate** reads that SVG after Pele draws it, works out the order things should appear in, and adds the motion, the step playback and the icons. The Mermaid source stays valid Mermaid, so GitHub or Obsidian still show a normal diagram. ## The calls - `mount(el, prepare(src), { icons })` is Pele's. The `icons` option is how glyphs get in (see [icons](./icons.md)). - `watch(el, src)` is pele-animate's. It marks the container as a scene (class `pa-scene`), animates each SVG Pele renders into it, and re-applies after Pele re-draws on resize. The intro is skipped on those re-draws once it has played. - `animate(svg, { source })` does one SVG. Use it if you render with Pele yourself and do not want `watch()`. - `replay(svg)` plays the intro again. ## The three kinds of motion | Motion | When | Notes | |---|---|---| | **Intro** | Once, when the diagram is 30% in view | Nodes appear by step, each edge draws from its source, labels fade in. A long diagram squeezes its steps so the intro stays near 3 seconds. | | **Tokens** | While hovering or focusing the diagram | Small dots travel along the edges in sequence. | | **Step mode** | Click, or Enter | You move through the same steps one at a time. See [step order](./step-order.md). | The resting state is always the finished picture. Everything honors `prefers-reduced-motion`. ## Where the order comes from Order is a list of **steps**. By default, a flowchart's steps are the breadth-first levels from the nodes that nothing points to; a sequence diagram's steps are its actors, then its messages in order. A `%% pa:` comment overrides that. After `animate()` runs, `svg.paSteps` holds one array per step, which is also what step mode uses. ## What it depends on in Pele pele-animate selects on class names in Pele's SVG (`.pele-node`, `.pele-edge`, and a few others) and on the `data-id` attribute. Pele is a peer dependency so you control its version. See [compatibility](./compatibility.md). --- # Diagram types pele-animate animates these Mermaid diagram types. Each one plays an intro when it scrolls into view and, on click, steps through its items with previous / play / next controls. | Type | Each step reveals | Pins, icons | Remarks | |---|---|---|---| | [Flowchart](#flowcharts)
`flowchart` | Nodes level by level, edges from their source | Pins and icons | The fullest support: groups, edge labels, large icon nodes. | | [Sequence diagram](#sequence-diagrams)
`sequenceDiagram` | Actors, then one message at a time | Neither | The order is the source order; reordering messages changes what the diagram says. | | [Architecture](#architecture-diagrams)
`architecture-beta` | Services level by level, edges from their source | Pins and icons | Icons are written `(name)` and are small and fixed in size. | | [State diagram](#state-diagrams)
`stateDiagram-v2` | States and transitions from the start marker | Pins | Markers are `root_start` and `root_end`. Composite states are untested. | | [C4 context](#c4-context-diagrams)
`C4Context` | People and systems, then relationships | Pins | Only `C4Context` is checked. Keep `Rel` labels short. | | [Block diagram](#block-diagrams)
`block-beta` | Blocks and arrows from their source | Pins | Put linked blocks in the same row or column. | | [Class diagram](#class-diagrams)
`classDiagram` | Classes and relationships, from the class written first | Pins | `A <\|-- B` reveals `A`, then `B`. | | [ER diagram](#er-diagrams)
`erDiagram` | Entities and relationships, from the entity written first | Pins | Same written-order rule as class diagrams. | | [Timeline](#timelines-journeys-git-graphs-and-gantt-charts)
`timeline` | One time period with its events | Neither | Section headers stay visible. | | [User journey](#timelines-journeys-git-graphs-and-gantt-charts)
`journey` | One task, its face and the line joining it to the last | Neither | | | [Git graph](#timelines-journeys-git-graphs-and-gantt-charts)
`gitGraph` | One commit with its edge; a branch with its first commit | Neither | Cherry-picks and tags are untested. | | [Gantt chart](#timelines-journeys-git-graphs-and-gantt-charts)
`gantt` | One task, top to bottom | Neither | `todayMarker` and parallel tasks are untested. | Pins are `%% pa:` comments; see [step order](./step-order.md). Types without pins use their source order. Icons are set up in [icons](./icons.md). Pele renders the other Mermaid types too (pie, quadrant, radar and the rest), but pele-animate is not built for them; see [Everything else](#everything-else). ## Flowcharts Nodes appear in step order, edges draw from their source node, and edge labels fade in. Groups (`subgraph`) and their titles are kept. Icon nodes (`@{ icon }`) work here; see [icons](./icons.md). The pins below put "Serve" (the cache hit) before "Fetch", then draw the `Fetch` to `Serve` edge as its own last step. Without them, `Serve` and `Fetch` would appear together. ```mermaid live title="Flowchart: cache hit or miss, with pins and an edge pin" flowchart LR %% pa: 1=A 2=B 3=C 4=D 5=D>C A[Client] --> B{Cached?} B -->|yes| C[Serve] B -->|no| D[Fetch] --> C ``` ## Sequence diagrams Actors appear first, then one step per message in source order, so a cache miss reads as five short steps. Participant boxes in a row get the same height, and group titles are bold. ```mermaid live title="Sequence: API request with a cache miss" sequenceDiagram Client->>API: request API->>Cache: lookup Cache-->>API: miss API->>Database: query Database-->>API: rows API-->>Client: response ``` **Limit:** Pele ignores `@{ icon }` on sequence participants (the box shows its id), so icons apply to flowcharts only. ## Architecture diagrams `architecture-beta`: services in groups, joined by edges. Services appear in step order like flowchart nodes, groups stay visible, and `%% pa:` pins work the same way. The icon in parentheses is looked up in your icon packs, so Tabler names and `doverunner-*` names work (import the packs as for [icons](./icons.md)). The pins show the services first (the request path, then what sits behind the API), then trace the request one edge at a time with edge pins (`3=user>cdn`). ```mermaid-icons live title="Architecture: request path through CDN, API, cache, database and worker" architecture-beta %% pa: 1=user,cdn,api 2=cache,db,queue,worker 3=user>cdn 4=cdn>api 5=api>cache 6=api>db 7=api>queue 8=queue>worker group edge(cloud)[Edge] group core(cloud)[Core] service user(device-desktop)[User] service cdn(cloud-computing)[CDN] in edge service api(server)[API] in core service cache(bolt)[Cache] in core service db(database)[Database] in core service queue(mail)[Queue] in core service worker(settings)[Worker] in core user:R --> L:cdn cdn:R --> L:api api:T --> B:cache api:B --> T:db api:R --> L:queue queue:R --> L:worker ``` Icons here are small and fixed in size; `form` and `h` apply to flowcharts only. Edges are read from their written direction (`user:R --> L:cdn` is user to cdn). A node id containing `-` can be mistaken for two ids on an edge, so use plain ids. ## State diagrams `stateDiagram-v2`: states appear in step order, transitions draw from their source state, and transition labels fade in. The start and end markers are nodes too, with the ids `root_start` and `root_end`, so pins can name them. ```mermaid live title="State: media player with loading, error and retry" stateDiagram-v2 %% pa: 1=root_start 2=Idle 3=Loading 4=Playing 5=Paused 6=Error 7=Error>Idle 8=root_end [*] --> Idle Idle --> Loading: play Loading --> Playing: ready Playing --> Paused: pause Paused --> Playing: resume Loading --> Error: fail Error --> Idle: retry Playing --> [*]: stop ``` The pins give each state its own step, in the order a viewer meets them, and the `retry` transition (`Error>Idle`) its own step after `Error` is on screen. Without pins the order is breadth-first from the start marker, which shows states at the same distance together (here `Playing` and `Error`). Pele gives state edges no readable ids, so pele-animate finds each transition's ends from where its path starts and stops. Composite states and forks are untested. State diagrams take no icons. ## C4 context diagrams `C4Context`: people and systems appear in step order and each `Rel` draws from its source, with its label. Pins use the ids you give `Person(...)` and `System(...)`; an edge pin is `3=app>drm`. The example puts the landscape on screen first, then draws the relationships one at a time, then adds analytics and the operator who reads it. ```mermaid live title="C4 context: player app and the systems around it" C4Context %% pa: 1=viewer,app,drm,cdn 2=viewer>app 3=app>drm 4=app>cdn 5=analytics,admin 6=app>analytics 7=admin>analytics Person(viewer, "Viewer", "Watches video") System(app, "Player app", "Plays protected video") System_Ext(drm, "DRM service", "Issues licenses") System_Ext(cdn, "CDN", "Delivers segments") System(analytics, "Analytics", "Tracks playback") Person(admin, "Operator", "Reviews usage") Rel(viewer, app, "watches") Rel(app, drm, "license") Rel(app, cdn, "segments") Rel(app, analytics, "events") Rel(admin, analytics, "reviews") UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ``` Pele lays C4 diagrams out in rows, so a long label between two boxes in the same row can leave no room for the arrowhead: keep `Rel` labels short. Only `C4Context` has been checked; the other C4 types and boundaries are untested. C4 shapes take no icons. ## Block diagrams `block-beta`: blocks appear in step order and each arrow draws from its source block. Pins use the block ids. ```mermaid live title="Block: video pipeline with a storage branch" block-beta columns 4 %% pa: 1=src 2=enc 3=pkg 4=cdn 5=store src["Source"] enc["Encode"] pkg["Package"] cdn["CDN"] space space store[("Storage")] space src --> enc enc --> pkg pkg --> cdn pkg --> store ``` Pele routes arrows between blocks in straight lines or short bends, so place linked blocks in the same row or column; a diagonal neighbour can give a bent arrow that runs along another one. Block diagrams take no icons. ## Class diagrams `classDiagram`: classes appear in step order, relationships draw from the class written first. `Media <|-- Video` reveals `Media` and then `Video`, whichever way the arrowhead points. Pins use the class names; the example shows the base class, its subtypes, the inheritance arrows as their own step, then the classes that use them. An edge pin may name the ends in either order, so `Video>Media` follows the arrow while the source says `Media <|-- Video`. ```mermaid live title="Class: media types, player and license" classDiagram %% pa: 1=Media 2=Video,Audio 3=Video>Media,Audio>Media 4=Player 5=License 6=Session Media <|-- Video Media <|-- Audio Player --> Media : plays Player --> License : needs License --> Session : opens Media : +id Media : +title Player : +play() License : +expiry ``` ## ER diagrams `erDiagram`: entities appear in step order, relationships draw from the entity written first (`CUSTOMER ||--o{ ORDER` reveals `CUSTOMER`, then `ORDER`). The example reads from the customer down to the supplier. ```mermaid live title="ER: customer orders and product suppliers" erDiagram %% pa: 1=CUSTOMER 2=ORDER 3=LINE 4=PRODUCT 5=SUPPLIER CUSTOMER ||--o{ ORDER : places ORDER ||--|{ LINE : contains LINE }o--|| PRODUCT : lists PRODUCT }o--|| SUPPLIER : "supplied by" ``` Neither takes icons. ## Timelines, journeys, git graphs and Gantt charts These have no edges to follow, so each steps through its items in reading order and `%% pa:` pins do nothing (as for sequence diagrams). Section headers, axes, grids and legends stay visible. - **`timeline`:** one step per time period, with its events. List periods in time order. ```mermaid live title="Timeline: product history in two phases" timeline title Product history section Build 2019 : Prototype 2020 : Private beta : First customers section Grow 2021 : Public launch 2023 : Mobile apps : Offline mode 2025 : Global rollout ``` - **`journey`:** one step per task: the task, its face, and the line segment that joins it to the task before. ```mermaid live title="User journey: watching a protected video" journey title Watching a protected video section Find Open the app: 5: Viewer Pick a title: 4: Viewer section Play Wait for the license: 2: Viewer, App Playback starts: 5: Viewer section Finish Resume later: 4: Viewer Rate the title: 3: Viewer ``` - **`gitGraph`:** one step per commit in the order written, with the edge that ends on it. A branch's label and lane appear with its first commit. ```mermaid live title="Git graph: feature branch, merge and release" gitGraph commit id: "init" branch feature commit id: "api" commit id: "ui" checkout main commit id: "fix" merge feature id: "m" branch release commit id: "v1" ``` - **`gantt`:** one step per task, top to bottom: write tasks in the order you want them to appear. ```mermaid live title="Gantt: plan, build and ship" gantt dateFormat YYYY-MM-DD section Plan Scope :a1, 2024-01-01, 3d Design :after a1, 4d section Build Code :b1, 2024-01-08, 8d Review :after b1, 2d section Ship Test :2024-01-18, 4d Release :2024-01-22, 1d ``` None of these take icons. Only these small examples have been checked in a browser; long diagrams, `todayMarker`, tags, cherry-picks and parallel tasks are untested. ## Everything else Other Mermaid types (pie, quadrant, radar and the rest) render through Pele as usual. pele-animate is not designed for them and its behavior on them is untested, so use plain Pele for those. Step mode has been checked in a browser on the types above only; sequence diagrams have not been checked the same way yet. --- # Icons A flowchart node becomes an illustrated node with Mermaid's own `@{ }` syntax: ```mermaid-icons live title="Icons: laptop sending a request to a server" flowchart LR A@{ icon: "device-desktop", form: "square", h: 88, label: "Laptop" } B@{ icon: "server", form: "square", h: 88, label: "Server" } A -->|request| B ``` Both names are in the Tabler `devices` category, so this needs `import 'pele-animate/tabler/devices'`. - `icon` is the glyph name. `label` is the caption. - `form: "square", h: 88` makes a large frameless icon. Leave both out for a small icon inside a frame. - Call `mount(el, prepare(src), { icons })` with `prepare` from `pele-animate`. Pele sizes an icon node from its label, so a label shorter than `h` would put edge ends and arrowheads under the icon; `prepare` pads the label so the box fits. - Unknown names get a neutral rounded square, never a blank slot. Pele ships no icons of its own, so every name must come from a pack you import. ## Packs Icons come from packs. Importing a pack module registers its icons. | Pack | Import | Contents | |---|---|---| | **Tabler categories** | `pele-animate/tabler/` | 4,368 outline icons in 36 categories, from [Tabler Icons](https://github.com/tabler/tabler-icons) 3.49.0 (MIT) | | **doverunner-\*** (optional) | `pele-animate/preset-doverunner` | 20 filled, two-tone illustrations named `doverunner-` (see below) | | **Your own** | `registerGlyphs()` | See [custom icons](./custom-icons.md) | Pass the resolver to Pele: `mount(el, src, { icons })` with `import { icons } from 'pele-animate/glyphs'`. ### Tabler categories Import only the categories you use; each is its own module (the largest, `system`, is about 199 KB). | Category | Icons | Category | Icons | Category | Icons | |---|---|---|---|---|---| | system | 784 | map | 263 | food | 96 | | devices | 363 | document | 209 | buildings | 95 | | design | 350 | text | 194 | vehicles | 90 | | arrows | 333 | shapes | 187 | math | 88 | | media | 160 | e-commerce | 155 | health | 87 | | communication | 121 | sport | 76 | currencies | 76 | | development | 63 | weather | 61 | photography | 52 | | mood | 52 | nature | 50 | games | 48 | | computers | 44 | database | 42 | laundry | 40 | | charts | 39 | symbols | 37 | badges | 22 | | electrical | 19 | animals | 18 | gestures | 17 | | version-control | 16 | extensions | 13 | logic | 8 | Brand, Letters, Numbers, Zodiac and Gender are not included. Counts are for Tabler 3.49.0. ### Finding a name Do not guess names. Look them up: - The [icon browser](https://pele-animate.danielkimdev.com/icons/) searches names and tags and copies a ready snippet; the same list is [icons.json](https://pele-animate.danielkimdev.com/icons.json). - Tabler's own site, [tabler.io/icons](https://tabler.io/icons), lists names and categories (use the outline style). - In your project: `node_modules/pele-animate/tabler/.js` lists every name in that category as an object key. - Names that exist in more than one place are not a problem: Tabler names are plain (`server`, `lock`), `doverunner-*` names are prefixed. ## Stroke width Tabler icons are line icons drawn to work at any stroke width. Set the CSS variable `--pa-stroke` on the container: ```css .pa-scene { --pa-stroke: 1; } ``` The default is `1.5`. Tabler draws at 2. The `doverunner-*` glyphs have a fixed stroke width. ## The optional doverunner-* pack `pele-animate/preset-doverunner` ships 20 filled, two-tone glyphs in the style of the DoveRunner Lab product cards, named `doverunner-`: `player`, `server`, `key`, `document`, `shield`, `phone`, `cloud`, `package`, `token`, `lock`, `database`, `video`, `media`, `encoder`, `storage`, `plugin`, `variants`, `watermark`, `bits-a`, `bits-b`. Import it only if you want that look; nothing else needs it. --- # Custom icons Register your own icons with `registerGlyphs`: ```js import { registerGlyphs, S, L } from 'pele-animate/glyphs'; registerGlyphs({ 'my-box': ``, }); ``` Then use `icon: "my-box"` in your Mermaid. ## The contract - A glyph is the **inner markup** of a 24 by 24 SVG: shapes only, no `` wrapper. Draw inside the 24 by 24 box. - Colors: use `S` (solid) and `L` (light), which are the CSS variables `--pa-solid` and `--pa-light` with fallbacks, plus `W` for white. Then the glyph follows light and dark themes (see [theming](./theming.md)). - `registerGlyphs(pack, strokeWidth = 1.1)` wraps each glyph in a group with that stroke width. Pele draws icons at stroke width 2, which looks heavy on a large icon, so glyphs get a thinner one. - Line icons that should respond to `--pa-stroke` (as the Tabler packs do) can use `registerTabler(pack, strokeWidth = 1.5)` instead. It sets fill none, round caps and joins, and the stroke color for you. ## Names Registering a name that already exists replaces it. Choose a prefix for your own pack (`acme-box`) so your names never clash with Tabler's plain names. ## Safety Glyph markup is inserted into the page as SVG. Only register markup you trust. See [security](./security.md). --- # Step order Every diagram is a list of **steps**. The intro plays them one after another, and step mode lets the reader move through them. By default, the order follows the edges. A `%% pa:` comment lets you set it yourself. ## Default order - **Flowchart:** nodes appear by level, counting from the nodes nothing points to. Each edge draws from its source once the source is on screen, and its target appears as the edge lands. Branches of a decision appear together. - **Architecture diagram:** like a flowchart: services appear by level, edges follow their written direction, pins work the same way. - **Timeline, journey, git graph, Gantt chart:** one step per item in reading order (period, task, commit, task); pins do nothing. - **Class and ER diagrams:** like a flowchart, but a relationship runs from the class or entity written first. - **Block diagram:** like a flowchart, from the blocks nothing points to. - **C4 context diagram:** like a flowchart, from the people and systems nothing points to. - **State diagram:** like a flowchart, starting from the start marker (`root_start`); the end marker is `root_end`. - **Sequence diagram:** actors first, then the messages in order. ## Pins Add one comment line anywhere in the source: ```mermaid live title="Pins: request, validate, respond with a shortcut" flowchart LR %% pa: 1=A 2=B 3=C A[Request] --> B[Validate] --> C[Respond] A --> C ``` `%% pa: 1=A 2=B,C` means: step 1 shows A, step 2 shows B and C. Steps are numbered from 1. Anything you do not pin keeps its default level. ### Edge pins `X>Y` pins the edge from X to Y to its own step, so an edge between two nodes that are already visible can be a step: ```mermaid live title="Edge pins: a reply as its own step" flowchart LR %% pa: 1=A,B 2=C 3=A>B A[Client] --> B[Server] B --> C[Database] B --> A ``` Rules that avoid surprises: - **Pin the nodes first.** An edge pinned before its target appears lands early. - Edge pins work in every diagram type that has edges, not only flowcharts. - A two-way edge (`A <--> B`) is pinned in the direction you wrote it first (`A>B`). - The ends may be named in either order (`Video>Media` or `Media>Video`), unless the diagram also has the opposite edge: then the order picks which of the two is meant. - Do not rename node ids after pinning: pins match ids. - Translated pages keep the same ids, so copy the `%% pa:` line across languages after checking the ids match. ## Step mode Click the diagram, or focus it and press Enter or Space. A small bar appears with previous, play/pause and next. | Key | Action | |---|---| | Space | Play or pause | | → | Next step | | ← | Previous step | | Esc | Leave step mode | Pass `{ controls: false }` as the third argument of `watch()` to turn step mode off; a click then just replays the intro. ## Checking your order After `animate()` runs, `svg.paSteps` is one array per step. Print what each step shows: ```js const svg = document.querySelector('.pa-scene svg.pele'); svg.paSteps.map((items, n) => `${n + 1}: ` + items.map(([el]) => el.textContent.trim() || el.dataset.id).join(' ; ')); ``` `parseOrder(source)` returns the parsed pins as a `Map` (node id or `'X>Y'` to a 0-based step), if you want to check them without a browser. --- # Theming `watch()` adds the class `pa-scene` to the container. That class gives the diagram its tinted panel and its colors. The defaults are written with `:where()`, so any rule in your stylesheet wins without a specificity fight. ## Variables Set these on the container (or any ancestor): | Variable | What it controls | Default (light) | |---|---|---| | `--pa-bg` | Panel background | `#efeafa` | | `--pa-solid` | Strong color: lines, icon strokes, accents | `#4F378B` | | `--pa-light` | Light fill: icon fills, group surfaces | `#e4dcf6` | | `--pa-stroke` | Stroke width of Tabler icons | `1.5` | | `--pele-*` | Any of Pele's own variables (`--pele-fg`, `--pele-surface`, ...) | Set from the `--pa-*` values | ```css .pa-scene { --pa-bg: #f6f6f0; --pa-solid: #0b5fff; --pa-light: #dbe7ff; --pa-stroke: 1.25; } ``` For the full list of `--pele-*` variables, see Pele's [theming docs](https://pele.run). ## Dark mode Dark colors apply when the system prefers dark, unless the page sets `data-theme="light"` on the root element. To force dark, set `data-theme="dark"` on the root element. Both follow the same variables, so overriding `--pa-solid` and friends works in either mode. ## Reduced motion With `prefers-reduced-motion: reduce`, the intro and the tokens do not play: the finished picture shows. ## Scene frame The panel, its padding and rounded corners come from `.pa-scene`. Override `border-radius`, `padding` or `margin` on it directly. --- # Recipes Pele and pele-animate need the DOM, so in every framework the rule is the same: **mount on the client, after the page has rendered.** Load them with dynamic `import()` inside the framework's mounted hook so server rendering never touches them. The shape is always: ```js const [{ mount }, { watch, prepare }, { icons }] = await Promise.all([ import('pele'), import('pele-animate'), import('pele-animate/glyphs'), import('pele-animate/tabler/computers'), // your icon categories import('pele-animate/style.css'), ]); mount(el, prepare(src), { icons }); watch(el, src); ``` | Framework | Status | Where the code goes | |---|---|---| | Plain HTML / Vite | Tested | A module script, as in [getting started](./getting-started.md) | | Astro | Tested in production (the DoveRunner docs site) | A client `
` or `
`) holding the Mermaid source, and call `mount` and `watch` once per element. - If the page changes without a reload (client-side routing), mount again for the new elements. - Importing the CSS (`pele-animate/style.css`) once is enough. In TypeScript, a CSS side-effect import needs a `*.css` module declaration (Vite projects get it from `vite/client`). - Static site generators are fine: nothing here needs a server. ## Astro ```astro


```

## SvelteKit

```svelte

``` --- # API reference TypeScript types ship with the package (`types/`). ## `pele-animate` | Export | Signature | Description | |---|---|---| | `watch` | `watch(container: HTMLElement, source?: string, options?: { controls?: boolean }): void` | Marks the container as a scene (`pa-scene`), animates each SVG Pele renders into it and re-applies after Pele re-lays-out on resize. `controls: false` keeps click-to-replay instead of step mode. | | `prepare` | `prepare(source?: string): string` | Returns the Mermaid source with icon-node labels padded so Pele lays out a box at least as wide as the icon (`h`). Pass its result to `mount()` and the original source to `watch()`. Without it, a label shorter than the icon puts edge ends and arrowheads under the icon. | | `animate` | `animate(svg: SVGSVGElement, options?: { hover?: Element; source?: string }): void` | Animates one rendered svg. `source` is read for `%% pa:` pins. Sets `svg.paSteps`. | | `replay` | `replay(svg: SVGSVGElement \| null \| undefined): void` | Plays the intro again. | | `parseOrder` | `parseOrder(source?: string): Map` | Parses the `%% pa:` comment: node id, or `'X>Y'` for an edge, to a 0-based step. | `svg.paSteps` (type `PaStep[]`, where `PaStep = Array<[element: Element, parts: Element[]]>`) is set by `animate()`: one array per step. ## `pele-animate/glyphs` | Export | Signature | Description | |---|---|---| | `icons` | `icons(name: string): string` | Pele's `icons` resolver. Unknown names return a neutral rounded square. | | `registerGlyphs` | `registerGlyphs(pack: Record, strokeWidth?: number): void` | Adds glyphs: `{ name: '' }`. Default stroke width 1.1. | | `registerTabler` | `registerTabler(pack: Record, strokeWidth?: number): void` | Adds outline icons whose stroke width follows `--pa-stroke` (fallback `strokeWidth`, default 1.5). | | `S`, `L`, `W` | `string` | Theme color expressions for glyph markup: solid, light, white. | ## `pele-animate/tabler/` Importing a module registers that category's icons. It also exports `pack: Record` (name to inner markup). Categories are listed in [icons](./icons.md). ## `pele-animate/preset-doverunner` Importing it registers the 20 `doverunner-*` glyphs. No exports. ## `pele-animate/style.css` The scene frame, motion and step controls. Import it once. ## Classes you may see `pa-scene` (container), `pa-ready` (an animated svg), `pa-in` and `pa-done` (intro states), `pa-stepping` (step mode), `pa-controls` (the button bar). These are internal and may change; prefer the CSS variables in [theming](./theming.md) over targeting them. --- # Compatibility | | Supported | |---|---| | Pele | `^1.7.0` (peer dependency). Tested with 1.7 (the docs site), 1.8 and 1.9. | | Diagram types | Flowcharts and sequence diagrams ([diagram types](./diagram-types.md)) | | Browsers | Current browsers with the Web Animations API, `IntersectionObserver` and `matchMedia` (Chrome, Edge, Firefox, Safari). Without `IntersectionObserver` the intro plays at once. | | Module format | ESM only | | Server rendering | Not applicable: mount on the client ([recipes](./recipes.md)) | ## What pele-animate depends on in Pele pele-animate reads the SVG Pele produces. It selects on these class names and attributes: `.pele-node`, `.pele-edge`, `.pele-edge-label`, `.pele-marker`, `.pele-cluster-label`, `.pele-icon`, `.pele-lifeline`, `.pele-actor-participant`, and the `data-id` attribute. Pele has not promised these are stable across releases (the question is open upstream). That is why Pele is a peer dependency: upgrade it deliberately, and re-check your diagrams afterwards. The repository's tests render every documentation example, which catches parse errors but not visual changes. ## Known limits - Sequence-diagram participants ignore `@{ icon }`. - Step mode is browser-tested on flowcharts only. - Glyph art is square-boxed; wide art is drawn as a wide shape inside the square. --- # Accessibility What is in place: - **Reduced motion.** `prefers-reduced-motion: reduce` turns off the intro and the tokens. The finished diagram shows. - **Keyboard.** The diagram container is focusable (`tabindex="0"`). Enter or Space starts step mode; then Space plays or pauses, ← and → step, and Esc leaves. - **Step controls.** The bar has `role="toolbar"` with an accessible name, and its buttons have labels. Previous and next use `aria-disabled` at the ends. - **Focus outline.** A visible focus ring uses the `--pa-solid` color. What has **not** been checked: a pass with a screen reader, and contrast ratios of custom palettes. Treat the diagram as an illustration and keep the key information in the surrounding text as well, as with any diagram. The text of the Mermaid source is not a substitute for a description. --- # Security - **Diagram text.** Pele escapes diagram text, filters links and styles, and limits source and output size. Read Pele's [security page](https://pele.run/security) before rendering diagrams you did not write. pele-animate does not weaken any of that: it reads the SVG after Pele renders it. - **Pins.** The `%% pa:` comment is parsed with a simple pattern (step numbers and ids). It cannot run code. - **Glyph markup is trusted.** `registerGlyphs()` and `registerTabler()` insert your markup into the page as SVG. Register only markup you wrote or reviewed. The bundled Tabler and `doverunner-*` packs are static files in the package. - **No network.** pele-animate makes no requests and loads no remote assets. - **No eval.** Nothing in the package uses `eval` or generates code, so a content security policy without `unsafe-eval` works. Report a vulnerability by opening an issue on the [GitHub repository](https://github.com/dkdev24/pele-animate) that asks for a private contact. Leave the details out of the public issue. --- # Migrating pele-animate stays on `0.x`: a minor release may break things. Pin a range such as `^0.13.0` and bump it deliberately. ## 0.12 to 0.13 Two breaking changes. ### 1. Illustrated glyphs are renamed `doverunner-` The plain names now belong to the Tabler icon packs. Rename the `@{ icon }` values in your Mermaid sources: | Before | After | |---|---| | `player` | `doverunner-player` | | `server` | `doverunner-server` | | `key` | `doverunner-key` | | `document` | `doverunner-document` | | `shield` | `doverunner-shield` | | `phone` | `doverunner-phone` | | `cloud` | `doverunner-cloud` | | `package` | `doverunner-package` | | `token` | `doverunner-token` | | `lock` | `doverunner-lock` | | `database` | `doverunner-database` | | `video` | `doverunner-video` | | `media` | `doverunner-media` | | `encoder` | `doverunner-encoder` | | `storage` | `doverunner-storage` | | `plugin` | `doverunner-plugin` | | `variants` | `doverunner-variants` | | `watermark` | `doverunner-watermark` | | `bits-a`, `bits-b` | `doverunner-bits-a`, `doverunner-bits-b` | A search and replace over `icon: ""` does it. Or switch to Tabler icons and drop the renamed ones; see [icons](./icons.md). ### 2. The glyphs moved into the preset The seven glyphs that lived in `pele-animate/glyphs` are now in `pele-animate/preset-doverunner`. `pele-animate/glyphs` is only the registry (`icons`, `registerGlyphs`, `registerTabler`). If you use any `doverunner-*` glyph, import the preset: ```js import 'pele-animate/preset-doverunner'; ``` ### New in 0.13 - Tabler icon packs: `pele-animate/tabler/`, with the `--pa-stroke` variable. - `icons()` returns a neutral rounded square for unknown names, instead of a blank slot. A missed rename shows up as that rounded square, not as an error, so look for unexpected squares after upgrading. --- # For coding agents If you are an AI coding agent adding pele-animate to a project, use the skill that ships with the package. It carries the rules below in an order that works. ## Install the skill The skill is in the npm package, so after `npm i pele-animate` it is at `node_modules/pele-animate/skills/pele-animate/SKILL.md`. For Claude Code, copy it where skills live: ```sh mkdir -p .claude/skills cp -r node_modules/pele-animate/skills/pele-animate .claude/skills/ ``` Or clone just the skill folder from [GitHub](https://github.com/dkdev24/pele-animate/tree/main/skills/pele-animate) (it has `SKILL.md`, `verify.js` and `references/`). A second skill, `diagram-step-order`, reviews a page's diagrams so the step order matches the text around them. No skill support? Paste this into your instructions file: ```md ## Diagrams (pele-animate) - Animated Mermaid diagrams use `pele` + `pele-animate`; mount on the client only (see node_modules/pele-animate/docs or the README). - Icon names must exist: read node_modules/pele-animate/tabler/.js. Never invent names. Unknown names render a plain square. - Order with `%% pa: 1=A 2=B,C`; check svg.paSteps in a browser. - Verify by rendering the page and confirming no unexpected rounded-square icons. ``` ## Rules 1. **Client only.** Never import `pele` or `pele-animate` in server code. Use the framework's mounted hook ([recipes](./recipes.md)). 2. **Never invent icon names.** Look them up in `node_modules/pele-animate/tabler/.js` (object keys) and import that category. An unknown name does not fail; it renders a rounded square, so a made-up name is a silent bug. 3. **Import three things:** `pele-animate/glyphs` (`icons`), the icon pack modules you use, and `pele-animate/style.css`. 4. **Pin the order** with `%% pa:` when the default is wrong ([step order](./step-order.md)). Do not rename node ids after pinning. 5. **Verify in a browser**, not by reading the code. ## Verify Render the page in a headless browser (Playwright, or any CLI browser) and run this in the page: ```js (() => { const svgs = [...document.querySelectorAll('.pa-scene svg.pele')]; const icons = [...document.querySelectorAll('.pa-scene svg.pele-icon[data-icon]')]; return { diagrams: svgs.length, steps: svgs.map((s) => s.paSteps?.length ?? 0), emptyIcons: icons.filter((i) => !i.innerHTML.trim()).map((i) => i.dataset.icon), }; })() ``` Expect `diagrams` equal to the number you added, every `steps` entry above 0, and `emptyIcons` empty. A name that was not registered renders a rounded square instead of nothing, so also compare the icon names in your Mermaid with the pack source. --- # Contributing ```sh git clone https://github.com/dkdev24/pele-animate cd pele-animate pnpm install pnpm test # renders every Mermaid example in the docs and README with Pele, and checks icon names pnpm build # builds the site (a placeholder until the site lands) ``` - The package is plain ESM at the repository root: no build step. The site lives in `site/`. - Docs are the Markdown files in `docs/`. A fenced block marked `mermaid` must render, and every `@{ icon }` name in it must exist. - Fence convention: ```` ```mermaid live ```` is a diagram GitHub can also draw; the site animates it. A diagram with `@{ icon }` nodes uses ```` ```mermaid-icons live ````, because GitHub's Mermaid cannot draw icons (it shows "?" boxes), so GitHub shows the source text and the site animates it. - Every `live` fence carries a title after the word `live`: `title="Flowchart: cache hit or miss"`. The playground's examples menu shows it (grouped by page), so write "Type: what it shows". - Tabler packs are generated: `node scripts/build-tabler.mjs ` rewrites `tabler/*.js`. Do not edit those files by hand. The Tabler version is recorded in each file header and in `THIRD-PARTY-NOTICES.md`. - Add an entry to `CHANGELOG.md` for any user-visible change. Breaking changes also get a section in `docs/migrating.md`. - Versioning stays on `0.x`. - Keep example diagrams generic (clients, APIs, databases). --- # FAQ **Is this part of Pele?** No. It is an unofficial add-on, not affiliated with or endorsed by the Pele or Obsidian teams. Pele does the layout and drawing; pele-animate adds step order, step-by-step playback and icons on top. **Why not just use Pele?** Pele alone draws a static diagram. Use it directly if that is enough. pele-animate is for diagrams you want to explain in order. **Is it a theme?** No. It does not offer visual styles beyond a simple scene frame and light/dark colors you can override. **Why not Mermaid.js?** Pele is much smaller and renders synchronously. pele-animate is built on Pele, so it follows that choice. **Does it work with server rendering?** Mount it on the client ([recipes](./recipes.md)). Static site generators are fine. **How big is it?** The core (`pele-animate.js` plus CSS) is about 20 KB unminified. Icon packs are separate modules, so you only ship the categories you import (the largest, `system`, is about 199 KB unminified). **Which diagrams does it support?** Flowcharts, sequence diagrams, architecture diagrams, state diagrams, C4 context, block, class and ER diagrams, timelines, journeys, git graphs and Gantt charts ([diagram types](./diagram-types.md)). **Can I use my own icons?** Yes ([custom icons](./custom-icons.md)). **Licenses.** pele-animate is MIT. Pele is MIT. The Tabler icon packs are from Tabler Icons (MIT, Paweł Kuna); see `THIRD-PARTY-NOTICES.md`.