D2 Diagram Styling Reference

Overview

D2 is a declarative diagramming language that compiles to SVG. This runbook covers every styling pattern used in domus-* diagrams.

Build Commands

Single File

# Basic SVG output
d2 diagram.d2 diagram.svg

# With dark theme (200 = dark)
d2 --theme 200 diagram.d2 diagram.svg

# Watch mode (rebuilds on save)
d2 --watch diagram.d2 diagram.svg

# Specify layout engine
d2 --layout elk diagram.d2 diagram.svg

Batch Build (Makefile)

# domus-captures Makefile target
make diagrams-d2
Makefile implementation
diagrams-d2:
	@echo "Building D2 diagrams..."
	@find docs/modules/ROOT/images/diagrams -name "*.d2" -exec sh -c \
		'd2 --theme 200 "$$1" "$${1%.d2}.svg"' _ {} \;
	@echo "Done."

Document Structure

Every D2 file follows this structure:

# =============================================================================
# Title / Description
# =============================================================================

direction: right

title: {
  label: "// TITLE::HERE"
  near: top-center
  shape: text
  style: {font-size: 28; bold: true; font-color: "#50fa7b"}
}

terminal: {
  label: "> command --flags"
  near: top-left
  shape: text
  style: {font-size: 11; font-color: "#6272a4"}
}

# =============================================================================
# NODES
# =============================================================================

# ... nodes here ...

# =============================================================================
# CONNECTIONS
# =============================================================================

# ... connections here ...

# =============================================================================
# FOOTER
# =============================================================================

footer: {
  label: "[ site.domain.dev ]"
  near: bottom-center
  shape: text
  style: {font-size: 11; font-color: "#6272a4"}
}

Animated Connections

The animated: true property creates moving "marching ants" effect - dashes flow along the line direction. Only visible in SVG viewed in browser.

Basic Animation

# Simple animated connection
a -> b: {style: {animated: true}}

# With label
a -> b: "flow" {style: {animated: true}}

Animated + Styled

# Colored animated line
a -> b: "critical" {style: {stroke: "#ff5555"; stroke-width: 3; animated: true}}

# Thicker for emphasis
a -> b: "primary" {style: {stroke: "#50fa7b"; stroke-width: 4; animated: true}}

Animated + Dashed

Combining stroke-dash with animated creates moving dashed lines:

# Static dashed (no movement)
a -> b: {style: {stroke-dash: 4}}

# Animated dashed (moves!)
a -> b: {style: {stroke-dash: 4; animated: true}}

Real Example: Workflow Pipeline

stage0 -> stage1: "capture" {style: {stroke: "#ff5555"; stroke-width: 3; animated: true}}
stage1 -> stage2: "process" {style: {stroke: "#ff79c6"; stroke-width: 3; animated: true}}
stage2 -> stage3: "queue" {style: {stroke: "#bd93f9"; stroke-width: 3; animated: true}}
stage3 -> stage4: "approve" {style: {stroke: "#f1fa8c"; stroke-width: 3; animated: true}}
stage4 -> stage5: "deploy" {style: {stroke: "#50fa7b"; stroke-width: 3; animated: true}}

Visual Priority Hierarchy

Use stroke-width, color, and dash patterns to indicate priority:

Priority Width Style Code

P0 Critical

4

Solid, bright red, animated

{stroke: "#ff5555"; stroke-width: 4; animated: true}

P1 High

3

Solid, orange, animated

{stroke: "#ffb86c"; stroke-width: 3; animated: true}

P2 Normal

2

Solid, purple, animated

{stroke: "#bd93f9"; stroke-width: 2; animated: true}

Background/Deps

2

Dashed, cyan, animated

{stroke: "#89dceb"; stroke-width: 2; stroke-dash: 4; animated: true}

Monitoring

1

Dashed, dim

{stroke: "#6272a4"; stroke-width: 1; stroke-dash: 4}

