mcpbeat

Flow Diagram

unclecatvn/flow-diagram

Draws an activity flow / system architecture diagram as ONE self-contained, INTERACTIVE HTML+SVG page (2-pane app layout — left: diagram, default fit-to-view with zoom/pan; right: sidebar with explainer cards), styled like a real hand-drawn enterprise doc (Stripe/AWS-docs style — real brand logos for MongoDB/Odoo/n8n/etc. from a bundled Simple Icons library, neutral white boxes, sans-serif, no "AI neon" look), scroll-to-zoom, auto light/dark with a toggle button — clicking a flow (a legend chip, a line, or a step badge) highlights that flow with traveling dots along the arrows in their direction, a ping at the destination box, and a sidebar panel that slides out, while everything else dims; two-way arrows (OUT = solid blue / RETURN = dashed green) carry numbered step badges, colored by semantic meaning (red = auth checkpoint, purple = scheduled flow, amber = data pipeline), a small "hop" arc where two lines are forced to cross, dashed group regions, one explainer card per flow in the right sidebar. Use this skill ANY TIME the user wants to "draw a diagram", "draw a flow", "draw a system diagram", "redraw this so it's easier to follow", "interactive diagram", "two-way diagram", "request/response flow", "make a free-form HTML diagram" — even if they never say the word HTML. Especially good when both the request direction AND the response direction need to be shown, or when a templated tool (archify, mermaid) is too rigid. Do NOT use for data charts (bar/line/pie), and do not use when the user asks specifically for archify or mermaid.

65k tokens
context cost
the whole folder, loaded on every use
11
files
ships runnable scripts
0
copies elsewhere
how many repositories repackaged it
118
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/unclecatvn/agent-skills --skill flow-diagram

What comes with it

233 478 bytes besides the instruction
assets/add-drag-to-rearrange.js
assets/add-flow-dots.js
assets/add-flow-ping.js
assets/check-collisions.js
assets/check-svg.js
assets/fix-arrow-gaps.js
assets/icons-library.html
assets/template.html
references/flow-diagram-example.html
references/layout-patterns-guide.html

The instruction itself

13 sections, as written by the author

Interactive two-way HTML flow diagrams

Output: one self-contained .html file (no dependencies, opens directly in a browser), laid out as a 2-pane app filling the viewport: a title row + light/dark toggle (thin-line SVG icon that recolors with the theme — see "Theme icon button") → flow-select chips → left: diagram, default FIT TO VIEW — the reader sees the whole system at once, no scrolling — with a zoom bar (+/− buttons, ⤢ fit-to-view, scroll-to-zoom at the cursor, drag to pan) → right: sidebar of explainer cards. The reader clicks a flow (a chip, a line, or a step badge) → that flow keeps its color and gets a small dot traveling along the line in its direction (see "Traveling dots"), everything else dims to ~10%, the sidebar shrinks to just that flow's card; clicking a step badge highlights the matching line in that card. That's how a non-technical reader answers "where does this flow go, start to finish?" without reading the whole diagram. A fully worked, machine-checked example: references/flow-diagram-example.html. Not sure which layout to use (linear? convergent? hub? branching?) → read references/layout-patterns-guide.html first — a guide to 4 common topologies, each with a "why this layout" section explaining the reasoning, not just numbers to copy.

Hard-won lesson behind this skill: writing SVG means placing coordinates blind — nobody sees the result while typing numbers, and the human eye (even a shrunk screenshot) misses collisions that are only a few pixels off. So the process revolves around a machine-measured loop: draw → check → fix coordinates → check again, until 0 errors. Don't trust the feeling of "that's probably not overlapping."

