diagram-design

Create technical and product diagrams — architecture, flowchart, sequence, state machine, ER / data model, timeline, swimlane, quadrant, nested, tree, layer stack, venn, pyramid — as standalone HTML files with inline SVG. Ships with a neutral editorial skin and a first-run gate that prompts users to customize the style guide (colors, fonts) from their own website before generating. Includes annotation-callout primitive and optional sketchy variant.

install
source · Clone the upstream repo
git clone https://github.com/cathrynlavery/diagram-design
Claude Code · Install into ~/.claude/skills/
git clone --depth=1 https://github.com/cathrynlavery/diagram-design ~/.claude/skills/cathrynlavery-diagram-design-diagram-design
manifest: SKILL.md
source content

Diagram Design

Create visual diagrams as self-contained HTML files with inline SVG and CSS, following an opinionated editorial design system.

Thirteen diagram types. One shared design system, complexity budget, and taste gate. Type-specific conventions live in

references/
and are loaded only when you pick a type.


0. First-time setup — style guide gate

Before generating your first diagram in a new project, verify the style guide has been customized.

Open

references/style-guide.md
and check the default tokens. If they're still the shipped defaults (paper
#faf7f2
, ink
#1c1917
, accent
#b5523a
rust), pause and ask the user:

"This is your first diagram in this project. The style guide is still at the default (neutral stone + rust). Do you want to customize it to match your brand first? Options: (a) run onboarding — I'll pull colors and fonts from your website, (b) paste your tokens manually, (c) proceed with the default for now."

Then branch:

  • (a) → follow
    references/onboarding.md
    to fetch the site, extract palette + fonts, propose a diff, and write
    style-guide.md
    .
  • (b) → accept the user's tokens and write them into
    style-guide.md
    under a new "Custom tokens" section.
  • (c) → proceed; optionally remind the user they can run onboarding later.

Once the style guide has been customized (or the user explicitly opted for default), skip this gate on subsequent runs. A simple way to detect customization: if the

accent
value in
style-guide.md
differs from
#b5523a
, assume custom.

Don't silently ship default-skinned diagrams into a branded project — that's the failure mode this gate exists to prevent.


1. Philosophy

The highest-quality move is usually deletion.

From

.impeccable.md
: "Confident restraint. Earn every element. One color accent, two families, a small spacing vocabulary. If removing it wouldn't hurt the page, remove it."

Applied to diagrams:

  • Every node represents a distinct idea. Two nodes that always travel together are one node.
  • Every connection carries information. If the relationship is obvious from layout, remove the line.
  • Coral is editorial, not a flag. 1–2 focal nodes per diagram. Using it on 5 nodes erases the signal.
  • The diagram isn't done when everything is added. It's done when nothing can be removed.

Target density: 4/10. Enough to be technically complete. Not so dense it needs a guide. Above 9 nodes, it's probably two diagrams.


2. When to Use

Use for any of the 13 diagram types (§3) when a reader will learn more from a visual than from prose, a table, or a bulleted list.

Don't use for:

  • Quick unicode diagrams → use wiretext.
  • Lists of things → table or bullets.
  • Simple before/after → table.
  • One-shape "diagrams" → just write the sentence.

Before drawing, ask: Would the reader learn more from this than from a well-written paragraph? If no, don't draw.


3. Diagram Types

Selection guide

If you're showing…UseReference
Components + connections in a systemArchitecturetype-architecture.md
Decision logic with branchesFlowcharttype-flowchart.md
Time-ordered messages between actorsSequencetype-sequence.md
States + transitions + guardsState machinetype-state.md
Entities + fields + relationshipsER / data modeltype-er.md
Events positioned in timeTimelinetype-timeline.md
Cross-functional process with handoffsSwimlanetype-swimlane.md
Two-axis positioning / prioritizationQuadranttype-quadrant.md
Hierarchy through containment / scopeNestedtype-nested.md
Parent → children relationshipsTreetype-tree.md
Stacked abstraction levelsLayer stacktype-layers.md
Overlap between setsVenntype-venn.md
Ranked hierarchy or conversion drop-offPyramid / funneltype-pyramid.md

