excalidraw-diagrams
Use when asked for an Excalidraw diagram or an .excalidraw file - a tech architecture, system, infrastructure or data-flow diagram - especially one that must show real tech-stack or brand logos, or when the Excalidraw MCP view cannot show images or logos.
metadata
| domain | documentation |
| category | auto-invoke |
| version | 1.0.0 |
| updated | 2026-10-04 |
| tags | diagrams excalidraw architecture |
| path | skills/excalidraw-diagrams/SKILL.md |
install
Run this in your repository. Commit .claude/skills/ and skills-lock.json afterwards — re-running the command overwrites local edits, and the commit is what makes that recoverable.
SKILL.md
# Excalidraw diagrams
## Overview
Generate the diagram as a real `.excalidraw` file from a short layout script, then open it in the real Excalidraw app headlessly and look at the screenshots. The scripts here already do the parts that are slow to rediscover: the file format, logo sourcing, text measuring, rendering and PNG export. Use them; do not rewrite them.
Paths below are relative to this skill's folder.
## Workflow
1. **Check for the Excalidraw MCP.** If it is not connected, prompt the user to connect it, exactly as described under *If the Excalidraw MCP is not connected*. Nothing below needs the MCP.
2. **Get the facts.** With a repo: read the real stack from manifests, compose files, ADRs and open branches, and note what is built, what sits on a branch, and what is only decided. Without one: draw only what the brief states. Where the repo and the brief disagree, draw the repo and say so.
3. **Check the logos:** `node scripts/logos.mjs react fastapi postgres ...` prints `ok` or `MISSING` per key. A key is the product's name without punctuation: `nextjs`, `nodejs`, `postgres`, `githubactions`. Test a custom source with `key=https://...svg` or `key=path/to/logo.png`.
4. **Write the layout script** in a scratch folder: copy `examples/architecture.mjs`, change it, run it with `node`.
5. **Check and render:**
- `node scripts/check.mjs out.excalidraw`
- `node scripts/render.mjs out.excalidraw renderDir --tiles 2`
- Read `overview.png` and the tiles. Fix and re-run until `save()` prints no warnings, render reports 0 "text wider than declared", and nothing overlaps or is cut off.
6. **Export the picture:** `node scripts/export-png.mjs out.excalidraw out.png` writes a PNG drawn by the real app and cropped to the drawing. Always deliver it next to the `.excalidraw` file, and Read it once as the final look.
7. **Draw the inline view,** whenever the Excalidraw MCP is connected, asked for or not: a simplified, text-only version of the same diagram with `create_view` (call its `read_me` first). Say that it has no logos because the MCP cannot show images.
8. **Report:** both file paths, how to open the `.excalidraw` (excalidraw.com > menu > Open, or the VS Code Excalidraw extension), which technologies are text-only, and what was verified.
`render.mjs` and `export-png.mjs` drive the installed Edge or Chrome and need network. If either says `playwright-core` is not installed, run the command it prints (`npm install --prefix scripts`); that is needed once per machine.
## Kit reference (`scripts/kit.mjs`)
| Call | Draws |
|---|---|
| `createDiagram({ logos: [...] })` | Start. List every logo up front: `'react'`, `{ key, urls: [...] }` or `{ key, file }` (SVG, PNG, JPEG; path relative to the cwd). |
| `d.card(x, y, w, h, { title, sub, logo, logoW, fill, status })` | Titled box, also used for zones. Returns `{ box, top, cx, cw }`. |
| `d.chips(x, y, w, items, { cols })` | Grid of `{ label, logo, status, span }` pills. Returns the next y. |
| `d.chip(x, y, w, item)` | One pill. |
| `d.rect(x, y, w, h, { fill, status, sw })` | A plain box. |
| `d.text` / `d.note` / `d.lines` | Free text: any size / 14px muted / multi-line. |
| `d.arrow(points, { from, to, status })` | Arrow through absolute points; `from`/`to` bind it to cards so it follows a drag. |
| `d.logo(key, x, y, size, { maxW })` | A logo alone. Returns its slot width, or `false` when there is none. |
| `d.legend(x, y, labels)` | The status key. |
| `measure(str, size)` | `{ width, height }` of a string in the real font, for placing things beside text. |
| `await d.save(path)` | Writes the file; prints text-only keys and overflow warnings. |
`status` is `built` (solid), `review` (dashed orange) or `planned` (dashed gray). `FILL` holds the pastel card fills. Wordmark and banner logos keep their shape: up to `logoW` (default 110) wide in a card, 60 in a chip.
## Rules that are not obvious
- **The Excalidraw MCP `create_view` cannot show images:** shapes, text and arrows only. Logos need the file, which is why the inline view in step 7 is text-only.
- **Text is clipped to its stored width,** and opening a file re-measures nothing. Use the kit's text calls, which measure with real Excalifont widths; never hand-write a text element.
- **No lookalike logos.** A `MISSING` key stays text-only, or gets a real source of its own. Report which were text-only.
- **Draw status honestly.** A decision or an open branch is not a running system: mark it `planned` or `review`. Add the legend whenever more than one status appears; leave it out when everything is built.
- **Leave 100-120px gutters** between columns and put arrow labels there with `d.note`, not on cards. Route long arrows through an empty lane.
- **Plain characters only** in labels: no emoji and no arrow glyphs; they fall back to another font.
## If the Excalidraw MCP is not connected
The MCP is "not connected" when no `create_view` tool from an Excalidraw server is in the tool list, deferred tools included.
This skill does not need it: every step runs from the scripts, and the `.excalidraw` file and the PNG come out the same without it. The MCP adds one thing, an inline text-only sketch in the chat. The user wants to be offered that, so prompt for it.
**Prompt once per session, before building anything.** Use the host's question tool if it has one; otherwise ask in plain text and wait for the reply. Say this:
> The Excalidraw MCP is not connected, so I can't draw the diagram inline in the chat. The `.excalidraw` file and the PNG, with logos, do not need it. Do you want to connect it first?
>
> - **I'll connect it myself.** In Claude Code, run `claude mcp add --transport http --scope user excalidraw https://mcp.excalidraw.com/mcp`. In the Claude apps, add it under Settings > Connectors (custom connector URL: `https://mcp.excalidraw.com/mcp`). Tell me when it is done.
> - **Add it for me.** Claude Code only: I run that command.
> - **Continue without it.**
| Reply | What to do |
|---|---|
| Continue without it | Run the workflow. Do not ask again in this session. |
| I'll connect it myself | Do not run the command. Wait until they say it is done, then look at the tool list again. |
| Add it for me | Run the `claude mcp add` command above, then look at the tool list again. Never add or change MCP configuration without this reply. |
| After either of those two | If `create_view` is now there, run the workflow, step 7 included. If it is still missing, say: "It is not showing up in this session; it should appear in a new one. I'll build the file and the PNG now." Then run the workflow. |
| Nobody to ask (running as a subagent, or headless) | Do not wait. Run the workflow and put the two connect instructions in the final report. |
A connected MCP whose `create_view` call fails is a different case: do not prompt. Say that the call failed, and deliver the file and the PNG.
## When something else is missing
| Situation | What to do |
|---|---|
| No network | Cached logos still resolve; the rest come back `MISSING`. `render.mjs` and `export-png.mjs` fail with a message. Build with what resolves, run `check.mjs`, deliver the `.excalidraw`, and say plainly that the render and the PNG could not be made, so the layout is unverified. |
| No Edge or Chrome, or `playwright-core` not installed | `render.mjs` and `export-png.mjs` exit 1 and print the reason, with the install command where there is one. Run it, or report as in the row above. |
## Common mistakes
| Mistake | Fix |
|---|---|
| Writing the JSON, a logo fetcher or a browser harness from scratch | Use `kit.mjs`, `logos.mjs`, `render.mjs`, `export-png.mjs` |
| Calling it done after `check.mjs` passes | Structure says nothing about overlap; read the screenshots |
| Reading only `overview.png` on a big diagram | Small text is unreadable when zoomed out; read the tiles |
| A logo shows as a blank box | Its SVG lacked `width`, `height` or `xmlns`; load it through the kit |
| Skipping the MCP prompt because the file does not need the MCP | The prompt is for the user's benefit; ask once, then carry on |
Logos are cached in `~/.cache/excalidraw-diagrams/logos`; set `EXCALIDRAW_LOGO_CACHE` to move the cache.