Process

  • Ask what language the diagram should be in — before gathering any content. Don't assume English by default and don't infer it from the conversation's language either; ask explicitly, e.g. a short multiple-choice question ("Which language should the diagram be in? English / [conversation's language] / other — I'll write it out"). This decides the language for every label, chip, card, and title in the final file. Skipping this step and guessing is the single most common way this skill produces a diagram the user then has to ask to redo from scratch.
  • Lock down the content before drawing. List out: the blocks (boxes), the flow GROUPS (each group becomes 1 chip + 1 card + 1 data-flow code), and for each flow — *both the outbound and the return direction*. If the user only describes the outbound leg, ask about the return leg (real-world lesson: users almost always want to see how data comes back, not just how it goes out). Any flow's routing that's unclear → ask a multiple-choice question, don't guess. Before placing any coordinates, work out which layout pattern the content matches in references/layout-patterns-guide.html (linear multi-phase / convergent multi-source / central hub / conditional branch) — real diagrams often NEST several patterns (e.g. two sources converging, then branching on pass/fail) — identify each piece and combine them rather than inventing a new layout from scratch.
  • Plan the coordinate grid and WRITE IT AS A COMMENT at the top of the SVG. Rule out vertical columns and horizontal rows, assign each box to a cell; list the "corridors" — empty lanes for long vertical/horizontal runs; list any point where two lines are forced to cross. Write all of it into <!-- LAYOUT PLAN: ... --> right after the <svg> tag — the next editing pass will then know which lane already has an owner. 10 minutes of planning saves 5 rounds of untangling overlaps.
  • Copy assets/template.html as the starting shell — it already has everything: light-mode CSS (with the text halo), chips, the detail panel, interaction JS, 5 arrow markers, a sample forward/return pair, a hop, a region, a box, a badge, a card. Don't rewrite the CSS/JS from scratch — copy it, then replace the content.
  • Draw following the Design System below, wiring up the interactive attributes as you go (line/badge/label → data-flow; box → data-flows). Keep the SVG's layer order as specified.
  • Machine-check to 0 errors — do NOT skip this step even if getting browser permission is annoying. Prefer checking in a real browser: open the file, then run assets/check-collisions.js verbatim through a browser JS tool. Check it in the default fit-to-view state (100%) — the whole SVG must be inside the viewport, because elementsFromPoint is blind outside the visible area; if you'd zoomed in, click ⤢ before checking or you'll get a false "0 errors". The script reports errors in viewBox coordinates (fix the numbers in the file directly) and draws red/yellow outline boxes on the page. Fix → re-check, usually 1–3 rounds. Only stop once: 0 errors, and any remaining warnings can be justified out loud (more than 5 warnings means the layout is genuinely cluttered — rearrange the boxes instead of arguing with the checker).

Can't connect to the browser (extension broken/unresponsive) → fall back immediately to the offline checker assets/check-svg.js (node assets/check-svg.js <file.html>, no browser permission needed): computes box-overlaps-box, arrow-too-close-to-box, floating-arrow (an arrowhead that touches no box), viewBox overflow, and box-too-close-to-region-border exactly from the real numeric coordinates — only the text-overlap check is an estimate of font width (reported as a warning, not a hard error, and needs a follow-up eyeball check). Never "check" by mentally estimating coordinates — past 10 arrows, doing it by hand reliably both misses real errors and invents false ones (this actually happened once: a by-hand pass "found" a floating line that didn't exist in the file at all). If neither checking path works, stop and ask the user rather than guessing blind.

  • Click through every flow, then step back and look at the whole thing. Click each chip: the right flow lights up, related boxes aren't dimmed by mistake, the panel shows the right card, Esc clears the selection. Then screenshot it and actually look — the checker only catches "wrong," not "ugly." Finally, name the file something clear and versioned (e.g. flow-<topic>_YYYY-MM-DD_v1.0.html). A substantial content change → bump the version, keep the old file.

Design System

"Big Tech docs" style: system sans-serif type (NEVER a monospace font for the whole page), restrained color, the diagram sits on a rounded card. Goal: the reader sees a formal enterprise document, not an "AI product."

What makes it read as hand-drawn (learned from real hand-crafted architecture diagrams — the progcoder/AWS-docs school):

  • Real brand logos instead of text: assets/icons-library.html ships 18 <symbol> elements from Simple Icons (CC0 — MongoDB, PostgreSQL, Odoo, n8n, Claude, Gemini, FastAPI, Messenger, Zalo, Discord, Telegram, Facebook, Sheets, Airflow, Grafana, Docker, Redis…). Copy the symbol you need into <defs>, place it inside the box's <g>: <use href="#ic-mongodb" x="boxX+12" y="boxY+(h−16)/2" width="16" height="16"/> (a second logo sits 20px further along). Missing a logo: curl -s https://cdn.simpleicons.org/<slug> then add a symbol following the pattern; a near-black logo should get its fill changed to #1f2937 so it recolors with the theme. Logos let the reader recognize a technology without reading text — the single strongest "hand-drawn" signal.
  • Neutral boxes, color reserved for flows: box fill stays white, its role is shown by the BORDER color; only 1–2 focal boxes get a tinted fill. A diagram where every box has its own distinct color — a uniform rainbow — is the single most obvious AI tell.
  • Sentence case text (Title Case), never ALL-CAPS across the whole diagram; generous spacing, empty space is fine.