Rules of thumb:

  • If a 3-column table communicates the same thing, pick the table.
  • If you're combining two types, pick the dominant axis — don't hybridize grammars.
  • If you're past the complexity budget (§7), split into an overview + detail.

Always load the relevant

references/type-*.md
before drawing — it contains layout conventions, anti-patterns, and example files for that type.


4. Universal Anti-patterns

These mark "AI slop" diagrams of any type:

Anti-patternWhy it fails
Dark mode + cyan/purple glowLooks "technical" without design decisions
JetBrains Mono as blanket "dev" fontMono is for technical content — ports, commands, URLs. Names go in Geist sans.
Identical boxes for every nodeErases hierarchy
Legend floating inside the diagram areaCollides with nodes
Arrow labels with no masking rectBleeds through the line
Vertical
writing-mode
text on arrows
Unreadable
3 equal-width summary cards as defaultGeneric grid — vary widths
Shadow on any elementShadows are out. Borders are in.
rounded-2xl
on boxes
Max radius 6–10px or none
Coral on every "important" nodeCoral is 1–2 editorial accents, not a signaling system

Type-specific anti-patterns live in each

references/type-*.md
.


5. Design System

The design system is skinnable. All colors, typography, and tokens live in a single source of truth —

references/style-guide.md
. This file describes semantic roles (
paper
,
ink
,
muted
,
accent
,
link
, …). The default skin is littlemight.com (warm paper, ink, coral); to apply your own brand, either edit
style-guide.md
directly or run the URL-based flow described in
references/onboarding.md
.

When specs below or in type references mention "ink", "accent", "muted", etc., look up the current hex value in

style-guide.md
.

Semantic roles (at a glance)

RolePurpose
paper
,
paper-2
Page bg and container bg
ink
Primary text / stroke
muted
,
soft
Secondary text, default arrows, sublabels
rule
,
rule-solid
Hairline borders
accent
,
accent-tint
1–2 focal elements per diagram
link
HTTP/API calls, external arrows

Focal rule:

accent
goes on 1–2 elements max. Everything else is
ink
/
muted
/
soft
. If you're tempted to accent 4 things, you haven't decided what's focal yet.

Node type → treatment

TypeFillStroke
Focal (1–2 max)
accent-tint
accent
Backend / API / Stepwhite
ink
Store / State
ink @ 0.05
muted
External / Cloud
ink @ 0.03
ink @ 0.30
Input / User
muted @ 0.10
soft
Optional / Async
ink @ 0.02
ink @ 0.20
dashed
4,3
Security / Boundary
accent @ 0.05
accent @ 0.50
dashed
4,4

Typography (summary — full spec in style-guide.md)

  • Title — Instrument Serif, 1.75rem, 400 — H1 only
  • Node name — Geist (sans), 12px, 600 — human-readable labels
  • Sublabel — Geist Mono, 9px — ports, URLs, field types
  • Eyebrow / tag — Geist Mono, 7–8px, uppercase, tracked — type tags, axis labels
  • Arrow label — Geist Mono, 8px — annotation on arrows
  • Editorial aside — Instrument Serif italic, 14px — callouts only

Mono is for technical content. Names are Geist sans. Page title is Instrument Serif. Italic Instrument Serif is reserved for annotation callouts. Never JetBrains Mono as a blanket "dev" font.

<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">

6. Core SVG Primitives

Universal building blocks. Type-specialized primitives (lifeline, activation bar, region) live in the relevant

references/type-*.md
. Optional primitives:

Background

<defs>
  <pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
    <circle cx="1" cy="1" r="0.9" fill="rgba(11,13,11,0.10)"/>
  </pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f4ed"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.6"/>

