Plank help · updated 2026-08-13
Diagrams in a deliverable
When a drawn figure beats a paragraph or a table, and the contract an inline SVG has to meet to belong in a Plank document — brand tokens, one file, light and dark, readable by a screen reader.
Agents: fetch the raw markdown of this page at /en/help/diagrams.md
Diagrams in a deliverable
Some findings are about a relationship — an order, a handoff, a containment, where the time goes — and a paragraph describing one makes the reader rebuild it in their head. A drawn figure hands it over intact.
Most findings are not like that. This page is as much about not drawing as about drawing.
A diagram lives inside the standard deliverable sheet, as inline SVG in the same file as everything else. Quantities have their own page — Dashboards & charts covers the chart chooser and the validated palette. A chart answers how much; a diagram answers how it fits together.
Draw one only when it beats the alternatives
Ask, in this order:
- Would a sentence do it? "Invoices go from registration to matching to approval to posting." That is a sentence. Write the sentence.
- Would a table do it? Anything with named rows and comparable columns is a table, and a table is searchable, copyable and translates without redrawing. If a three-column table says the same thing, the table wins.
- Is the shape the point? If what matters is that a step loops back, that one box contains three others, that two paths diverge and rejoin, or that one span is six times the others — a table has to spell that out and a picture just shows it.
Draw it when the answer to 3 is yes. Concretely, that is: a path or sequence where an order or a handoff carries the meaning; a structure where nesting or containment carries it; a proportion across steps where one part dominates; states and the transitions between them; a decision with branches; overlap between sets.
Don't draw:
- a list of things — that is bullets or a table;
- a simple before/after — two columns;
- a single box with a label — that is a sentence with a border;
- anything you'd have to explain underneath in a paragraph that says the same thing.
A useful tell: if the paragraph you just wrote is enumerating — "first this, then this, which goes back to…" — the diagram is doing work. If it is describing, it is not.
The remove test, before you draw. Every node is a distinct idea; two that always travel together are one node. Every connector carries information; if the relationship is obvious from the layout, delete the line. The drawing isn't finished when everything is in it — it's finished when nothing more can come out.
The contract
Six rules. Rules 1, 2, 4 and 5 are checked mechanically — see Check it — so they are not a matter of judgement or memory. Rule 3 is checked only where it actually collides, and rule 6 is not checked at all; both are on you.
- Colour comes from the sheet's tokens.
fill="var(--ink)",stroke="var(--mist)",fill="var(--blue)". Never a literal#0a0a0f. A Plank deliverable is one file whose light/dark follows its host, and it does that with CSS custom properties. A literal colour is correct in the theme you drew it in and wrong — usually invisible — in the other one. This is the single most common defect in a diagram copied from anywhere else: most diagram tools bake hexes into the SVG and ship a separate file for dark mode, and this house style has nowhere to put a second file. - The SVG is named, or explicitly decorative. A diagram carries
role="img"andaria-labelledby="<slug>-title <slug>-desc". Its<title>is the first child of<svg>, before<defs>— assistive technology may ignore one placed later.<desc>is one sentence saying what the diagram shows to someone who cannot see it: describe the content, not the shapes. "Approval holds the invoice for 6 of the 11 days," not "a row of boxes with bars underneath." An SVG that carries no meaning — a bullet, a rule, an icon next to text that already says it — takesaria-hidden="true"instead. Those are the only two states; an unnamed SVG is announced as a picture with nothing said about it. - Every id inside the diagram is prefixed with the diagram's slug. (Checked only on collision — a unique unprefixed id passes, and then breaks the day a second diagram arrives.) Ids are document-wide. Two diagrams pasted from the same template bring two
id="arrow"markers, andurl(#arrow)resolves to whichever came first — so the second diagram silently borrows the first one's arrowheads, and bareid="title"gets it announced with the first one's name. - It scales, and the type survives the scaling.
viewBox="0 0 W H"on the<svg>, and let CSS size it. Without a viewBox it is cropped on a phone. The trap is that an SVG scales its text along with everything else: a 760-wide viewBox inside the sheet's 656px content box renders every label at 0.86× — a 12px caption arrives as 10.3px, and on a phone as 5px. So make the viewBox 656 wide (the sheet's widest content box, giving 1:1 on a desktop), and let the figure scroll on a narrow screen rather than shrink below the floor. - It stays one file. No
<image href="https://…">, no remote anything. Draw the mark as SVG or inline it as adata:URI. A remote reference is blank the moment the file is forwarded or opened offline, which is most of the time. - Text is real text. (Not checked — nothing can tell a
<path>that spells a word from one that draws a box.)<text>elements, never paths or a picture of type. It has to be selectable, searchable, translatable and legible when the page is zoomed.
The block to paste
Add these rules to the sheet's <style>:
/* The sheet's own rule needs `figure` added, or a figure after a heading
sits 8px lower than every other element does. */
h2 + p, h2 + ul, h2 + figure { margin-top: 12px; }
figure.diagram { margin: 20px 0 0; overflow-x: auto; }
figure.diagram svg {
display: block;
width: 100%;
min-width: 592px; /* scroll rather than shrink the type below the floor */
height: auto;
}
figure.diagram figcaption {
margin-top: 10px;
color: var(--muted);
font-size: 13px;
}
.d-label { font-family: Inter, system-ui, sans-serif; font-size: 14px; font-weight: 600; }
.d-sub { font-family: "JetBrains Mono", monospace; font-size: 13px; }
.d-edge { font-family: "JetBrains Mono", monospace; font-size: 12px; letter-spacing: 0.06em; }
Those three sizes are chosen so the smallest of them still clears 10px at 592px wide, which is the narrowest the figure ever renders. Measured: 14 / 13 / 12 on a desktop, 12.6 / 11.7 / 10.8 on a phone.
And the figure itself. Replace invoice-wait with your own slug everywhere it appears — that prefix is what keeps two diagrams in one document from colliding:
<figure class="diagram">
<svg
viewBox="0 0 656 184"
role="img"
aria-labelledby="invoice-wait-title invoice-wait-desc"
xmlns="http://www.w3.org/2000/svg"
>
<title id="invoice-wait-title">Where a supplier invoice waits</title>
<desc id="invoice-wait-desc">
Five steps from an invoice arriving to it being paid, and a band
underneath showing how long each one takes. Approval by the cost-centre
owner takes 6.1 of the 11.2 days — more than the other four steps put
together.
</desc>
<defs>
<!-- One marker per colour: a marker paints itself, it does NOT inherit
the stroke of the line that uses it. -->
<marker id="invoice-wait-arrow" markerWidth="8" markerHeight="6"
refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="var(--muted)" />
</marker>
<marker id="invoice-wait-arrow-focus" markerWidth="8" markerHeight="6"
refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="var(--blue)" />
</marker>
</defs>
<!-- Connectors first, so the nodes sit on top of them. -->
<line x1="116" y1="72" x2="144" y2="72" stroke="var(--muted)"
stroke-width="1" marker-end="url(#invoice-wait-arrow)" />
<line x1="248" y1="72" x2="276" y2="72" stroke="var(--blue)"
stroke-width="1.5" marker-end="url(#invoice-wait-arrow-focus)" />
<!-- A neutral node. The row carries the SEQUENCE and nothing else — the
band carries proportion, the table carries the values. -->
<rect x="12" y="48" width="104" height="48" rx="6"
fill="var(--surface)" stroke="var(--muted)" stroke-width="1" />
<text class="d-label" x="64" y="77" fill="var(--ink)"
text-anchor="middle">Registration</text>
<!-- The focal node. One per diagram, two at the very most. -->
<rect x="276" y="48" width="104" height="48" rx="6"
fill="var(--blue-soft)" stroke="var(--blue)" stroke-width="1.5" />
<text class="d-label" x="328" y="70" fill="var(--blue-on)"
text-anchor="middle">Approval</text>
<text class="d-sub" x="328" y="87" fill="var(--blue-on)"
text-anchor="middle">6.1 d</text>
<!-- The same steps as one band, to scale. The focal segment is also
TALLER, so the argument survives greyscale and print. -->
<text class="d-edge" x="12" y="124" fill="var(--muted)">ELAPSED DAYS, TO SCALE</text>
<rect x="12" y="138" width="20" height="8" rx="2" fill="var(--muted)" />
<rect x="84" y="132" width="342" height="20" rx="2" fill="var(--blue)" />
<text class="d-sub" x="255" y="172" fill="var(--blue-on)"
text-anchor="middle">6.1 of 11.2 days</text>
</svg>
<figcaption>
Median days per step, Q2 2026. Approval is 54% of the total elapsed time
— nearly twice the next largest step.
</figcaption>
</figure>
This is an excerpt — two of the five nodes and two of the five band segments, so the shape is readable here. The complete deliverable it comes from — sheet, stats, the full diagram, table, decision — ships in the repo as apps/website/src/content/help/assets/example_diagram_deliverable.html, and is the fixture the checks below are tested against, so it is guaranteed to pass them. If you paste the excerpt, rewrite the <desc> to describe what you actually drew — a description of five steps over a picture of two is the exact failure rule 2 exists to prevent.
Craft — the part no checker can see
One focal element. The accent (--blue / --blue-soft / --blue-on) marks the one thing the diagram is about. Everything else is --ink, --graphite, --muted, --mist. Accenting four nodes means you haven't decided which one matters, and the reader gets no signal at all.
Budget: 9 nodes, 12 connectors. Past that it is two diagrams — an overview and a detail — not one dense one. Density is the point of failure: a figure that needs its own key has already lost to the table.
Connectors are the craft. They are also where a generated diagram gives itself away:
- Right-angle elbows, not diagonals. A straight
<line>is fine when both ends share an x or a y; anything else turns a corner. - No two connectors sharing a path or running on top of each other. If two arrows want the same route, the layout is too tight — move a node.
- Where several connectors leave the same edge of a box, give each its own attach point, roughly 12px apart. One arrow hiding another is a failed drawing.
- A connector should not pass behind a box that is neither its source nor its destination. Reroute.
- Label an arrow with an opaque
<rect fill="var(--surface)">behind the text, and leave a 6–10px gap between that rect and the line — a label sitting on its own arrow hides the thing it is labelling. Never rotate label text vertically. - Draw connectors before nodes, so the boxes cover the line ends.
A line that carries meaning is not a hairline. This one is measured, not felt. --mist — the sheet's border token — is 1.3:1 against the sheet in both themes. That is right for a rule separating two blocks of text, and wrong for anything a reader has to see: a node's edge, a connector, a bar drawn to scale. Those are graphical objects and need 3:1. --muted is the lightest token that clears it (4.95:1 light, 5.51:1 dark) — --ink and --graphite clear it comfortably too, but at 1px they read as text weight rather than as a line. --faint (2.4:1 light) and --stone do not clear it at all. --blue clears it against both the sheet and --blue-soft. So:
| Token | |
|---|---|
| Node edges, connectors, arrowheads, to-scale bars | --muted |
| The focal node, its connector, its bar | --blue (fill --blue-soft, text --blue-on) |
| Node labels | --ink |
| Sublabels, units, legend text | --muted |
| A separator under the legend, and nothing else | --mist |
Colour is never the only channel. --blue against --muted is 1.1:1 in luminance, so two bars that differ only in hue are one bar in greyscale — and the recommended way to make a PDF of a deliverable is to print it. Give the focal element a second, non-chromatic difference: the worked example makes its focal band taller (20px against 8px) and its focal node's stroke 1.5px against 1px. Then the argument survives a black-and-white printer, a projector and a colour-blind reader.
Legend at the bottom, outside the drawing, and only if the picture needs one — a horizontal strip under a hairline, never floating among the nodes; extend the viewBox height rather than squeezing the drawing. Before you add one, check that it is not the fourth place the same sentence appears: if the focal node is already labelled, the focal bar is already annotated and the caption already names it, a legend saying "blue = the slow step" is decoration. The worked example has no legend for exactly that reason.
Type. Names in Inter (.d-label); figures, codes, ports and units in JetBrains Mono (.d-sub, .d-edge). Nothing below 10px, and nothing below 11px if it carries a number a reader has to act on — this is a document for someone reading at a desk, not a technical poster.
Geometry. Keep coordinates, widths and gaps on a 4px grid; corner radius 4–8px; hairlines at 1px. Two deliberate exceptions, both in the worked example: a stroke of 1.5px marks the focal element (see the channel rule above), and a bar drawn to scale is sized by its data, not by the grid — start from value / total × (W − gaps) and round only to keep the row summing to W − gaps, never to reach a multiple of 4. Rounding a to-scale bar onto the grid breaks the one thing it is for; the worked example's five segments come to 20, 48, 342, 32 and 180 for 0.4, 0.9, 6.1, 0.6 and 3.2 days, and three of those are off the grid because the data put them there. No shadows and no gradients — borders do that job here. Deliverables are quiet.
Check it
The same tool that proofreads a document checks its diagrams:
mkdir -p /workspace/scripts/deck
curl -sSfL -o /workspace/scripts/deck/plank_deck_qa.py https://plank.md/help/assets/plank_deck_qa.py
python3 /workspace/scripts/deck/plank_deck_qa.py q2-payables.html
Errors — these exit non-zero, so they stop a build → check → repair loop: a paint that is a literal colour instead of a token (including a literal hiding in a var() fallback, and one in the page stylesheet if its selector targets the diagram); an SVG that is neither named nor marked decorative; a <title> that isn't the first child, or is empty, or a missing <desc>; aria-labelledby that doesn't resolve to its own <title>/<desc>; a bare id, or one already used anywhere else in the document; a reference to anything outside the file — a sibling chart.png as much as an https URL; and an <svg> that is never closed.
Warnings — reported but not fatal: a missing viewBox. Read the report; don't key your loop on the exit code alone.
It runs on any .html document, English included, and it is the same command and the same repair loop as the Russian typography check on that page.
What it cannot see is the drawing: whether the connectors are traceable, whether the layout reads, whether nine nodes should have been two diagrams — and above all whether the diagram was worth drawing. If a table says the same thing, the table wins, and no checker will ever tell you that.
In a deck, and in a PDF
- HTML document or dashboard — this is the home. Inline SVG, one file, both themes.
- PDF — print the HTML document from the browser. SVG is vector, so type and hairlines stay crisp at any zoom; a diagram exported as a PNG does not.
.pptxdeck — a deck's slides are native, editable shapes, so an SVG cannot be pasted into one. For a linear path, the deck builders give youprocess(...)on both the.pptxand the HTML-slides path; for anything richer, build the deck as HTML and print it to PDF, where this same block works unchanged. See Presentations for which of the two formats a deck should be.
Credit
The design rules under Craft — the complexity budget, the connector grammar, the accessible-SVG contract, the "would a table do it" gate — are adapted from diagram-design by Cathryn Lavery, used under the MIT licence. The palette, typography, theming and validation here are Plank's own; the original ships a light-only house style in different typefaces and is not brand-compatible as-is.