Complete Example
focus -> active: "NOW" {style: {stroke: "#ff5555"; stroke-width: 4; animated: true}}
focus -> done: "DONE" {style: {stroke: "#50fa7b"; stroke-width: 3; animated: true}}
focus -> planned: "NEXT" {style: {stroke: "#bd93f9"; stroke-width: 2; animated: true}}
focus -> infra: "RUNS ON" {style: {stroke: "#f5a623"; stroke-width: 2; stroke-dash: 4; animated: true}}
focus -> growth: "LEARNING" {style: {stroke: "#ff79c6"; stroke-width: 2; stroke-dash: 4; animated: true}}

Stroke Dash Values

Value Appearance Use Case

stroke-dash: 3

Short, tight dashes

Draft borders, WIP items

stroke-dash: 4

Medium dashes

Dependencies, background flows

stroke-dash: 5

Long, loose dashes

Optional, future items

Node Styling

Basic Node

node: "Label Text" {
  style: {
    fill: "#1e1e2e"
    stroke: "#f38ba8"
    stroke-width: 2
    font-color: "#cdd6f4"
    font-size: 14
  }
}

Complete Node Style Options

node: {
  label: "Multi-line\nLabel"
  shape: rectangle

  style: {
    # Background
    fill: "#0d0d0d"

    # Border
    stroke: "#ff5555"
    stroke-width: 3
    stroke-dash: 5           # Dashed border (WIP/draft)

    # Text
    font-color: "#ff5555"
    font-size: 14
    bold: true
    italic: false

    # Effects
    shadow: true             # Drop shadow
    opacity: 0.8             # Transparency
    border-radius: 8         # Rounded corners
  }
}

Draft/WIP Node (Dashed Border)

wip_item: ":draft: true" {
  style: {
    fill: "#1a1a1a"
    stroke: "#f1fa8c"
    stroke-dash: 3
    font-color: "#f1fa8c"
    font-size: 10
  }
}

Focus/Highlight Node

focus: {
  label: "[FOCUS]\n2026-02-21"
  shape: diamond
  style: {
    fill: "#0d0d0d"
    stroke: "#f5a623"
    stroke-width: 4
    font-color: "#f5a623"
    font-size: 14
    bold: true
    shadow: true
  }
}

Shapes

Shape Visual Use Case

rectangle

Standard box (default)

General nodes, items

diamond

Rotated square

Focus points, decisions

hexagon

Six-sided

Services, APIs, active items

cylinder

Database barrel

Databases, storage, endpoints

oval

Ellipse

Start/end points

circle

Perfect circle

Status indicators

cloud

Cloud shape

External services

package

Folder/box

Containers, groups

text

Label only (no shape)

Titles, annotations, footers

Shape Examples
start: "Begin" {shape: oval}
decision: "Check?" {shape: diamond}
api: "API Service" {shape: hexagon}
db: "Database" {shape: cylinder}
external: "Cloud" {shape: cloud}
title: "Title Text" {shape: text}

Grid Layouts

Create grid-based layouts inside containers:

container: "[2] CLASSIFY" {
  style: {
    fill: "#0d0d0d"
    stroke: "#bd93f9"
    stroke-width: 2
    font-color: "#bd93f9"
    font-size: 16
    bold: true
  }

  grid-columns: 3
  grid-gap: 4

  w: "WRK" {style: {fill: "#50fa7b"; font-color: "#0d0d0d"; font-size: 9; bold: true}}
  mo: "MON" {style: {fill: "#8be9fd"; font-color: "#0d0d0d"; font-size: 9; bold: true}}
  mt: "MTG" {style: {fill: "#ff79c6"; font-color: "#0d0d0d"; font-size: 9; bold: true}}
  pl: "PLN" {style: {fill: "#f1fa8c"; font-color: "#0d0d0d"; font-size: 9; bold: true}}
  lr: "LRN" {style: {fill: "#ffb86c"; font-color: "#0d0d0d"; font-size: 9; bold: true}}
  dc: "DOC" {style: {fill: "#8be9fd"; font-color: "#0d0d0d"; font-size: 9; bold: true}}
}