Arrow markers (define all three, always)

<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
  <polygon points="0 0, 8 3, 0 6" fill="#52534e"/>
</marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
  <polygon points="0 0, 8 3, 0 6" fill="#f7591f"/>
</marker>
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
  <polygon points="0 0, 8 3, 0 6" fill="#1a70c7"/>
</marker>
ArrowStrokeWhen
Defaultmuted
#52534e
Internal, generic
Accentcoral
#f7591f
Primary / highlighted / headline
Link-blue
#1a70c7
HTTP/API calls, external systems
Dashed
stroke-dasharray="5,4"
+ any color
Optional, passive, return, async

Draw arrows before boxes so z-order puts lines behind nodes.

Node box — full pattern

<!-- 1. Opaque paper mask — prevents arrows bleeding through transparent fills -->
<rect x="X" y="Y" width="W" height="H" rx="6" fill="#f5f4ed"/>
<!-- 2. Styled box -->
<rect x="X" y="Y" width="W" height="H" rx="6" fill="FILL" stroke="STROKE" stroke-width="1"/>
<!-- 3. Rectangular type tag (rx=2, NOT a pill) -->
<rect x="X+8" y="Y+6" width="28" height="12" rx="2" fill="transparent" stroke="STROKE@0.40" stroke-width="0.8"/>
<text x="X+22" y="Y+15" fill="STROKE@0.8" font-size="7" font-family="'Geist Mono', monospace"
      text-anchor="middle" letter-spacing="0.08em">API</text>
<!-- 4. Node name (Geist sans — human-readable) -->
<text x="CX" y="CY+2" fill="#0b0d0b" font-size="12" font-weight="600"
      font-family="'Geist', sans-serif" text-anchor="middle">Node Name</text>
<!-- 5. Technical sublabel (Geist Mono) -->
<text x="CX" y="CY+18" fill="#52534e" font-size="9"
      font-family="'Geist Mono', monospace" text-anchor="middle">tech:port</text>

Arrow labels — always mask

Every arrow label needs an opaque rect behind it. Without one it bleeds through the line.

<rect x="MID_X-18" y="ARROW_Y-12" width="36" height="12" rx="2" fill="#f5f4ed"/>
<text x="MID_X" y="ARROW_Y-3" fill="#65655c" font-size="8"
      font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">WRITE</text>

Rules: ≤14 characters, all-caps, centered on segment midpoint, 8–10px above line. Never

writing-mode
vertical.

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:

<line x1="30" y1="LEGEND_Y-8" x2="VIEWBOX_W-30" y2="LEGEND_Y-8"
      stroke="rgba(11,13,11,0.10)" stroke-width="0.8"/>
<text x="30" y="LEGEND_Y+8" fill="#52534e" font-size="8" font-family="'Geist Mono', monospace"
      letter-spacing="0.14em">LEGEND</text>
<!-- Items — horizontal row, ~160px apart -->

Expand SVG

viewBox
height by ~60px.


7. Layout & Spacing

4px grid

All values — font sizes, padding, node dimensions, gaps, x/y coords — divisible by 4. Non-negotiable.

CategoryAllowed values
Font sizes8, 12, 16, 20, 24, 28, 32, 40
Node width / height80, 96, 112, 120, 128, 140, 144, 160, 180, 200, 240, 320
x / y coordinatesmultiples of 4
Gap between nodes20, 24, 32, 40, 48
Padding inside boxes8, 12, 16
Border radius4, 6, 8

Exempt: stroke widths (0.8, 1, 1.2), opacity values, and the 22×22 dot-pattern.

Quick check: if a coordinate ends in 1, 2, 3, 5, 6, 7, 9 — fix it.

Complexity budget (per diagram)

LimitRule
Max nodes9
Max arrows / transitions12
Max coral elements2
Max lifelines (sequence)5
Max lanes (swimlane)5
Max items (quadrant)12
Max entities (ER)8
Max nesting levels (nested)6
Max tree depth4
Max layers (layer stack)6
Max circles (venn)3
Max layers (pyramid)6
Max annotation callouts2

