--- id: unclecatvn/agent-skills/flow-diagram version: "d2e2f918" license: MIT install: manual updated: 2026-07-14 --- # Flow Diagram — Flow Diagram generates a single self-contained HTML file that visualizes system architectures and request/response flows with a 2-pane layout: an interactive diagram on the left with zoom, pan, and fit-to-view controls, and an explainer sidebar on the right. Click any flow to highlight it with traveling dots along arrows, dim everything else, and reveal its details. Two-way arrows (solid outbound, dashed return) carry numbered step badges colored by semantic meaning—red for auth, purple for scheduled tasks, amber for data pipelines. Publisher: unclecatvn · Stars: 116 · Updated: 2026-07-14 Install (manual): `git clone https://github.com/unclecatvn/agent-skills` ## SKILL.md # 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](references/flow-diagram-example.html). **Not sure which layout to use** (linear? convergent? hub? branching?) → read [references/layout-patterns-guide.html](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 0. **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. 1. **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](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. 2. **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 `` right after the `` tag — the next editing pass will then know which lane already has an owner. 10 minutes of planning saves 5 rounds of untangling overlaps. 3. **Copy [assets/template.html](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. 4. **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. 5. **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](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](assets/check-svg.js) (`node assets/check-svg.js `, 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. 6. **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-_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](assets/icons-library.html) ships 18 `` 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 ``, place it inside the box's ``: `` (a second logo sits 20px further along). Missing a logo: `curl -s https://cdn.simpleicons.org/` 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=""` to EVERY line, badge (wrapped in ``), and label of that flow; give `data-flows=" "` to the `` 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: "` — 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) `defs` → **arrows** → **regions** → **boxes** → **badges + 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](assets/fix-arrow-gaps.js) (`node assets/fix-arrow-gaps.js `) — 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 `` containing **2 ``** (r≈3.4, `fill` = **the same hex** as the line's `stroke`), each carrying an `` — `path` = `"M x1,y1 L x2,y2"` for a ``, or copy the `d` attribute verbatim for a ``. 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](assets/add-flow-dots.js) (`node assets/add-flow-dots.js `, overwrites in place, idempotent — it strips any existing dots before regenerating, so re-running it any number of times is safe) — it walks every ``/`` with `data-flow` + `marker-end` in the file, generates a `` block matching its exact coordinates, and inserts it right after that arrow. Full example: [assets/template.html](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](assets/add-flow-ping.js) (`node assets/add-flow-ping.js `, 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 `` as a **child of that box's own ``** (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](assets/add-drag-to-rearrange.js) (`node assets/add-drag-to-rearrange.js `, 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? [View on SkillFed](https://skillfed.io/unclecatvn/agent-skills/flow-diagram) · [View on GitHub](https://github.com/unclecatvn/agent-skills)