Grid Options

container: {
  grid-columns: 3         # Number of columns
  grid-rows: 2            # Number of rows (optional)
  grid-gap: 4             # Spacing between items

  # Items auto-flow into grid
  item1: "A"
  item2: "B"
  item3: "C"
  item4: "D"
  item5: "E"
  item6: "F"
}

Text Labels & Positioning

Title Positioning

title: {
  label: "// DOMUS::CAPTURES"
  near: top-center
  shape: text
  style: {font-size: 28; bold: true; font-color: "#ff79c6"}
}

Position Options

Position Location

near: top-left

Top left corner

near: top-center

Top center

near: top-right

Top right corner

near: bottom-left

Bottom left corner

near: bottom-center

Bottom center

near: bottom-right

Bottom right corner

Terminal-Style Header

terminal: {
  label: "> domus-infra --status --date=2026-02-21"
  near: top-left
  shape: text
  style: {font-size: 11; font-color: "#6272a4"}
}
footer: {
  label: "[ captures.domusdigitalis.dev ]"
  near: bottom-center
  shape: text
  style: {font-size: 11; font-color: "#6272a4"}
}

Direction

Controls flow direction of the diagram:

direction: right    # Left to right (default, preferred)
direction: down     # Top to bottom
direction: up       # Bottom to top
direction: left     # Right to left

Color Palettes

Dracula Theme (Primary)

Name Hex Use

Background

#0d0d0d

Node fills

Red

#ff5555

Critical, P0, errors, active

Orange

#ffb86c

High priority, P1

Yellow

#f1fa8c

Warning, review, pending

Green

#50fa7b

Success, complete, deployed

Purple

#bd93f9

Strategic, P2, classify

Pink

#ff79c6

Learning, drafts, personal

Cyan

#8be9fd

Info, monitoring, docs

Comment

#6272a4

Dim text, annotations

Catppuccin Mocha (Alternative)

Name Hex

Base

#1e1e2e

Surface

#313244

Red

#f38ba8

Peach

#fab387

Yellow

#f9e2af

Green

#a6e3a1

Mauve

#cba6f7

Pink

#f5c2e7

Blue

#89b4fa

Teal

#94e2d5

Text

#cdd6f4

Subtext

#a6adc8

Containers (Grouping)

active: "[ACTIVE] IN PROGRESS" {
  style: {
    fill: "#0d0d0d"
    stroke: "#ff5555"
    stroke-width: 3
    font-color: "#ff5555"
    font-size: 16
    bold: true
  }

  grid-columns: 2

  k3s: {
    label: "K3s Cluster\n--\nmaster-01\nVault Agent"
    shape: hexagon
    style: {fill: "#ff5555"; font-color: "#0d0d0d"; font-size: 10; bold: true}
  }

  vault-ha: {
    label: "Vault HA\n--\nPhase 5\n3-Node Raft"
    shape: hexagon
    style: {fill: "#ff5555"; font-color: "#0d0d0d"; font-size: 10; bold: true}
  }
}

Connection Types

Basic Arrow

a -> b             # Directed
a -- b             # Undirected
a <-> b            # Bidirectional

With Labels

a -> b: "label"
a -> b: "label" {style: {stroke: "#ff5555"}}

Connection to Container Items

active.k3s -> infra.storage: "VM" {style: {stroke: "#fab387"; stroke-dash: 4}}
active.vault-ha -> infra.vault: "Raft" {style: {stroke: "#50fa7b"; stroke-dash: 4}}

Complete Dark Theme Template

Copy this as a starting point:

# =============================================================================
# Diagram Name - Dark Theme
# =============================================================================

direction: right

