Graphviz Diagram Style Guide
Table of Contents
Design philosophy
Every diagram on wal.sh is a claim about structure. The visual language should make structural relationships legible without decoration. Three principles govern the system:
- Color encodes semantics, not decoration. Each of the six color families carries a fixed meaning across all diagrams. Blue is input and primary flow. Green is output and success. Red is error and critical path. When a reader sees a green node on any page, they know it represents an output or success state without checking a legend. This contract holds across 50+ diagrams and must not be broken for aesthetic reasons.
- Diagrams carry the color; banners stay quiet. Research page banners are greyscale (728 × 90, leaderboard format). The pastel diagrams are the visual centerpiece of each page. A color banner would compete with the diagram for the reader's attention. Greyscale banners recede; diagrams advance.
- The palette is a protocol, not a preference. The Tailwind 100/700 pairs were chosen because they are widely known, machine-readable, and have accessible contrast ratios. Switching to a different palette would require updating every diagram simultaneously. The cost of that coordination is the reason the palette is documented here rather than left implicit.
The six color families
| Family | Fill (100) | Border/Text (700) | Cluster BG (50) | Semantic role |
|---|---|---|---|---|
| Blue | #dbeafe |
#1d4ed8 |
#eff6ff |
Primary data flow, input, process |
| Purple | #ede9fe |
#6b21a8 |
#f5f3ff |
Abstraction, protocol, API |
| Green | #dcfce7 |
#15803d |
#f0fdf4 |
Output, success, storage |
| Amber | #fef3c7 |
#b45309 |
#fffbeb |
Config, orchestration, external |
| Yellow | #fef9c3 |
#a16207 |
#fefce8 |
Warning, intermediate, cache |
| Red | #fee2e2 |
#b91c1c |
#fef2f2 |
Error, critical path, alert |
The 100-weight fills are pastel backgrounds. The 700-weight values serve triple duty: node border, node text, and cluster label. The 50-weight is for cluster backgrounds only — lighter than the node fill so the cluster recedes behind its contents.
Diagram archetypes
The site uses six recurring diagram structures. Each has conventions for layout direction, color assignment, and cluster usage.
Pipeline
Pipelines show data flowing through sequential transformations. They read
left to right (rankdir=LR). Color follows the data lifecycle: blue for
ingestion, amber for processing, green for output. Intermediate stores
(caches, queues) are yellow.
rankdir=LR
[blue: source] → [blue: ingest] → [amber: transform] → [green: output]
↓
[yellow: cache]
Examples: diagram-rag-pipeline, diagram-ci-cd-pipeline,
diagram-2019-ml-pipeline, jq/diagram-pipeline,
kubeflow/diagram-pipeline.
Clusters group stages that share a concern (ingestion plane, serving plane). Each cluster border matches the dominant color family of its contents.
Layer stack
Layer stacks show architectural layers from top (user-facing) to bottom
(substrate). They read top to bottom (rankdir=TB). Each layer is a
cluster with a distinct color family. The top layer is typically blue
(closest to the user); lower layers shift through purple, amber, and green.
rankdir=TB
┌─ blue: Model layer ─────────────┐
│ [blue: Opus] [blue: Haiku] │
└─────────────────────────────────┘
↓
┌─ purple: Capability layer ──────┐
│ [purple: hooks] [purple: MCP] │
└─────────────────────────────────┘
↓
┌─ green: State layer ────────────┐
│ [green: settings] [green: db] │
└─────────────────────────────────┘
Examples: 2026-q2-claude-code-features/diagram-feature-surface,
diagram-tool-systems-layers, diagram-llm-frameworks-taxonomy,
2026-q2-cloudflare-agents/diagram-stack.
The convention: each layer gets its own color family, and the cluster
border matches the nodes inside it. Cross-layer edges use the default grey
(#888); failure-path edges use red.
Comparison
Comparisons place two or more systems side by side. They use rankdir=TB
with clusters arranged horizontally via rank=same or Graphviz's natural
layout. Each system gets a distinct color family. The contrast between
cluster colors is the primary visual signal.
rankdir=TB ┌─ blue: System A ──┐ ┌─ amber: System B ──┐ │ [blue: node] │ │ [amber: node] │ │ [blue: node] │ │ [amber: node] │ └────────────────────┘ └─────────────────────┘
Examples: 2026-agent-isolation-freebsd-jails/diagram-isolation-comparison
(blue=DIY jails, amber=Cloudflare sandbox),
diagram-datalayer-schema-compare (four schema formats, four colors),
diagram-eda-vs-actor, 2025-terminal-ai-agents/diagram-comparison.
The rule: never use the same color family for both sides of a comparison. The reader should distinguish the two systems by color alone.
State machine
State machines show transitions between states. They read left to right
(rankdir=LR). Each state gets a color that reflects its semantic nature:
green for healthy/fresh, amber for degraded/stale, blue for
active/processing, purple for fallback, red for terminal/error.
rankdir=LR
[green: fresh] → [amber: stale] → [blue: revalidating] → [green: fresh]
↓ ↓
[purple: serve-stale] [red: evicted]
Example: diagram-cache-states (HTTP cache lifecycle per RFC 9111).
State machines are the only archetype where individual nodes routinely use different color families. The color of each node is its most important visual attribute — it tells the reader whether the state is healthy, degraded, or terminal before they read the label.
Process loop
Process loops show a cyclical workflow with named phases. They read left to
right (rankdir=LR) to emphasize the forward flow, with a dashed
feedback edge closing the loop. Each phase is a cluster with a distinct
color family, typically cycling through blue, purple, amber, green.
rankdir=LR
┌─ blue: Propose ─┐ → ┌─ purple: Challenge ─┐ → ┌─ amber: Defend ─┐ → ┌─ green: Refine ─┐
│ [blue: scan] │ │ [purple: question] │ │ [amber: answer] │ │ [green: summary]│
└──────────────────┘ └──────────────────────┘ └─────────────────┘ └─────────────────┘
↑ │
└─────────────────────── dashed: resubmit ────────────────────────────────────┘
Example: 2026-elenctic-vibe-code-review/diagram-review-loop.
The feedback edge (green → blue) is always dashed and uses the default grey. Exception nodes (e.g., operational override) use red and connect with red dashed edges.
Ecosystem map
Ecosystem maps show the relationships between tools, libraries, or
systems in a domain. They use rankdir=TB with clusters grouping related
items by category. Color assignment follows function: blue for core
implementations, purple for protocols/standards, green for outputs/tooling,
amber for configuration/build, yellow for community/ecosystem, red for
deprecated or risky components.
rankdir=TB
┌─ blue: Core ────────┐
│ [blue: impl A] │
│ [blue: impl B] │
└──────────────────────┘
↓
┌─ purple: Protocols ──┐ ┌─ amber: Build ──────┐
│ [purple: spec] │ │ [amber: toolchain] │
└───────────────────────┘ └──────────────────────┘
Examples: diagram-scheme-llm-toolkit,
2025-terminal-ai-agents/diagram-ecosystem,
diagram-clojure-ecosystem.
Typography
All text uses fontname"Helvetica"=. Three sizes, no exceptions:
| Element | fontsize |
|---|---|
| Graph label | 11 |
| Node label | 10 |
| Edge label | 9 |
Node conventions
Every node uses shape=box with style"rounded,filled"=. The three color
attributes are mandatory and must come from the same family:
If a node has fillcolor but is missing color or fontcolor, the diagram
is broken. Black text on a pastel background means a missing fontcolor.
Special shapes
shape=note: used for annotation nodes (reviewee, reviewer, legend entries). Still uses the palette; still needs all three color attributes.shape=diamond: used for decision points in pipelines. Typically amber (config/decision semantics).shape=plain: used for invisible structural nodes (start/end markers in state machines). No fill, no border.
Cluster conventions
Clusters group related nodes. The default style is border-only — a colored border with a white interior. This keeps the diagram clean: the pastel node fills carry the color, and the cluster border frames them without adding visual weight.
Use style"rounded,filled"= with a 50-weight fillcolor only when
the cluster background needs to distinguish a region from the graph
background (e.g., layer stacks where the layers must be visually
separated, or comparison diagrams where two systems sit side by side and
need distinct background tints). Prefer border-only for most diagrams.
Edge conventions
- Default:
color"#888", =fontcolor"#555"= - Failure/error paths:
color"#b91c1c", =fontcolor"#b91c1c"= - Optional/fallback:
style=dashed - Deprecated/pressure:
style=dotted - Feedback loops:
style=dashedwith default grey (the loop is structural, not semantic)
Backgrounds
- Graph:
bgcolor=white(always) - Cluster: 50-weight of the cluster's color family
- Never use
#fafafafor clusters; it has no semantic meaning
What to avoid
- Shorthand hex (
#369,#d63): always use full 6-digit hex - Named HTML colors (
lightblue,yellow): not reproducible across renderers - Dark backgrounds with light text: inverts the site's light theme
- Hard saturated fills: always use the 100-weight pastel
- Non-Helvetica fonts: the site CSS uses sans-serif
- Neutral grey clusters (
#444/#fafafa): every cluster should use a color family that matches the semantic role of its contents - Missing
fontcolor: if you see black text on a pastel node, thefontcolorattribute is missing
Banner conventions
Research page banners are 728 × 90 pixels, greyscale, leaderboard format.
magick input.png -resize 728x90^ -gravity center -extent 728x90 -colorspace Gray output.png
Org reference:
#+ATTR_HTML: :alt Description :class banner-leaderboard :width 728 :height 90 [[file:banner.png]]
Real examples by archetype
| Archetype | Canonical example | Page |
|---|---|---|
| Pipeline | diagram-rag-pipeline |
RAG system |
| Pipeline | diagram-observability |
REPL-driven flight tracking |
| Layer stack | diagram-feature-surface |
Claude Code features Q2 |
| Layer stack | diagram-layers |
Unix V4 |
| Comparison | diagram-isolation-comparison |
Agent isolation (jails) |
| State machine | diagram-cache-states |
HTTP cache (RFC 9111) |
| State machine | diagram-order-states |
TLA+ system design |
| State machine | diagram-process-lifecycle |
Unix V4 |
| Process loop | diagram-review-loop |
Elenctic vibe code review |
| Ecosystem map | diagram-ecosystem |
Unix V4 games |
| Ecosystem map | diagram-scheme-llm-toolkit |
Scheme LLM toolkit |
ASCII diagram policy
Any ASCII arrow diagram in a #+begin_example block that represents a data
flow, state machine, or architecture should be converted to a graphviz dot
diagram for visual consistency. ASCII directory trees (├── .config/) and
inline prose arrows (agent loops 200\times \to costs $0.00) stay as text.
Migration status
As of 2026-05-19, all 53+ .dot files follow the canonical palette.
The migration from legacy shorthand hex is complete. Twelve mermaid
diagrams in agentic-systems-q4-2024 converted to dot. New research
pages are audited on arrival. Four research pages that had zero diagrams
(TLA+, Unix V4, Unix V4 Games, flight-tracking observability) now have
them.
