---
title: "Diagrams"
description: "A diagram carries an explanation that prose alone shows slower, and the set is the system's visual leitmotif."
canonical: index.html
navigation-title: "Diagrams"
---

<a id="diagrams"></a>

# Diagrams

- [The numbers](#the-numbers)
- [On a slide](#on-a-slide)
- [Two ways to a drawing](#two-ways-to-a-drawing)
- [Drawn from a text](#drawn-from-a-text)
- [Drawn by hand](#drawn-by-hand)
- [Drawing rules](#drawing-rules)
- [Worked examples](#worked-examples)
- [For a drawing tool](#for-a-drawing-tool)

A diagram carries an explanation that prose alone shows slower, and the set
is the system's visual leitmotif. A shared grammar keeps them from one-off
drawings whose colour, geometry and meaning a reader learns again each time.

**One claim per diagram.** The heading of the page states it, and the
caption under the drawing states its consequence. The drawing carries
neither. Two claims are two diagrams.

**If the drawing still works as a bulleted list, it is not a diagram.**
Position, length or alignment carries the meaning. Boxes and arrows are the
last resort, not the first vocabulary.

Solid means there. A dashed outline of the same shape means absent or not yet
reachable, so a shortfall has a *size*, not a sentence. Where the absent part
is a degradation, not a precondition, the dashed outline carries
`--status-warn`.

Orange marks the one thing the diagram is about: exactly one element per
drawing, often a connector, since the claim is usually a relation. When the
drawing is about degradation or failure, status colour replaces the accent
and orange stays out.

<a id="the-numbers"></a>

## The numbers

| | |
| --- | --- |
| Canvas | `viewBox="0 0 1200 H"`, always 1200 wide, height to fit. No radius, shadow, gradient or texture |
| Margin | 60 units every side. Nothing enters it, labels included |
| Type | Source Sans 3; identifiers, paths and flags in Source Code Pro. The value of a tile 36 · node title 16 · node body 14 · label 13. **13 is the floor** |
| Stroke | 1 node outline, 1.5 connector or boundary, 2 for the one accented connector |
| Radius | 6 node or boundary, 4 bar, 2 unit square. Never above 6 |
| Node | `--surface-raised`, 1px `--border-subtle`, radius 6. Peers share one treatment; their names tell them apart, not their hues |
| Boundary | Hairline only, **no fill**. A filled container makes depth out of colour |
| Connector | 1.5px, orthogonal, one arrowhead, `--text-muted`. No curves. Dashed means optional or not yet, and nothing else |

> [!WARNING]
> **Colour is an attribute**, never a rule in a `<style>` block, which
> GitHub strips. Each attribute is the token with the light hex behind it,
> `fill="var(--text-primary, #1C1A17)"`. The hex is what a page shows, and
> the token is ready for the day a page can read it. Ship that one file and
> wrap its shapes in `<g id="soul-ref">`, the handle `make diagrams`
> reads them out from under for the specimen cards. [Artwork](../artwork.md) holds
> the whole file contract.

A diagram sits on `--surface-sunken`. The drawing brings its own canvas,
which makes it read as a figure with clear space. On `--surface-canvas` it
dissolves into the page.

<a id="on-a-slide"></a>

## On a slide

A drawing for a slide keeps the grammar and leaves the frame. It has no
eyebrow, no title and no closing line: the slide's head says them. It has
no margin, as the slide's margin is its margin. Its canvas is the room of
its layout in the frame's pixels, which [Slides](../slides.md) states, and not 1200
wide. A drawing in parts is a row of drawings: the slide gives each its
plane, its word and its caption as text. `slide-lookup.svg` is one
drawing for `wide`, the three `slide-cache-*.svg` a row.

<a id="two-ways-to-a-drawing"></a>

## Two ways to a drawing

A drawing comes from a text or from a hand. Both follow the grammar on this
page, and both give the same file: an SVG in the tokens, 1200 wide.

- **From a text.** The engine draws a `.diagram` text and holds the
  numbers above for the author. [Writing a diagram](writing.md) is the whole of that path:
  where the text goes, its syntax, and a drawing of each layout.
- **By hand.** A drawing the layouts cannot make comes from a hand, in a
  tool or in an editor. The author then holds every number and rule on this
  page. [Drawn by hand](#diagrams-by-hand) says what the file must be.

<a id="drawn-from-a-text"></a>

## Drawn from a text

The engine in `packages/frontend/src/lib/diagram/` holds the numbers
above, so a text cannot break them. It refuses a text too wide for its box,
type under 13 and a radius above 6. It also refuses a second emphasis of
another kind and a connector label with no room. The error names the part,
so the author shortens the text or widens the gap.

A flow on a page first
moves room between its columns, then grows up to twice its width. Columns
grow the same way, for a long word or a control that cannot wrap. The
engine refuses only what still does not fit.

The element is the source, and a built page holds the drawing as a file.
`sds-diagram` draws its text live: in a story, on a card, on a page
written by hand. The finish step of a site draws each one once, into a file
under `_diagrams/`. The page keeps a small `svg` with the title, the
description and a `use` of the group `soul-ref` in that file. So the
shapes read the page's tokens and turn with its mode, and the browser draws
nothing.

A drawing on a figure slide gets the room the slide gives it. The step
works that room out from the slide's head, with no browser. A deck's stage
can give another room. Then a second drawing stands beside the first, and
the stylesheet shows the one for the place of the slide. A drawing a press
zooms keeps its element, because the viewer is its work.

The text names its layout. Each layout is a shape of claim:

| Layout | The claim it carries |
| --- | --- |
| `flow` | nodes on a grid, orthogonal connectors, boundaries around nodes |
| `sequence` | lanes and numbered steps in phases, time down the page |
| `matrix` | rows against columns with a sign in each cell, and a legend |
| `bars` | lengths on one scale, in segments, against a dashed target |
| `units` | counts as unit squares, a missing part as dashed squares in its place |
| `cells` | things as cells in named groups, each with its sign |
| `chips` | identifiers as chips in rows or columns: planned, small, later |
| `notes` | a numbered list, the sentences behind the markers of a sketch |
| `compose` | layouts side by side or one under the other, with one emphasis |
| `tiles` | figures side by side, the value largest |
| `columns` | the parts of a whole as columns of items |
| `sketch` | screens as nested regions, with numbered places |

Each drawing has one emphasis: one part, or peers of one kind that make the
same claim. `claim` sets its colour: the accent, or a
status colour when the drawing is about a failure or a degradation. A part
in the state `absent` or `degraded` gets the dashed outline of the same
shape.

The signs have one meaning each, in every drawing, by hand or from a text:

- A check in `--status-ok` means yes. The check in brackets in
  `--status-warn` means in part. A dash in `--status-error` means no. A
  question mark in `--text-muted` means not known.
- A filled dot means present. A ring means planned.
- A screen with a filled page means in the page. A screen with a filled
  side means in a panel next to the page. Both filled means both. A filled
  top means in the bar over the page. A filled left side means in the
  navigation beside the page. A filled top with a band under it means in
  the head of a module. A sheet with lines means in a form or a dialog. A
  screen with each part in a quiet fill means a place that each case fills
  in.
- A filled chip means part of the plan now. An outlined chip means a small
  first version, and a bare identifier means later. A ring under a chip adds the
  one condition the legend names.
- `NEW` above a node is a part that the change adds. `CHANGED` in
  `--status-warn` is a part that the change alters, and its edge is solid
  in the same colour. A dashed edge in that colour is a degraded part.
- A phase of a sequence is a band. A grey band is a step on the way. A band
  in the quiet wash of a status ends in that status. That is
  `--surface-error-quiet` for a failure, `--surface-ok-quiet` for the
  result.
- A head and shoulders before a name is a person. A cylinder before a name
  is a store that the other parts read and write.
- An ink disc with a number is a step of a sequence. On a sketch, it is a
  place that the text refers to.

A drawing from a text carries its faces. A picture shown as an image sees no
face of the page. So the file brings a cut of each face and weight that it
sets, in one `<style>` block. That block holds faces and nothing else:
every colour stays an attribute. A renderer that drops the block sets the
shipped face or the system's instead. The engine measures every text
against the widest fallback too, so the text still fits its box.

A drawing from a text is the drawing alone. It is 1200 wide, with its margin
and its canvas, and with no eyebrow, title or consequence in it. The page
around it says the claim in its heading and the consequence in the caption
of `sds-figure`. The title and the description of the text are the
accessible name of the file.

In a page, the drawing takes the width of its column. It shrinks until its
smallest type, 13, stands at 9 pixels, and a narrower column scrolls it. So
a phone shows a part of the drawing at a size a reader can read.

With a slide's room, the engine draws the same text with no margin, at the
room's pixels. It refuses a drawing taller than the room, and says how much
of the room the drawing fills.

In a room that a slide lends, the engine also sets the drawing dense. The
type stays at its sizes, and the controls, rows, pads and gaps are lower.
[Writing a diagram](writing.md) says when.

<a id="diagrams-by-hand"></a>

<a id="drawn-by-hand"></a>

## Drawn by hand

A drawing by hand keeps every number of this page. A reader must not see
which way a drawing came.

- **Canvas and margin.** `viewBox="0 0 1200 H"`, 60 units free on every
  side. For a slide, the room of its layout and no margin.
- **Colour as an attribute.** Every fill and stroke is the token with the
  light hex behind it, `fill="var(--text-primary, #1C1A17)"`. No colour
  goes into a `<style>` block, and no gradient, shadow or texture goes
  anywhere.
- **The handle.** The shapes stand in `<g id="soul-ref">`. A page
  references that group, so the drawing reads the page's tokens.
- **Type.** Source Sans 3 and Source Code Pro, 13 at the smallest. Set the
  text in the drawing as `<text>`, not as paths, so a reader can find it.
- **The name.** `<title>` holds the claim and `<desc>` what the drawing
  shows, with `role="img"` on the root.
- **One emphasis, one meaning per sign.** The signs above, and nothing new
  in their place.

[Artwork](../artwork.md) holds the whole file contract. The prompt at
the foot of this page gives the same rules to a drawing tool. A file by hand
has no `.diagram` beside it, so `make diagrams` reads its shapes and
leaves the file as it is.

<a id="drawing-rules"></a>

## Drawing rules

[Drawing rules · 980x674](../../_cards/guidelines/diagrams-rules.card.html)

<a id="worked-examples"></a>

## Worked examples

Different shapes of claim need different structures. The examples below use
an axis, a sequence and containment instead of boxes joined by arrows.

[System overview — a map with no axis · 1400x759](../../_cards/guidelines/diagrams-overview.card.html)

[Worked example · 1400x683](../../_cards/guidelines/diagrams-example.card.html)

[Fallback — a sequence without a flowchart · 1400x790](../../_cards/guidelines/diagrams-fallback.card.html)

<a id="for-a-drawing-tool"></a>

## For a drawing tool

The rules above as an instruction for a drawing tool, with the file contract
and the negative constraints. It stands here in full because it is a thing
to hand over. Copy the whole block and replace `[CLAIM]` and
`[CONSEQUENCE]`, the only two fields that change. New numbers or new states
start a second grammar, and a set in two grammars costs a reader at every
drawing.

A diagram is the one drawing that does not come back as a picture. The
result is the SVG itself, because its colours are tokens and \``make
diagrams`\` reads its shapes out for the cards above. A drawing that a layout
can make is a `.diagram` that the engine draws. The prompt is for a
drawing that no layout makes.

The diagram prompt — replace only the claim and its consequence

````markdown
# Explanatory diagrams

A diagram carries a claim. An illustration only sets a register beside the
heading it stands under. If a reader need not understand a position, a
connection or a quantity in the image, draw an illustration instead:
`docs/design-system/illustration-prompt.md` is that prompt.

A diagram does not come back as a picture. It is one SVG file in the grammar
below, shipped as source. The colours in it are tokens a page can reach into,
and `make diagrams` reads the shapes back out for the specimen cards.

## The fixed language

- **Canvas:** `viewBox="0 0 1200 H"`, always 1200 wide, the height to fit. A
  flat rectangle at `var(--surface-canvas, #FBFAF7)` fills it. No radius,
  shadow, gradient or texture.
- **Margin:** 60 units every side. Nothing enters it, labels included.
- **Type:** Source Sans 3, with identifiers, paths and flags in Source Code
  Pro. Node title 16 · node body 14 · label 13. **13 is
  the floor.** A diagram that needs smaller type carries too much.
- **Stroke:** 1 node outline, 1.5 connector or boundary, 2 for the one
  accented connector.
- **Radius:** 6 node or boundary, 4 bar, 2 unit square. Never above 6.
- **The two states:** solid means there. A dashed outline of the same shape
  in the same place means absent or not yet reachable. So a shortfall has a
  *size*, not a sentence. Dashed means nothing else.
- **Colour:** names tell peers apart, never hue. Exactly one element carries
  the accent, often a connector, since the claim is usually a relation.
- **Accessibility:** the drawing is the explanation, never decoration. The
  root names a `<title>` with the claim and a `<desc>` with the axes.

## Prompt

Replace `[CLAIM]` and `[CONSEQUENCE]` and leave the rest unchanged:

```text
Use case: explanatory diagram
Asset type: one hand-written SVG file, 1200 units wide, height to fit

Primary request: Draw a diagram that makes this single claim visible:
[CLAIM]. The line it closes on is: [CONSEQUENCE].

Canvas: viewBox="0 0 1200 H", H chosen to fit the drawing. A flat rectangle at
var(--surface-canvas, #FBFAF7) fills it. 60 units of margin on every side, and
nothing enters that margin, labels included. No outer radius, no shadow, no
gradient, no texture anywhere in the file.

Structure: the claim is carried by position, length or alignment — an axis, a
sequence, containment, a span across a scale. Boxes joined by arrows are the
last resort, not the starting vocabulary. If the drawing would still work as a
bulleted list, change the structure rather than adding detail to it.

Frame: none. No eyebrow, no title, no lead and no closing line in the
drawing. The page around it states the claim in its heading and the
consequence in the caption under the drawing. The root still names a
<title> with the claim.

Type: 'Source Sans 3' with a system-ui fallback for prose, 'Source Code Pro'
with a monospace fallback for identifiers, paths and flags. Node title 16
semibold, node body 14, label 13. 13 is the floor and nothing goes under it.

Shapes: node radius 6, bar radius 4, unit square radius 2, never above 6. A
node is var(--surface-raised, #FFFFFF) behind a 1-unit
var(--border-subtle, #E3DFD6) outline. A boundary is a hairline with no fill —
a filled container makes depth out of colour. A connector is 1.5 units,
orthogonal, one arrowhead, var(--text-muted, #726C63), and never curved.

The two states: a filled shape is what you get. A dashed outline of that same
shape, in that same place, is what is missing or not yet reachable, drawn in
var(--text-muted, #726C63). Dashed carries no other meaning. Where the missing
part is a degradation rather than a precondition, the dashed outline takes
var(--status-warn, #986200) instead.

Colour: peers share one treatment and are distinguished by their names, never
by hue. Ink is var(--text-primary, #1C1A17), a quieter line
var(--text-secondary, #4A453D), a label var(--text-muted, #726C63). Exactly
one element in the whole drawing carries var(--accent, #FF8700), and it is the
one thing the diagram is about. Where the diagram is about degradation or
failure, status colour replaces the accent and orange stays out entirely.

File contract: every colour is a presentation attribute written as a var()
with the light hex behind it. No style block, and no fill or color on the root
element. The root carries role="img" and aria-labelledby pointing at a title
and a desc inside it. Every drawn shape sits inside one group with the id
soul-ref. No comment in the file may contain two dashes in a row: that is
malformed XML, and such a file draws nothing wherever it is fetched.

Constraints: one claim and no second one; no legend repeating what the shapes
already say, no colour key, no hue palette, no icon set, no screenshot of an
interface, no logo, no watermark.

Avoid: flowcharts of everything, decorative or double-headed arrows, curved
connectors, drop shadows, elevation, rounded canvases, multicoloured node
sets, isometric or three-dimensional treatment, clip art, dense small type,
fine hatching and decorative clutter.
```

## The claim

Write the claim as a sentence before you draw. A topic, "how the sources
work", has nothing to draw. A claim, "bundled knowledge is the only source
that spans the whole axis", decides the structure by itself. It names the
thing under comparison and the scale of the comparison.

The heading states the claim and the caption states its consequence. Two
claims are two diagrams. A drawing that carries both ends up as boxes joined
by arrows, the shape of a claim that has stopped being one.

Then pick the structure from the claim, not from the shapes. A span along a
scale wants an axis. A thing that must happen before another wants a
sequence. A thing that holds others wants containment. Boxes and arrows are
what remains when none of those fits.

Which drawings a set holds is a property of that set, not of this prompt.
`packages/frontend/assets/diagrams/` owns that list. A copy here is an
inventory that falls behind the files.

## What to hand back

One file in `packages/frontend/assets/diagrams/`, named after the claim, not
the drawing. Then `make diagrams`. It reads the shapes out from under
`soul-ref` for the specimen cards. It refuses a file without the group or
the `viewBox`, and a file with a comment that is not valid XML.

Look at it twice. At 1200 wide, where a reader reads the type. And at the
width a card gives it, where only position, length and alignment survive. If
the second view no longer makes the claim, the claim was in the labels.
````