title: {
  label: "// TITLE::HERE"
  near: top-center
  shape: text
  style: {font-size: 28; bold: true; font-color: "#50fa7b"}
}

terminal: {
  label: "> command --help"
  near: top-left
  shape: text
  style: {font-size: 11; font-color: "#6272a4"}
}

# =============================================================================
# NODES
# =============================================================================

focus: {
  label: "[FOCUS]"
  shape: diamond
  style: {
    fill: "#0d0d0d"
    stroke: "#f5a623"
    stroke-width: 4
    font-color: "#f5a623"
    font-size: 14
    bold: true
    shadow: true
  }
}

stage1: "[1] INTAKE" {
  style: {
    fill: "#0d0d0d"
    stroke: "#ff5555"
    stroke-width: 2
    font-color: "#ff5555"
    font-size: 14
  }
}

stage2: "[2] PROCESS" {
  style: {
    fill: "#0d0d0d"
    stroke: "#bd93f9"
    stroke-width: 2
    font-color: "#bd93f9"
    font-size: 14
  }
}

stage3: "[3] COMPLETE" {
  shape: cylinder
  style: {
    fill: "#50fa7b"
    stroke: "#0d0d0d"
    stroke-width: 2
    font-color: "#0d0d0d"
    font-size: 14
    bold: true
    shadow: true
  }
}

# =============================================================================
# CONNECTIONS
# =============================================================================

focus -> stage1: "START" {style: {stroke: "#f5a623"; stroke-width: 4; animated: true}}
stage1 -> stage2: "process" {style: {stroke: "#ff5555"; stroke-width: 3; animated: true}}
stage2 -> stage3: "complete" {style: {stroke: "#50fa7b"; stroke-width: 3; animated: true}}

# =============================================================================
# FOOTER
# =============================================================================

footer: {
  label: "[ site.domusdigitalis.dev ]"
  near: bottom-center
  shape: text
  style: {font-size: 11; font-color: "#6272a4"}
}

File Organization

docs/modules/ROOT/
+-- images/diagrams/           # PRODUCTION - committed SVGs
|   +-- workflow.d2            # Source
|   +-- workflow.svg           # Generated
+-- examples/diagrams/         # DRAFTS - alternative versions
    +-- workflow-v2-radial.d2
    +-- workflow-v3-vertical.d2
    +-- workflow-v7-dark.d2

SVG Output Notes

Animation only works in SVG viewed directly in browser.

  • PNG/PDF exports render static dashed lines

  • Antora image embeds may not animate (depends on browser)

  • For guaranteed animation, link to SVG file directly

Troubleshooting

Animation Not Showing

  1. Verify viewing SVG in browser (not image viewer)

  2. Check animated: true is inside style: {} block

  3. PNG exports never animate

# WRONG
a -> b: {animated: true}

# CORRECT
a -> b: {style: {animated: true}}

Colors Not Rendering

Use hex format with quotes:

# WRONG
style: {fill: #ff5555}
style: {fill: red}

# CORRECT
style: {fill: "#ff5555"}

Layout Overlapping

  1. Try different direction: values

  2. Use containers to group related nodes

  3. Switch layout engine: d2 --layout elk

Font Not Bold

# WRONG (must be inside style)
node: {bold: true}

# CORRECT
node: {style: {bold: true}}

Quick Reference

Animated Line

a -> b: {style: {stroke: "#ff5555"; stroke-width: 3; animated: true}}

Dashed Animated

a -> b: {style: {stroke: "#89b4fa"; stroke-dash: 4; animated: true}}

Draft Node

draft: "WIP" {style: {fill: "#1a1a1a"; stroke: "#f1fa8c"; stroke-dash: 3}}

Grid Container

container: {grid-columns: 3; grid-gap: 4; item1: "A"; item2: "B"; item3: "C"}

Title Text

title: {label: "TITLE"; near: top-center; shape: text; style: {font-size: 24; bold: true}}