If you exceed, split into two diagrams (overview + detail).

Page layout

  1. Header — eyebrow (Geist Mono), title (Instrument Serif), optional subtitle (Geist muted).
  2. Diagram container
    #efeee5
    bg, 1px
    rgba(11,13,11,0.12)
    border, 8px radius,
    overflow-x: auto
    .
  3. Summary cards — 2–3 col grid with varied widths (e.g.,
    1.1fr 1fr 0.9fr
    ).
  4. Footer — colophon in Geist Mono, muted, hairline top border.

8. Summary Card Pattern

Don't use 3 identical generic cards. Vary the treatment:

<div class="card">
  <p class="eyebrow">SECTION LABEL</p>
  <div class="card-header">
    <span class="card-dot coral"></span>
    <h3>Card Title</h3>
  </div>
  <ul><li>Item</li></ul>
</div>

Rules:

  • background: #ffffff
    (not paper — slight lift without shadow)
  • border: 1px solid rgba(11,13,11,0.12)
  • border-radius: 6px
    ,
    padding: 1.25rem
  • No
    box-shadow
  • Card dots: 7px,
    border-radius: 50%
    — ink / muted / coral / link / soft variants

9. Pre-Output Checklist (Taste Gate)

Run before producing any diagram.

Type fit:

  • Right type for what I'm showing? (§3 selection guide)
  • Would a table / paragraph do the same job? (If yes — don't draw.)
  • Loaded the matching
    references/type-*.md
    ?

Remove test:

  • Can I remove any node? (Would a reader still understand?)
  • Can I merge any two nodes? (Do they always travel together?)
  • Can I remove any arrow? (Is the relationship obvious from layout?)
  • Can I remove any label? (Does color or shape already signal it?)

Signal:

  • Coral used on ≤2 elements? If more, which actually deserve focal status?
  • Legend covers every type used — and nothing extra?
  • Within the type's complexity budget (§7)?

Technical:

  • Arrows drawn before boxes?
  • Every arrow label has an opaque
    fill="#f5f4ed"
    rect behind it?
  • Legend is a horizontal bottom strip, not floating?
  • No vertical
    writing-mode
    text?
  • viewBox
    expanded for the legend strip (~60px)?
  • Every font size, coord, width, height, gap divisible by 4?

Typography:

  • Human-readable names in Geist sans, not Geist Mono?
  • Technical sublabels (ports, commands, URLs) in Geist Mono?
  • Page title in Instrument Serif?
  • Annotation callouts (if any) in italic Instrument Serif? (see primitive-annotation.md)
  • No JetBrains Mono anywhere?

10. Templates & Variants

Every diagram ships in three variants (see

assets/
):

VariantFile patternWhen to use
Minimal light (default)
template.html
,
example-<type>.html
Screenshot-ready. Diagram + title. Warm paper.
Minimal dark
template-dark.html
,
example-<type>-dark.html
Dark mode sites, slides, high-contrast posts.
Full editorial
template-full.html
,
example-<type>-full.html
Long-form posts where the diagram is the hero.

Sketchy variant (optional, applied to any of the above) — see primitive-sketchy.md. SVG turbulence filter wobbles strokes for a hand-drawn feel. Good for essays, not for technical docs.

To create a new diagram

  1. Copy the variant closest to what you want (
    template.html
    for minimal,
    template-full.html
    for cards).
  2. Load the matching
    references/type-<name>.md
    for layout conventions.
  3. Replace the eyebrow, h1, and SVG body.
  4. Run the §9 taste gate.

11. Output

Always produce a single self-contained

.html
file:

  • Embedded CSS (no external except Google Fonts)
  • Inline SVG (no external images)
  • No JavaScript required

Renders correctly in any modern browser.