diagram-design
Create branded architecture, architecture delta, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, polar chart (polar/radial lollipop), loop/flywheel, nested, tree, org chart, layer stack, exploded axonometric, axonometric plan, Venn, pyramid/funnel, treemap and marimekko, heatmap, bar and dumbbell, waterfall, line (slopegraph, ridgeline, streamgraph, bump), Gantt and scatter charts (bubble, beeswarm, box plot), high-level, process,
Install
npx skills add https://github.com/cathrynlavery/diagram-design --skill diagram-designCore SVG primitives
Routed from SKILL.md §6. SKILL.md keeps the six connector rules as one line each, the arrow roles, and a one-line node box summary. This file holds the markup and the long form of every rule. The copied template already carries the background rect, the commented dotted-paper pattern, and all three arrow markers, so load this file when you need exact markup or a rule's edge cases.
Type-specialized primitives (lifeline, activation bar, region) live in the relevant type reference linked in SKILL.md §3.
Snippets here and in every type reference name colors and fonts by role, never by value: {ink}, {muted}, {paper}, {accent}, {link}, {soft} for colors, {ink @ 0.10} for a role at an opacity, and {title}, {node-name}, {sublabel}, {eyebrow}, {arrow-label}, {callout} for font families. Resolve each against the active skin in style-guide.md [blocked] before emitting: {ink @ 0.10} becomes the ink color as rgba(r,g,b,0.10), and {sublabel} becomes that role's full family stack. Uppercase names (X, FILL, STROKE) are geometry and per-node values you compute.
Background
Default: clean paper, no dot pattern. Single <rect> filled with paper. Don't wrap the diagram in a secondary container background — the diagram sits directly on the page.
Optional: dotted paper variant. When a long-form editorial diagram benefits from textured ground (essays, hero diagrams on a dedicated page), opt in by adding the dots pattern and a second rect:
Don't use the dot pattern when the diagram sits inside a product page, slide, or card — the texture compounds with surrounding chrome and reads as noise.
Arrow markers (define all three, always)
Draw arrows before boxes so z-order puts lines behind nodes.
The geometry source gate excludes shapes inside definition-only containers (defs, symbol, marker, pattern, clipPath, mask) because those shapes do not paint at their local coordinates. It does not resolve <use> instances; check referenced drawings with rendered lint and visual review. Inert HTML comments and > inside quoted tag attributes do not alter definition custody. The gate reads single- and double-quoted SVG attributes.
Mandatory connector rules
These six rules are non-negotiable. Run the pre-output checklist (SKILL.md §9) to verify before producing any diagram.
- Rounded right-angle (orthogonal) connectors are mandatory. Never use diagonal
<line>or straight slanted paths between nodes that don't share an x or y axis. Every bend must be a quarter-arc withr=8(orr=6minimum for tight layouts). Seereferences/type-architecture.mdfor the elbow-path formula. Reserve plain straight<line>only for connections whose endpoints share the same x or y coordinate. Diagonal connectors are an automatic fail. A connector also leaves and enters each box perpendicular to the edge it touches, at a port on the straight part of that edge at least 8px from a corner. A horizontal segment that starts on a top or bottom edge, or a vertical one that starts on a side edge, runs along the border behind the node fill and reads as an arrow coming out of the corner. From the installed skill directory,python3 scripts/verify_geometry.py <file>reports diagonal connectors, border-riding segments, and corner ports. - Label-to-connector margin: 6–10px gap, always. A label must never sit on its arrow — the connector must remain visible. Place the label centered above (or beside, for vertical segments) the line with a minimum 6px gap between the bottom of the label's mask rect and the connector stroke. The opaque mask rect prevents the arrow from bleeding through, but the visible gap between mask edge and line preserves the reader's ability to trace the connection. If the label is large enough that 6px feels cramped, push it to 8–10px. Never let the mask rect touch or overlap the stroke.
- No overlapping connectors. Two connectors must never share the same stroke path, run parallel on top of each other, or be drawn on top of each other for any segment. When two orthogonal arrows must cross at a single point, apply the bridge / hop primitive (see
references/type-architecture.md§ Crossing arrows). When two arrows naturally want to overlap, offset their routing by ≥12px so each line is independently traceable. If you find yourself stacking connectors, redesign the layout — it means two nodes are too close, or the diagram is over budget (split into overview + detail). -
Shared edge → fan the attach points. When two or more connectors enter or exit the same edge of a box, each must have its own distinct attach point along that edge — no two connectors may share a single point on a box. Spread the attach points evenly along the edge with ≥12px between adjacent points (8px minimum for very small boxes). Routing rules:
- For N connectors on an edge of length L, attach point
k(1..N) sits at offsetL * k / (N + 1)from the edge's leading corner. - When the connectors fan out to destinations on different sides, route each one orthogonally from its own attach point — no merging strokes near the box.
- When two parallel connectors run in the same direction, keep them ≥12px apart along their entire length, not just at the attach point. Each arrow must remain independently traceable end-to-end.
No connector may hide another. If you can't tell two arrows apart at a glance, the layout has failed.
- For N connectors on an edge of length L, attach point
-
A connector must not pass behind a box that isn't its source or destination — except when the box is geometrically unavoidable on a direct orthogonal path. Reroute around intervening boxes by default. The only legitimate exception is when a cross-cutting node (e.g., a footer service, a horizontal layer bar) physically sits between the connector's source and destination on the only straight path between them. In that exception:
- The stroke must be dashed (e.g.,
stroke-dasharray="4,3") to signal "transit, not interaction" — it tells the reader the intervening box is not an endpoint. - The label sits at the visible end of the connector (typically near the source) so it doesn't fall behind the intervening box.
- No marker (arrowhead) may land on the intervening box's edge — the marker resolves at the true destination only.
When in doubt, reroute. The exception exists for the narrow case where rerouting is geometrically impossible, not as a shortcut to avoid layout work.
- The stroke must be dashed (e.g.,
- A label mask must not overlap a node drawn after it. Compare their bounds after supported group and element
translate()offsets, in the shared canvas rather than separate panel-local coordinates. Rule 2 keeps the label off its own connector; this one keeps it off the boxes. Because nodes are painted after labels, a mask that lands partly inside a node is covered by the node fill and the text renders as a fragment sitting on the node border. Place the label on a segment of the connector that runs through open canvas — for a connector leaving a node's right edge, that means clearing the node'sx + widthbefore the mask starts. A mask fully inside a node is a badge chip and is fine; a mask overlapping a zone container is fine too, since zones are painted first. From the installed skill directory, verify withpython3 scripts/verify_geometry.py <file>.
Node box — full pattern
Arrow labels — always mask, always with margin
Every arrow label needs an opaque rect behind it. Without one it bleeds through the line. And the label must sit with a visible gap above the connector — never on top of it.
Rules:
- ≤14 characters, all-caps, centered on segment midpoint.
- Mandatory 6–10px gap between the bottom of the mask rect and the arrow stroke. The connector must remain visible — a label that hides its own arrow is a hard fail.
- Never
writing-modevertical. - For vertical segments, place the label to the side (not on the line) with the same 6–10px horizontal gap.
Font substitution. Don't estimate per-character widths — use the style-guide.md budget (0.60em sans / 0.62em mono per char). A production run sized mono labels at ~0.47em/char and clipped twice; the calibrated budget fits both the default skin's mono face (Geist Mono) and the common substituted mono faces (Menlo, Courier New, Monaco, all ≈0.60em). Previews via rsvg/inkscape can't fetch the injected Google Fonts, so verify with python3 scripts/verify_geometry.py <file> from the skill directory — it checks masks against later-painted nodes, not text fit. scripts/self_check.py checks text against the visible box it sits in, but not against an arrow-label mask, so on masks holding the budget is yours.
Legend — horizontal strip at the bottom
Never put the legend inside the diagram area. Place as a horizontal strip after all nodes, with a hairline separator:
Expand SVG viewBox height by ~60px.
Accessible SVG contract
SKILL.md §12 lists these six points one line each; this is the long form.
Every diagram is an accessible figure by default:
- Its
<svg>carriesrole="img"andaria-labelledbynaming the diagram's<title>and<desc>. <title>is the first child of<svg>, before<defs>. Assistive technology may ignore a title placed later.- The IDs are prefixed per diagram and variant:
<slug>-title/<slug>-desc, where the slug matches the file (loop,loop-dark,loop-full). Baretitle/descIDs are banned — two inline diagrams would otherwise share one ID, and the second could be announced with the first's name. Each naming ID must also be unique across the whole document, including non-SVG elements; the packagedscripts/self_check.pyrejects these collisions. <title>is the short name of the subject — roughly the page<h1>, and about 60 characters or fewer.<desc>is one sentence stating what the diagram shows in terms a reader needs without the image. Describe the content, not the geometry: “Org chart showing a command center routing work to specialist agents and escalation owners,” not “A box at the top with five boxes below it.” A shape-by-shape narration is worse than no useful description.- Decorative-only SVG, such as the specimen glyphs in
assets/icons.html, carriesaria-hidden="true"instead.