Diagrams
Generate diagrams from structured JSON → SVG (and optionally PNG).
Quick Start
Install elkjs in the target directory (if not already present):
cd <project>/docs/diagrams && npm init -y && npm install elkjs
# Set "type": "module" in package.json
Write ELK JSON files describing the diagram (see schema below)
Render:
# Single file
node <skill-dir>/scripts/render-elk.mjs diagram.json output.svg
# Batch: all .json files in a folder → svg/ subfolder
node <skill-dir>/scripts/render-elk.mjs --dir <folder>
# Batch + PNG (macOS only, uses sips)
node <skill-dir>/scripts/render-elk.mjs --dir <folder> --png
Embed in markdown:

ELK JSON Schema
{
"id": "root",
"title": "Diagram Title (rendered as heading)",
"layoutOptions": {
"elk.algorithm": "layered",
"elk.direction": "DOWN",
"elk.spacing.nodeNode": "30",
"elk.layered.spacing.nodeNodeBetweenLayers": "40",
"elk.padding": "[top=40,left=20,bottom=20,right=20]"
},
"children": [
{
"id": "node1",
"width": 220,
"height": 45,
"labels": [{"text": "Node label for ELK layout"}],
"label": "📦 Display label (rendered in SVG)",
"color": "core",
"subtitle": "Optional second line"
}
],
"edges": [
{
"id": "e1",
"sources": ["node1"],
"targets": ["node2"],
"labels": [{"text": "Yes", "width": 25, "height": 14}],
"edgeColor": "#10B981",
"dashed": true
}
]
}
Node Properties
| Property |
Type |
Description |
id |
string |
Required. Unique identifier |
width |
number |
Node width in px (default 120) |
height |
number |
Node height in px (default 40) |
labels |
array |
[{text}] — used by ELK for layout calculation |
label |
string |
Display text rendered in SVG (supports emoji). Falls back to id |
color |
string |
Color key from palette (see below) |
subtitle |
string |
Smaller text below the label |
fontSize |
number |
Label font size (default 13) |
children |
array |
Nested nodes — makes this a container |
containerColor |
string |
Color key for container background |
Edge Properties
| Property |
Type |
Description |
id |
string |
Required. Unique identifier |
sources |
string[] |
Source node id(s) |
targets |
string[] |
Target node id(s) |
labels |
array |
[{text, width, height}] — edge labels |
edgeColor |
string |
Hex color (default #64748B) |
dashed |
boolean |
Dashed line style |
strokeWidth |
number |
Line thickness (default 1.5) |
Color Palette
8 semantic colors. Every node maps to one. A legend auto-renders at the bottom of each SVG.
| Key |
Color |
Meaning |
Use For |
action |
🔵 Blue |
System action |
Steps the app performs — API calls, DB writes, cron triggers |
external |
🟢 Teal |
External service |
Third-party APIs — Google, Twilio, Stripe, Resend |
decision |
🩷 Pink |
Decision point |
Yes/no branches, if/else, conditional checks |
user |
🟠 Orange |
User action |
Things the user does — clicks, inputs, reviews |
success |
🟢 Green |
Positive outcome |
Done, confirmed, created, visible |
negative |
🔴 Red |
Negative outcome |
Canceled, error, failed, not built |
neutral |
⚫ Gray |
Neutral / info |
Starting points, labels, inactive, informational |
data |
🟡 Amber |
Data / artifact |
Records, drafts, outputs, intermediate data |
Set "legend": false on the root graph to hide the auto-legend.
Layout Options
Common layoutOptions values:
elk.direction: DOWN (default), RIGHT, LEFT, UP
elk.algorithm: layered (default, best for flowcharts), force, stress
elk.spacing.nodeNode: Space between sibling nodes (px)
elk.layered.spacing.nodeNodeBetweenLayers: Space between layers (px)
elk.padding: [top=N,left=N,bottom=N,right=N]
Design Tips
- Sizing: 200-280px wide for most nodes. 45px tall for single-line, 55px for two-line labels.
- Decision nodes: Use
context (pink) color for yes/no branching.
- Edge labels: Keep short (Yes/No/Error). Set
width/height for proper positioning.
- Containers: Add
children array to a node. Use containerColor: "step" for a light blue group.
- Manual trips vs automated: Use
dashed: true on edges for alternative/optional paths.
- Title: Set
title on the root graph for a rendered heading above the diagram.
- Emoji in labels: Supported and encouraged for visual scanning.
Gotchas
- Container layout: ELK's layered algorithm with nested containers in
RIGHT direction can produce overlapping layouts. Prefer DOWN for containers, or flatten to a non-container layout if horizontal.
labels vs label: labels (array) is what ELK uses for layout spacing. label (string) is what gets rendered in the SVG. Always set both — labels[0].text should approximate the display label length for correct sizing.
package.json must have "type": "module" for the ESM import to work.
- elkjs must be installed locally in the directory where you run the script. It's not global.
1---2name: diagrams3description: Generate visual flow diagrams, architecture diagrams, and system maps as SVG/PNG. Use when the user asks for flowcharts, user flow diagrams, architecture diagrams, system diagrams, interaction maps, or any visual diagram. Supports ELK JSON layout engine with automatic rendering to SVG.4---56# Diagrams78Generate diagrams from structured JSON → SVG (and optionally PNG).910## Quick Start11121. **Install elkjs** in the target directory (if not already present):13 ```bash14 cd <project>/docs/diagrams && npm init -y && npm install elkjs15 # Set "type": "module" in package.json16 ```17182. **Write ELK JSON** files describing the diagram (see schema below)19203. **Render:**21 ```bash22 # Single file23 node <skill-dir>/scripts/render-elk.mjs diagram.json output.svg2425 # Batch: all .json files in a folder → svg/ subfolder26 node <skill-dir>/scripts/render-elk.mjs --dir <folder>2728 # Batch + PNG (macOS only, uses sips)29 node <skill-dir>/scripts/render-elk.mjs --dir <folder> --png30 ```31324. **Embed in markdown:**33 ```markdown34 35 ```3637## ELK JSON Schema3839```json40{41 "id": "root",42 "title": "Diagram Title (rendered as heading)",43 "layoutOptions": {44 "elk.algorithm": "layered",45 "elk.direction": "DOWN",46 "elk.spacing.nodeNode": "30",47 "elk.layered.spacing.nodeNodeBetweenLayers": "40",48 "elk.padding": "[top=40,left=20,bottom=20,right=20]"49 },50 "children": [51 {52 "id": "node1",53 "width": 220,54 "height": 45,55 "labels": [{"text": "Node label for ELK layout"}],56 "label": "📦 Display label (rendered in SVG)",57 "color": "core",58 "subtitle": "Optional second line"59 }60 ],61 "edges": [62 {63 "id": "e1",64 "sources": ["node1"],65 "targets": ["node2"],66 "labels": [{"text": "Yes", "width": 25, "height": 14}],67 "edgeColor": "#10B981",68 "dashed": true69 }70 ]71}72```7374### Node Properties7576| Property | Type | Description |77|----------|------|-------------|78| `id` | string | Required. Unique identifier |79| `width` | number | Node width in px (default 120) |80| `height` | number | Node height in px (default 40) |81| `labels` | array | `[{text}]` — used by ELK for layout calculation |82| `label` | string | Display text rendered in SVG (supports emoji). Falls back to `id` |83| `color` | string | Color key from palette (see below) |84| `subtitle` | string | Smaller text below the label |85| `fontSize` | number | Label font size (default 13) |86| `children` | array | Nested nodes — makes this a container |87| `containerColor` | string | Color key for container background |8889### Edge Properties9091| Property | Type | Description |92|----------|------|-------------|93| `id` | string | Required. Unique identifier |94| `sources` | string[] | Source node id(s) |95| `targets` | string[] | Target node id(s) |96| `labels` | array | `[{text, width, height}]` — edge labels |97| `edgeColor` | string | Hex color (default `#64748B`) |98| `dashed` | boolean | Dashed line style |99| `strokeWidth` | number | Line thickness (default 1.5) |100101### Color Palette1021038 semantic colors. Every node maps to one. A legend auto-renders at the bottom of each SVG.104105| Key | Color | Meaning | Use For |106|-----|-------|---------|---------|107| `action` | 🔵 Blue | **System action** | Steps the app performs — API calls, DB writes, cron triggers |108| `external` | 🟢 Teal | **External service** | Third-party APIs — Google, Twilio, Stripe, Resend |109| `decision` | 🩷 Pink | **Decision point** | Yes/no branches, if/else, conditional checks |110| `user` | 🟠 Orange | **User action** | Things the user does — clicks, inputs, reviews |111| `success` | 🟢 Green | **Positive outcome** | Done, confirmed, created, visible |112| `negative` | 🔴 Red | **Negative outcome** | Canceled, error, failed, not built |113| `neutral` | ⚫ Gray | **Neutral / info** | Starting points, labels, inactive, informational |114| `data` | 🟡 Amber | **Data / artifact** | Records, drafts, outputs, intermediate data |115116Set `"legend": false` on the root graph to hide the auto-legend.117118### Layout Options119120Common `layoutOptions` values:121122- `elk.direction`: `DOWN` (default), `RIGHT`, `LEFT`, `UP`123- `elk.algorithm`: `layered` (default, best for flowcharts), `force`, `stress`124- `elk.spacing.nodeNode`: Space between sibling nodes (px)125- `elk.layered.spacing.nodeNodeBetweenLayers`: Space between layers (px)126- `elk.padding`: `[top=N,left=N,bottom=N,right=N]`127128## Design Tips129130- **Sizing:** 200-280px wide for most nodes. 45px tall for single-line, 55px for two-line labels.131- **Decision nodes:** Use `context` (pink) color for yes/no branching.132- **Edge labels:** Keep short (Yes/No/Error). Set `width`/`height` for proper positioning.133- **Containers:** Add `children` array to a node. Use `containerColor: "step"` for a light blue group.134- **Manual trips vs automated:** Use `dashed: true` on edges for alternative/optional paths.135- **Title:** Set `title` on the root graph for a rendered heading above the diagram.136- **Emoji in labels:** Supported and encouraged for visual scanning.137138## Gotchas139140- **Container layout:** ELK's layered algorithm with nested containers in `RIGHT` direction can produce overlapping layouts. Prefer `DOWN` for containers, or flatten to a non-container layout if horizontal.141- **`labels` vs `label`:** `labels` (array) is what ELK uses for layout spacing. `label` (string) is what gets rendered in the SVG. Always set both — `labels[0].text` should approximate the display label length for correct sizing.142- **`package.json` must have `"type": "module"`** for the ESM import to work.143- **elkjs must be installed locally** in the directory where you run the script. It's not global.144