Pugflow
Complete offline reference for building, styling, editing, and exporting source-first diagrams.
Getting started
A project needs one Pug file and may have one companion CSS file. Pug describes structure and local overrides; CSS holds reusable node, flow, and annotation types. The smallest diagram is:
graph
.node
.id start
.right-ports distributed
.label Start
Indentation defines ownership. Use two spaces consistently. Blank lines and lines beginning with // are ignored. Save Pug and CSS to keep the project editable; SVG and PNG are export formats.
Canvas and graphs
The canvas is always implied, so an empty file is a valid empty canvas. Put canvas defaults, one or more sibling graph blocks, and cross-graph flows directly at the source root. Graphs never nest. Declare every node directly inside its graph and put a flow inside that graph when both endpoints belong to it. Legacy #canvas and #diagram roots still parse, but new source never needs or emits them.
.background #ffffff
graph
.id main
.label Main process
.label-position outside
.align center
.font-size 16
.font-weight bold
.node
.id root
.label Root
graph
.id separate
.layer 1
.offset (0, 40)
.node
.label Independent graph
Graph titles support .label-position inside|outside, horizontal .align left|center|right, vertical .vertical-align top|middle|bottom, .label-offset (x, y), .color, and all font options. Titles default to top-center inside the graph. Drag a graph title to set its label offset. Use .placement above|below|left|right with .relative-to graph-id to position a graph relative to another graph. Without these fields, each graph is placed below the previous graph. Use .x-spacing and .y-spacing to tune layout within a graph. Frames are automatically packed without overlap by default. Explicit offsets may overlap frames. Clean Up removes small accidental flow kinks within and between graphs while preserving deliberate bends; cross-graph corrections move whole graphs rather than altering their internal layouts. Higher layers render in front; equal layers retain source order. The right-side Objects panel combines collapsible graph ordering with a graph picker and clickable node/flow lists. A flow renders at the higher layer of its endpoint graphs.
Nodes
A node always starts with .node; nest a reusable @node class such as .card directly beneath it to apply that type, exactly as flows nest their class inside .flow. Declaring the class itself as the keyword still works for older documents. Keep identity, label, layout, and appearance fields together beneath it. Declaration order initially controls stacking within the graph, with later nodes in front. Reordering nodes adds integer .layer overrides to the source.
.node
.id service
.layer 1
.label
| Service name
| Multiline description
.shape rounded
.width 220
.height auto
.fill #ffffff
.color #111827
.outline #94a3b8
.outline-style dashed
.outline-width 2
.align left
.vertical-align middle
Nodes do not nest. Give connected nodes IDs and declare their relationships as sibling .flow blocks in the graph.
Flows
Every flow explicitly identifies its source and target node. Branching and merging are inferred when several flows share a source or target.
graph
.node
.id start
.label Start
.node
.id validate
.label Validate
.node
.id publish
.label Publish
.flow
.from start
.to validate
.direction right
.flow
.from validate
.to publish
Directions are right, left, up, and down. Every node face has its own port setting: top-ports, right-ports, bottom-ports, and left-ports. Each is shared by default or may be distributed. Multiple outgoing flows are rendered as branches; multiple incoming flows are rendered as a merge. No separate branch, merge, or connection keyword is needed.
A flow may also connect any two nodes by ID, regardless of their order in the source. This supports convergence, feedback, cross-graph connections, and targets declared later:
.flow
.from archived
.to active
.from-direction up
.to-direction left
.feedback_flow
.label feedback
.from-direction controls the side used to leave the source; .to-direction independently controls the side used to enter the target.
Flows may contain any number of direct annotations. They appear above the route by default and may be dragged to write an .offset:
.flow
.from validate
.to publish
.annotation
| first note
.annotation
.color #dc2626
| second note
Set .arrow-shape chunky for a filled block arrow. Its .color is the fill, while .outline and .outline-width control the outside border. .arrow-height sets shaft height and .arrow-head-width sets head length.
Reusable CSS
Reusable definitions may be written at the source root in Pug or in the optional CSS tab. CSS uses Pugflow at-rules rather than browser selectors:
@node card {
shape: rounded;
fill: #ffffff;
outline: #94a3b8;
shadow-color: #000000;
}
@flow warning {
color: #dc2626;
stroke-style: dashed;
arrow-style: both;
}
@annotation note {
color: #2563eb;
font-style: italic;
}
Nest .card inside .node, and .warning/.note inside the flow or annotation they decorate. Local fields beneath an item override its reusable type. Names must be unique across all four definition kinds: node, flow, graph, and annotation.
Text and annotations
Graph titles, nodes, annotations, and connection labels support font-family, font-size, font-weight, font-style, text-decoration, text-outline, and text-outline-width. Text outlines are transparent with zero width by default. Nodes additionally support horizontal .align left|center|right and .vertical-align top|middle|bottom. A node may have any number of annotations above or below it. In the node inspector, use + Add Annotation, choose Above or Below as a convenient starting position, then edit, hide, delete, or drag each annotation to set its own offset.
.node
.label Important
.font-size 20
.font-weight bold
.font-style italic
.text-decoration underline
.annotation
.above
.font-size 13
.color #2563eb
| Annotation above
.below Annotation below
Use $...$ for inline math and $$...$$ for display math. Pugflow bundles MathJax and renders real TeX as self-contained SVG paths in nodes, node annotations, and flow annotations. Equations are measured during layout and work in offline SVG, PNG, and CLI exports.
Images and shadows
.node
.label Profile
.image photos/profile.png
.image-width 96
.image-height 96
.image-fit contain
.image-opacity 1
.image-padding 8
.shadow-color #000000
.shadow-offset-x 4
.shadow-offset-y 5
.shadow-blur 8
.shadow-opacity 0.3
The node grows to contain the complete image plus padding. Image fit accepts contain, cover, or fill. Drag the image or its resize handles on the canvas to write image geometry and .image-offset.
Hiding items
Add .hidden to a node, annotation, flow, or graph. Hidden nodes remain in layout so before/after versions retain exact placement; their annotations and every touching flow are also hidden. Hidden graphs, nodes, and flows appear struck through in the Objects panel.
Canvas editing
- Click a node, label, annotation, flow, flow label, or graph to select it and jump to its source.
- From a node's properties, use Add Connected Node to create a new node above, below, left, or right.
- Graph boundaries fade in when the pointer approaches them, making transparent graphs easy to find and select.
- Ctrl-click (Cmd-click on macOS) toggles multi-selection. Mixed selections show only actions shared by every selected item.
- Drag nodes, text, images, and graphs; hold Cmd on macOS or Ctrl on other platforms to constrain movement to the dominant axis. Shift remains supported as an alternative. Graph offsets may intentionally overlap frames.
- Use Graph Layer in the graph inspector or the collapsible Graphs list in the Objects panel to control graph stacking.
- Choose a graph in the Objects panel to browse its nodes and flows. Its node list is ordered front to back from declaration order. Dragging nodes, or using Node Layer in the node inspector to send selected nodes to the front or back, writes explicit layers to the source. Clicking any node or flow opens its normal property inspector.
- The origin ghost shows the previous position while dragging.
- A selected node lists all incoming and outgoing flows, including flows crossing graph boundaries. Each flow exposes its source and target faces.
- Text fields provide adjacent expandable Font options for family, style, size, weight, decoration, and color.
- Use node/flow types first, then local inspector values as overrides.
- Align or distribute multiple selected nodes or graphs from the inspector.
- Clean up aligns connected flow nodes and collapses unnecessary bends by correcting offsets. It never changes flow faces or moves untouched sibling branches.
- Undo/Redo and Ctrl+Z/Ctrl+Y cover canvas and direct source edits, including edits that temporarily cause a parse error.
- Zoom affects preview only; Fit frames the drawing. Exports use diagram bounds.
- + New contains Graph, Node, and Flow creation.
- Copy writes a 2× high-DPI PNG to the clipboard. Save opens format and PNG-resolution options for PNG or SVG export.
Source editor
The editor provides line numbers, current-line highlighting, syntax color, color pickers beside hex values, completions with Ctrl+Space, Tab/Shift+Tab indentation, and click-to-source navigation. Vim mode is off by default and supports Normal, Insert, and Visual modes, a block cursor, movement/edit/yank/paste/marks, >/< indentation, and Escape/Ctrl+[ /Ctrl+C to return to Normal mode.
Pug and CSS files
The File menu creates or opens Pug and CSS documents. Save writes the active tab directly to its selected system file, while Save As chooses a new location. The CSS file is optional unless custom types are referenced. Each editor tab shows the exact filename being edited. Hide source collapses the source panel and remembers that preference. Pugflow intentionally starts blank or with --demo; it does not restore source text in browser storage.
Command line
pugflow # blank GUI
pugflow --demo # GUI with feature tour
pugflow --vim # GUI with Vim enabled
pugflow --gui diagram.pug --css diagram.css
pugflow diagram.pug
pugflow diagram.pug --css styles.css --output diagram.png --scale 2
pugflow --no-browser --host 127.0.0.1 --port 4173
Direct rendering uses an installed Edge, Chrome, or Chromium browser. Set PUGFLOW_BROWSER if automatic discovery cannot find it.
Property reference
| Scope | Fields |
|---|---|
| Canvas | background, font, nested defaults.node, defaults.flow, defaults.annotation |
| Graph | id, label, label-position, label-offset, align, vertical-align, layer, placement, relative-to, offset, x-spacing, y-spacing, fill, color, font options, text-outline, text-outline-width, outline, outline-style, outline-width, padding, hidden |
| Node | id, label, optional layer, top-ports, right-ports, bottom-ports, left-ports, shape, fill, color, outline, outline-style, outline-width, width, height, align, vertical-align, offset, label-offset, font options, text-outline, text-outline-width, image, shadow, hidden |
| Flow | from, to, direction, from-direction, to-direction, color (chunky fill), width, outline, outline-width, roundness (corner radius; 0 for sharp bends), stroke-style, arrow-style, arrow-shape, chunky arrow-height and arrow-head-width, source-face, target-face, label, repeated annotation, label-offset, font options, text-outline, text-outline-width, hidden |
| Annotation | color, offset, font options, text-outline, text-outline-width, hidden |
| Font options | font-family, font-size, font-weight, font-style, text-decoration, text-outline, text-outline-width |
| Image | image, image-width, image-height, image-fit, image-opacity, image-padding, image-offset |
| Shadow | shadow-color, shadow-offset-x, shadow-offset-y, shadow-blur, shadow-opacity |