Render a Mermaid diagram to a self-contained HTML file and a PNG, using the browse shim as the renderer — no heavy diagram toolchain to install.
Scanned 9/3/2026
Install to Claude Code
npx -y skills add timurgaleev/vibestack --skill diagram --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Diagram?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/timurgaleev-diagram)More formats (shields.io, HTML) on the badges page.
---
name: diagram
description: |
Render a Mermaid diagram to a self-contained HTML file and a PNG, using the browse shim as the renderer — no heavy diagram toolchain to install.
allowed-tools:
- Bash
- Read
- Write
triggers:
- draw a diagram
- make a mermaid diagram
- render this flowchart
- diagram this
- visualize this as a graph
---
## When to invoke
Use to turn a description or some Mermaid source into a picture — "draw a
diagram", "make a flowchart", "diagram this architecture", "visualize this as a
graph".
# /diagram — Render a Mermaid diagram
vibestack renders **Mermaid** offline. The repo ships the whole renderer as one
self-contained page (`lib/diagram-render/dist/diagram-render.html`, mermaid
pinned at build time), so the browse shim turns your source into SVG with no
network at all. The SVG is inlined into the HTML artifact you keep — it opens in
any browser, on any machine, forever, with nothing to fetch — and the PNG comes
from the same `$B` you already use for QA.
{{include lib/snippets/browse-setup.md}}
### 1. Get the Mermaid source
If the user gave Mermaid, use it. Otherwise write it from their description
(`flowchart`, `sequenceDiagram`, `erDiagram`, `classDiagram`, `gantt`, etc.).
Authoring rules, all in service of one thing — a diagram nobody can read is
worse than a paragraph:
- **Direction follows the subject.** `graph LR` for pipelines and flows, where
the eye should travel with the data; `graph TD` for hierarchies and
decompositions, where depth is the point.
- **5-15 nodes is the readable range.** Keep labels to a few words. If the ask
genuinely needs more, split it into two diagrams along a seam the reader
already understands (one per subsystem, or overview plus detail) rather than
shrinking everything to fit.
- **Say what the arrows mean** when a graph mixes kinds of edge — a call, a
data flow and a deploy step look identical otherwise.
Then pick an output base path. In a git repo, default to a diagram directory
the repo already uses (or `docs/`) with a slug from the diagram's subject, so
the artifacts land somewhere tracked and stop overwriting each other. Outside a
repo, default to `./diagram`. Confirm the path with the user if the diagram is
one they will commit.
Write the Mermaid source to `$OUT.mmd` with the Write tool. Passing it through a
heredoc mangles quotes and backslashes in edge labels, and it is the artifact the
user edits to re-render anyway.
### 2. Render the source to SVG with the offline bundle
The staged copy is content-addressed, so concurrent sessions and mixed vibestack
versions never clobber each other:
```bash
OUT="${OUT:-./diagram}" # base path; produces $OUT.mmd, $OUT.html, $OUT.png
mkdir -p "$(dirname "$OUT")"
# The renderer is linked into this skill's own directory at install time
# (skills/diagram/renderer -> lib/diagram-render), so it resolves the same way
# in a user-scope install, a project-scope install, and the checkout itself.
BUNDLE=""
for c in "${CLAUDE_SKILL_DIR:-$HOME/.claude/skills/diagram}/renderer/dist/diagram-render.html" \
"$(git rev-parse --show-toplevel 2>/dev/null)/lib/diagram-render/dist/diagram-render.html"; do
[ -f "$c" ] && BUNDLE="$c" && break
done
if [ -z "$BUNDLE" ] || [ -z "${B:-}" ] || [ "$("$B" status 2>/dev/null)" = "BROWSE_NOT_AVAILABLE" ]; then
echo "RENDER_UNAVAILABLE"
else
SHA=$(shasum -a 256 "$BUNDLE" | cut -c1-16)
STAGED="${TMPDIR:-/tmp}/vibestack-diagram-render-$SHA.html"
[ -f "$STAGED" ] || { cp "$BUNDLE" "$STAGED.$$" && mv "$STAGED.$$" "$STAGED"; }
TAB=$("$B" newtab --json | sed -n 's/.*"tabId":[[:space:]]*\([0-9]*\).*/\1/p')
[ -z "$TAB" ] && { echo "TAB_OPEN_FAILED — daemon busy? check browse status"; } || {
"$B" load-html "$STAGED" --tab-id "$TAB"
"$B" wait '#done' --tab-id "$TAB"
echo "RENDER_TAB_READY: tab $TAB"
}
fi
```
**Both failure lines are terminal — STOP, do not continue to Step 3.** With no
`$TAB` and no `$OUT.svg`, the steps below would build an HTML file around a
file that does not exist and screenshot a blank page, reporting success. On
`RENDER_UNAVAILABLE`, say which half is missing. A missing bundle means an
incomplete checkout — re-run `./install` from it. A missing shim means the
browse dependencies were never fetched; `$B status` bootstraps them on first
use and prints `BROWSE_NOT_AVAILABLE` when it cannot (npm absent). On
`TAB_OPEN_FAILED`, report what `$B status` says — the daemon is busy, wedged,
or unable to open a tab — and re-run.
Every `$B js` / `$B wait` / `$B closetab` below MUST pass `--tab-id $TAB`. Without
it the call hits whatever tab is active, which may be a live `/qa` or `/scrape`
session sharing the daemon.
Render the source, reading it from disk inside the page call so no shell quoting
touches the Mermaid text:
```bash
"$B" js "__renderMermaid('diagram-1', $(python3 -c 'import json,sys; print(json.dumps(open(sys.argv[1]).read()))' "$OUT.mmd"))" \
--tab-id "$TAB" --out "$OUT.svg"
"$B" closetab "$TAB" >/dev/null 2>&1 || true
```
If the call errors, the Mermaid has a syntax problem — read the error, fix
`$OUT.mmd`, and re-render. Do not fall back to a CDN page: rendering is offline
by design, and a silent network fallback turns an air-gapped failure into a
blank diagram nobody notices.
### 3. Inline the SVG into the HTML artifact, then screenshot it
```bash
{
printf '%s' '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Diagram</title>'
printf '%s' '<style>body{margin:0;padding:20px;background:#fff;font-family:system-ui}svg{max-width:100%}</style>'
printf '%s' '</head><body>'
cat "$OUT.svg"
printf '%s' '</body></html>'
} > "$OUT.html"
if [ -n "${B:-}" ] && [ "$("$B" status 2>/dev/null)" != "BROWSE_NOT_AVAILABLE" ]; then
"$B" chain "goto file://$(cd "$(dirname "$OUT")" && pwd)/$(basename "$OUT").html" "wait --load" "screenshot $OUT.png"
echo "DIAGRAM: $OUT.png (and $OUT.html, $OUT.svg, $OUT.mmd)"
else
echo "DIAGRAM_HTML_ONLY: $OUT.html — open it in a browser (no shim for PNG)"
fi
```
The SVG is already drawn, so the capture waits on page load rather than on a
fixed timer — nothing is still rendering when the screenshot fires.
### 4. Report
List the artifacts: `.mmd` (the source to edit), `.svg` (vector for docs),
`.html` (opens anywhere, offline), `.png` (for chat and READMEs).
## Notes
- **Mermaid is the supported format.** Excalidraw, DOCX, and multi-page exports
are not rendered here — for those, hand the user the HTML or use a dedicated
tool.
- **Fully offline.** The renderer is vendored in the repo, and the HTML artifact
carries the finished SVG inline — no CDN, no network, at render time or later.
- **PDF-bound diagrams belong to `/make-pdf`.** It renders a ```` ```mermaid ````
fence as a vector figure inside the document, which beats pasting this skill's
PNG into a page. Say so when the user's real target is a PDF.
- If `RENDER_UNAVAILABLE` printed, say what is missing and how to get it back
(`./install` from the checkout for the bundle; `$B status` to bootstrap the
browse dependencies). Never fall back to a CDN behind the user's back.
{{include lib/snippets/capture-learnings.md}}
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!