Light/dark theme: every color is declared via light-dark() inside :root (already in the template), defaults to the OS setting, the toggle button overrides it and persists to localStorage. Golden rule when writing SVG: only write LIGHT-palette hex inside an attribute (stroke="#2563eb", fill="#ffffff"…) — the "color mapping" CSS block in the template remaps them per theme automatically (CSS always wins over a presentation attribute). Never write var() inside an attribute, never invent a new hex outside the table — an unlisted color won't follow the theme and will be immediately obvious in dark mode.

Semantic colors (light-palette hex — the dark palette maps automatically, don't hardcode a new one)

| Variable | Light hex (use in SVG) | Meaning | Stroke |

|---|---|---|---|

| --di | blue #2563eb | OUT direction (request) | solid, width 1.8 |

| --ve | emerald #059669 | RETURN direction (response) | dashed 6 4, width 1.6 |

| --sec | red #dc2626 | auth checkpoint / security | solid |

| --script | violet #7c3aed | scheduled / cron / batch flow | solid (secondary branch: dashed 5 4) |

| --pipe | amber #d97706 | data pipeline (ETL/sync) | dashed 7 5 for async |

Secondary colors for system groups: --sky #0284c7 (data store/API), --fx #c026d3 (content generation). In HTML (chips, card dots) use var(--sky)/var(--fx); in SVG use the hex. Every flow color has its own marker (m-di, m-ve, m-sec, m-sc, m-pi) — an arrow always uses the marker matching its own color.

Interaction (the part that makes it "easy to follow" — do all of it, don't skip steps)

  • One short code per flow group (qa, s, c, d, m…). Give data-flow="<code>" to EVERY line, badge (wrapped in <g data-flow data-step="...">), and label of that flow; give data-flows="<code1> <code2>" to the <g> wrapping each box — list every flow that actually passes through it (missing one dims the box by mistake when that flow is selected; an extra one lights it up for no reason).
  • Chips above the diagram: one chip per flow, style="color: <flow color>" — the chips double as the color legend (replacing an in-SVG legend) and as select buttons. Include a hint like "Click a flow to see it run end to end".
  • The right sidebar holds every explainer card; selecting a flow hides the other cards — a card is the SINGLE SOURCE of the description, don't repeat it elsewhere. A badge's data-step must match the text inside a span.n1–n5 in its card so clicking the badge highlights the right line — the template's matcher understands both ranges (5→7 = steps 5, 6, 7) and merged codes (3·11), so just write the spans in the same order the story is told.
  • Zoom/pan already works out of the box: defaults to Fit-to-View (= 100%, whole system visible), +/− and scroll zoom up to 800% anchored at the cursor, drag to pan. Just keep the template's .board > .zoombar + .canvas > svg structure — no extra logic needed.
  • All the CSS/JS already lives in the template (.dim, .flowing, sidebar, zoom, Esc) — just set the right data-attributes.
  • The file still has to be readable when printed / without JS: printing flattens the layout automatically (diagram at full page width, every card fully shown, the zoom bar/theme button hidden) — interaction is an extra layer, not a requirement for understanding the diagram.

SVG layer order (important — SVG paints in document order)

defsarrowsregionsboxesbadges + labels. Arrows are drawn first so a box's solid white fill (drawn later) covers the overrun at both ends; a dashed region sits below the boxes but above any long arrow that cuts through the region. Text is drawn last because the template wraps a background-colored halo around every glyph (stroke = var(--panel), recolors with the theme) — a line forced to run near text still leaves it readable. The halo is a safety net, not a license to drop text straight onto a line. There's no in-SVG legend anymore — the chips handle that.

Boxes

  • Size ~160–220 × 64–84, rx=10; name 11–13px weight 650–700 in --text, a secondary caption at 9px --muted underneath.
  • Role is shown by the BORDER, fill stays white #ffffff: usually border #d1d5db · security border --sec · data store border #0284c7 · secondary component border #e5e7eb with dimmer text. Only the one focal/orchestrator box gets a tinted fill #eff6ff with a --di 1.7 border.
  • Spacing between boxes in the same row ≥ 40px; between rows ≥ 70px (leave room for arrow labels).

Two-way arrows + step badges

  • One relationship = one pair of parallel lines offset by 20–60px: outbound (solid, blue) and return (dashed, emerald). Don't merge them into a double-headed arrow — the reader needs to see two separate journeys.
  • LEAVE A GAP AT THE ARROWHEAD — hard-won lesson: a line's END point (wherever marker-end is) must sit ~8px from the destination box's edge (6–10px is acceptable), NEVER ending right at the edge (0–4px). Reason: the default marker scales with stroke-width (a 1.8 stroke → an arrowhead ~16px in real size); ending 2px away makes the arrowhead glue itself to / overlap the box border, which looks cramped. Quick conversion when placing coordinates: an arrow pointing into a box's top edge y_box → end at y_box − 8; bottom edge → y_box + 8 (remember a box below has a LARGER y); left edge → x_box − 8; right edge → x_box + 8. The START point doesn't matter (the box's solid fill, drawn later, covers the overrun) — only the ARROWHEAD needs breathing room. The check-collisions.js/check-svg.js checkers now catch this automatically ("ARROWHEAD TOO CLOSE TO BOX").
  • Bulk-fixing legacy files that don't follow the 8px rule yet (drawn before this rule was locked in): run assets/fix-arrow-gaps.js (node assets/fix-arrow-gaps.js <file.html>) — automatically pulls every arrowhead currently under 6px out to ~8px, preserving direction (slides along the exact vector of the final segment, including diagonals), without touching any middle waypoints. On a diagonal line the result can sometimes fall a hair short from rounding — run it twice to be safe, and stop once check-svg.js reports 0 errors. Run this script BEFORE add-flow-dots.js/add-flow-ping.js (those two read the arrows' current coordinates to generate the dots/pings — running them first would generate against the wrong coordinates).
  • Each box edge anchors at most 1 incoming + 1 outgoing arrow, offset their anchor points so they don't coincide.
  • Number every step with a badge: a circle r=9 for 1–2 characters, a rounded rect 26×17 rx=8.5 for a 2-character group code (S1, C5); a merged code like 3·11 gets a wider 40×17. Badge background is white, border + text share the flow's color. Center the badge exactly on its line (the solid fill covers the line under it) — a badge floating next to a line instead of centered on it reads as belonging to a different line.
  • Codes are grouped by flow: the primary flow is numbered plainly 1→n; other groups get a letter prefix (S = scheduled, C = API gateway, D = data warehouse, M = media…). If the primary flow passes through the same box pair twice, merge the badges (3·11) instead of drawing 4 lines.
  • Line labels: 8.5px, font-weight 400, ≤ 4 words — push detail down into the explainer card. A label is fully separate from its badge — offset along the line, or nudged to the other side of the line — as long as it doesn't share the badge's exact center; a badge and a label at the same coordinates is the most common text-overlap bug. A long vertical line → rotate the label with transform="rotate(-90 x,y)".
  • Don't drop a long important note (like "⚠ CASCADE DELETE required…") as a long line of text running across the diagram — other lines will cut through it. Push it down into the explainer card, or shorten it to ≤ 4 words + a ⚠ mark and say the rest in the card.

Traveling dots (animation when a flow is selected)

When the reader clicks a flow, the "this is flowing" signal is a small dot traveling along the line — NOT a fake "marching ants" dashed animation. This is the convention used by large infrastructure monitoring tools (Kiali/Istio service mesh, Datadog network maps…): the line keeps its stroke/color, only getting selected-state emphasis via dimming everything else; the small dots running along the exact path are what signal direction and speed — a human reads "a dot is moving from A to B" far more intuitively than "a line is twitching."

  • CSS: svg .flow-dot { opacity: 0; transition: opacity .25s; } svg .flow-dot.flowing { opacity: 1; } (dots hidden by default, only shown once their flow is selected) · add @media (prefers-reduced-motion: reduce) { svg .flow-dot { display: none; } }. Don't use stroke-width to "bolden" a selected line (tried it, removed it — users read it as the diagram "zooming in," more distracting than clarifying); the "this flow is selected" signal relies on exactly two things: everything else dims (.dim) and the dots/ping appear — that's clear enough without also changing line thickness.
  • Every arrow with marker-end is immediately followed by 1 <g class="flow-dot" data-flow="..."> containing 2 <circle> (r≈3.4, fill = the same hex as the line's stroke), each carrying an <animateMotion repeatCount="indefinite" path="...">path = "M x1,y1 L x2,y2" for a <line>, or copy the d attribute verbatim for a <path>. The 2 dots are phase-offset (begin="0s" and begin="{dur/2}s") so the flow feels continuous, with no gap between loops.
  • dur (seconds) is computed from the line's length: clamp(length/130, 1.2, 3.5) (~130px/s — already slowed down once based on real feedback; the earlier ~260px/s speed was called "too fast to track") — short and long lines both *look* like the dot moves at the same speed.
  • JS: broaden the .flowing toggle condition to also catch the dot group (and the ping, see below), not just line/path: if (e.matches('line, path') || e.classList.contains('flow-dot') || e.classList.contains('flow-ping')) e.classList.toggle('flowing', !!f && mine);
  • Past 5 arrows, do NOT hand-type each dot block — the risk of a coordinate typo is high. Run assets/add-flow-dots.js (node assets/add-flow-dots.js <file.html>, overwrites in place, idempotent — it strips any existing dots before regenerating, so re-running it any number of times is safe) — it walks every <line>/<path> with data-flow + marker-end in the file, generates a <g class="flow-dot"> block matching its exact coordinates, and inserts it right after that arrow. Full example: assets/template.html (its 3 sample arrows already have dots).
  • Ping ring at the destination box: once a dot arrives, the destination box's border "breathes" gently — swells ~5% then fades, with a rest between pulses — the same convention Kiali/Datadog use when a packet lands somewhere. Lesson learned once already: an earlier version had each box compute its own SMIL dur/begin from the dot's speed, peaking at 0.8 opacity with a linear 12px swell — it repeated too fast (sometimes under 1s per cycle) and felt jerky; a real user called it "tiring to look at for a while." The current version: one SHARED CSS @keyframes for every ring (no more per-arrow timing) — transform: scale(1→1.05) (a ratio of the box's own size, not a fixed pixel amount, so small and large boxes swell proportionally the same), peak opacity only 0.35, a slow 2.6s cycle with ease-out (decelerating, not linear) and a rest period (opacity stays at 0% from 65%→100% of the cycle) — it reads like an "alive" indicator light, not an alert. CSS: svg .flow-ping-ring { transform-box: fill-box; transform-origin: center; animation: flow-ping 2.6s ease-out infinite; }.
  • The ping's color = the DESTINATION BOX'S OWN BORDER color, not the arriving arrow's color — consistent with the design system's "role shown via border" rule (a neutral gray-bordered box gets a soft gray ring; a focal/branded box with a blue/red/logo-colored border gets a ring in that same color), without inventing a new color layer just for this one animation.
  • Run assets/add-flow-ping.js (node assets/add-flow-ping.js <file.html>, idempotent) to generate the markup — it finds the box nearest each arrow's END point (~14-unit threshold), reads that box's own rect stroke, and inserts <g class="flow-ping" data-flow="..."><rect class="flow-ping-ring" stroke="{box border color}" .../></g> as a child of that box's own <g data-flows> (not a free-floating element — the animation lives entirely in the shared CSS @keyframes, no per-arrow dur to compute). Placing it inside the box is deliberate: when the box gets dragged (see "Drag to rearrange"), the ring follows the box's transform automatically.
  • Printing (@media print): hide both the dots and the ping (svg .flow-dot, svg .flow-ping { display: none; }), lines return to a plain static stroke — animation is an interactive layer, not a requirement for reading the diagram on paper.

Drag boxes to rearrange (optional feature, off by default)

When the user needs to adjust the layout themselves without asking for coordinate edits, add the "✥ Rearrange" mode (a dedicated button next to the theme toggle, id="edit-toggle") — turning it on is what makes boxes draggable; turning it off restores normal behavior (click to select a flow). Run assets/add-drag-to-rearrange.js (node assets/add-drag-to-rearrange.js <file.html>, idempotent — does nothing if it's already present) to insert the CSS+button+JS into a file already built from the template.

  • v1 only translates the whole box; an arrow anchored to it (endpoint within 14 units of the edge at load time, matching the skill's ~8px convention) stretches its FINAL SEGMENT to follow, while the corridor waypoints in between stay put — the rest of the route is never redrawn.
  • Known limits, worth saying plainly to the user: badges/labels on a line do NOT follow a dragged box; a dashed region border does NOT grow to keep containing a box dragged out of it — recenter it by eye or edit the region's coordinates. Traveling dots hide while rearrange mode is on; each dot's path refreshes the instant the drag ends, so turning rearrange mode off and reselecting the flow shows the dots running along the new position.
  • This is a runtime-only (JS) feature — it does not write a new "official" position back into the file. Dragging doesn't persist to the HTML. If the user likes the new layout, read the new coordinates via DevTools and edit them into the file by hand (or ask for that) — dragging in the browser is for TRYING OUT a layout, it doesn't replace the coordinate-planning step 2 of the process.

Keeping it uncluttered — rules pulled from real mistakes

  • Maximize straight lines. Line up two related boxes on the same column/row so their arrow is a single straight segment. Repositioning a box is always better than bending a line.
  • A dedicated corridor for long runs: a long vertical line travels through the empty lane between box columns (the canvas's outer margin is also a good corridor — see the alert line running at x=25 in the example).
  • Keep region titles clear of corridors. The single most repeated mistake across real diagrams: a region's title sits in the top-left corner exactly where vertical lines enter the region → the text gets sliced through. Before placing a title, check the corridor list in the LAYOUT PLAN comment: if a line crosses the region's top edge there, move the title to an empty stretch (the top-right corner works well too).
  • Where two lines must cross → a hop (a small arc jumping over, radius 8): a line curving to the right uses A8,8 0 0 1, curving to the left uses A8,8 0 0 0 (both bulge upward). Only hop across a straight horizontal/vertical segment, never across a diagonal one. Mention "a small arc = two lines passing over each other" in the subtitle.
  • A crossing is only acceptable between 2 DIFFERENT-colored flow groups. Two lines from the same group crossing = the layout is wrong, rearrange it.
  • Group region: a rect with dashes 7 5, opacity 0.55, a bold 11px label, its own color per region. Keep boxes ≥ 20px from the region's border.

Explainer cards

  • In the right sidebar, stacked vertically: one card per flow group, carrying a data-flow matching its code, a dot in the flow's color, and every step number inside the text colored correctly (.n1.n5). The last card is usually "Principles" summarizing the locked-in architectural decisions (no data-flow needed on it).

Checklist before delivering

  • [ ] Does every data flow have BOTH directions (or is it clearly noted why it's only one-way)?
  • [ ] Does check-collisions.js (browser) or check-svg.js (offline) report 0 errors? ≤ 5 warnings and each one explainable?
  • [ ] Opening the file: is the WHOLE diagram visible by default without scrolling (fit-to-view)? Do zoom +/−/⤢, Ctrl+scroll, and drag-to-pan all work?
  • [ ] Click each chip: the right flow lights up, related boxes aren't dimmed by mistake (data-flows is complete), the sidebar shrinks to that flow's card, clicking a badge highlights the right line, Esc clears it? Do the traveling dots clearly run along the right line in the right direction, and does the destination box ping in sync with the dots arriving?
  • [ ] Try both modes (the theme button): does dark mode avoid any stray white patches, are text/lines/badges all legible?
  • [ ] Are badges numbered continuously with no gaps; does every explainer card match its badges?
  • [ ] Does every box for a recognizable technology have its brand logo attached? Are boxes neutral (only 1–2 emphasized ones tinted)?
  • [ ] Have you screenshotted the whole thing and actually looked at it (balanced, no area overly cramped)?
  • [ ] Is the file named clearly, with a version?

How to use it

Copy the folder

Take unclecatvn/flow-diagram from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.