# 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 `