Diagrams
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.
The numbers#
viewBox="0 0 1200 H", always 1200 wide, height to fit. No radius,
shadow, gradient or texture--surface-raised, 1px --border-subtle, radius 6. Peers share
one treatment; their names tell them apart, not their hues--text-muted. No curves. Dashed
means optional or not yet, and nothing else| 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 |
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 holds
the whole file contract.
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 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.
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 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.
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
.diagramtext and holds the numbers above for the author. Writing a diagram 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 says what the file must be.
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:
flowsequencematrixbarsunitscellschipsnotescomposetilescolumnssketch| 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-okmeans yes. The check in brackets in--status-warnmeans in part. A dash in--status-errormeans no. A question mark in--text-mutedmeans 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.
NEWabove a node is a part that the change adds.CHANGEDin--status-warnis 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-quietfor a failure,--surface-ok-quietfor 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 says when.
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, withrole="img"on the root. - One emphasis, one meaning per sign. The signs above, and nothing new in their place.
Artwork 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.
Drawing rules#
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.
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.
# 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.
# 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.