Skip to content

How I structure D2 files

I use D2 for diagrams in my documentation.

D2 vscode extension does not support automatic rendering on file save, so I use RunOnSave extension to run D2 CLI on file save. Files with leading underscore in filename are ignored as I use them as imports.

"emeraldwalk.runonsave": {
"commands": [
{
"match": "\\.d2$",
"notMatch": "\\\\_.*\\.d2$",
"isAsync": true,
"cmd": "cd ${fileDirname} && d2 ${file} ${fileBasenameNoExt}.svg"
}
]
}

To keep things organized, I structure my D2 files like this:

  1. vars and classes
  2. diagram elements or shapes
  3. relationships between elements or connections
  4. tala layout positioning directives

This is how I create new D2 diagram:

  1. Copy my D2 template that I keep in separate file.
  2. Define elements.
  3. Add relationships and see how tala layout positions them automatically.
  4. Adjust positions with top and left directives.

Example D2 file:

# Colors to choose from: https://github.com/terrastruct/d2/blob/master/lib/color/color.go#L190
vars: {
d2-config: {
pad: 2
center: true
theme-id: 5
dark-theme-id: 5
layout-engine: tala
sketch: false
}
}
direction: down
***.label.near: top-center
***.style: {
stroke-width: 1
shadow: true
border-radius: 5
font-size: 8
font-color: black
}
classes: {
arrow: {
style: {
stroke: black
stroke-width: 1
font-size: 8
font-color: black
}
}
box: {
width: 150
height: 50
}
}
cloud: {
label: Azure Cloud
apim: {
label: API Management
class: box
}
vpn: {
label: VPN Gateway
class: box
}
}
onprem: {
label: On-Premises
service: {
label: Service\n\[WinService\]
class: box
}
}
cloud.apim -> cloud.vpn: {class: arrow; label: Private Endpoint}
cloud.vpn -> onprem.service: {class: arrow; label: S2S VPN}
cloud: {top: 0; left: 0; label.near: top-left}
cloud.apim: {top: 0; left: 0}
cloud.vpn: {top: 100; left: 0}
onprem: {top: 300; left: 0; label.near: top-left}