Skip to content
TYPO3Soul Design System

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#

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.

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 .diagram text 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:

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 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, with role="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#

Drawing rules · 980x674

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
Worked example · 1400x683
Fallback — a sequence without a flowchart · 1400x790

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.