Visual system
Every chart and diagram on this site is inline SVG generated from the same data as the tables beside it, styled from the same tokens as the surrounding page, and animated under one motion policy. This page is the catalogue: for each layout, the decision that selects it, a text blueprint, and the rendered component.
Principles¶
Four rules that decide what a figure is allowed to do. They are the reason no chart here carries a heat map, a gradient, or a rounded corner.
Colour classifies
A mark's colour repeats the classification axis it belongs to — static, dynamic, collected, manual, ownership. It never encodes magnitude, and it never ranks. A bar and a status tag describing the same value always agree.
Every number is written
A bar carries its value as text, a point carries its name, an axis carries its ticks. Removing all colour from the page loses a grouping, never a fact.
Figures are data, not decoration
A figure appears only where it says something the adjacent table cannot. There are no hero images and no illustrative graphics; a figure that repeats its caption is deleted.
Motion is state, not entrance
Animation shows a value arriving or a state changing. Nothing loops except where the loop is the subject, and every rule is inside a reduced-motion guard.
Motion system¶
Three durations and two easings. A transition that cannot be assigned one of them is not added.
linear — reserved for continuous motion that must not appear to
decelerate: streaming pipeline dashes, the flywheel.--ease, cubic-bezier(.2,.6,.2,1) — the default for state
change: hover, focus, colour.--ease-out, cubic-bezier(0,.7,.3,1) — arrivals: bars growing,
lines drawing, a figure entering the viewport.Specimen tracks replaying the three timing functions. With
prefers-reduced-motion: reduce set, the pucks hold still and every rule below is inert.
| Token | Value | Role | Applied to |
|---|---|---|---|
--dur-fast | 120 ms | Pointer feedback | Tag and cell hover, value labels appearing on a chart mark |
--dur-base | 220 ms | State change | Card hover lift, navigation colour, focus ring |
--dur-slow | 480 ms | Arrival | Bars growing from the axis, boxes fading in, the permalink flag |
--dur-draw | 1100 ms | Path drawing | Line series drawn along stroke-dashoffset |
Stagger .d1–.d8 | 60 ms each | Sequencing | A series reads left to right instead of arriving at once |
animation-timeline: view() | Scroll-linked | Trigger | Where supported, a figure animates as it enters; elsewhere it animates on load |
The complete motion vocabulary. Durations are declared once as custom properties; no component defines its own timing.
@media (prefers-reduced-motion: no-preference), so the reduced state is the plain stylesheet
rather than a stripped variant of an animated one. Bars are drawn at full width, lines at full length, and
no information is carried by the animation itself.
Quantitative charts¶
Seven layouts covering comparison, trend, distribution, composition, and schedule. Each is drawn from the atlas dataset rather than from sample numbers, so the figure on this page is the same figure that appears in context.
Bar chart
Use when: one categorical dimension is compared on one measure, and the category labels are too long to fit under columns.
Cat A: [========] 80
Cat B: [=====] 50
Cat C: [==========] 100
0 ------------ max
Column chart
Use when: the categorical dimension is itself ordered — decades, versions, releases — so position along the axis carries meaning.
9 | #
6 | # #
3 | # # # # # #
0 +--------------------
50s 60s 70s 80s 90s 00s
Line chart
Use when: a measure is continuous over an ordered axis and the shape of the change matters more than any single value.
Val ^ _.-*
| _.-*
| _.-*
+------------------> Time
Quadrant / 2×2
Use when: two independent axes place items relative to each other, and the interesting content is which quadrant something falls in, not its exact coordinate.
Checked early
(Static + GC) | (Static + manual)
GC ------------+------------ Manual
(Dynamic+GC) | (Dynamic+manual)
Checked late
Radar / spider
Use when: three or more comparable subjects are scored on the same small set of axes and the shape of the profile is the point.
[Safety]
|
[Speed]---\ | /---[Interop]
\|/
[Tooling]-+-[Simplicity]
Gantt
Use when: several items occupy spans on one shared axis and the overlap between them is the question.
Task 1: [====]
Task 2: [======]
Task 3: [===]
W1 W2 W3 W4 W5
Stacked bar
Use when: each category splits into parts of a whole and both the total and the split matter.
Family A |####----| 8
Family B |###### | 6
collected manual
Pyramid / funnel
Use when: levels are ranked and each one rests on the one below, or a population narrows at each step.
/ \
/ L1 \ (strategic)
/------\
/ L2 \ (tactical)
/----------\
/ L3 \ (foundational)
Layers
Use when: the same stacking is better read as equal tiers, because each one has a name and a note rather than a size.
+-----------------------------+
| Layer 3: Presentation |
+-----------------------------+
| Layer 2: Domain services |
+-----------------------------+
| Layer 1: Infrastructure |
+-----------------------------+
Venn
Use when: two sets overlap and naming the intersection is the whole point.
/-----\ /-----\
/ A \ / B \
( ( A and B ) )
\ / \ /
\-----/ \-----/
Structural diagrams¶
Six layouts for things that have parts. The distinction that decides between them is whether the relationship is containment, descent, connection, or reference.
Architecture
Use when: components, tiers and the connections between them, where the reader needs to know what talks to what.
[Client] --> [API Gateway] --> [Service]
| |
v v
[(Queue)] [(Database)]
Nested / box-in-box
Use when: the relationship is containment — every inner thing is inside exactly one outer thing.
+---------------- Module ------------+
| +---------- File ----------------+ |
| | [Type A] [Type B] | |
| +--------------------------------+ |
+------------------------------------+
internal stops at the module boundary, package at the package boundary, and private at the enclosing declaration.Tree
Use when: a single root descends into children and no node has two parents.
[ Root ]
/ | \
[B1] [B2] [B3]
/ \ |
[L1] [L2] [L3]
Org chart
Use when: the tree encodes ownership and routing — who decides, who is asked, where a change goes.
[ Core team ]
/ \
[ Workgroup ] [ Workgroup ]
/ \ / \
[SG] [SG] [SG] [SG]
ER / data model
Use when: entities have identity, fields and keys, and the cardinality between them is load-bearing.
+-------------+ +--------------+
| PACKAGE |1 *| TARGET |
+-------------+------+--------------+
| PK name | | PK name |
| products | | FK package |
+-------------+ +--------------+
Timeline
Use when: events sit at points on one axis and the reader needs their order and their spacing.
--(2014)---[ 1.0 ]---------------->
--(2019)---[ ABI stable ]--------->
--(2024)---[ Swift 6 ]------------>
Loop / flywheel
Use when: the process has no end state — its output is its own next input.
--> [Build] --
/ \
[Monitor] [Deploy]
\ /
<-- [Test] <--
Flow and process¶
Five layouts for things that happen in an order. The choice between them is whether the axis is logic, time, actor, or data.
Flowchart
Use when: a decision branches the path and the reader has to follow one branch to an outcome.
[Start] --> < Decision? >
| Yes | No
v v
[Action A] [Action B]
Process flow
Use when: the steps are sequential with no branch, and naming the handoffs is what the reader needs.
[Intake] -> [Validate] -> [Execute] -> [Log]
swiftc, with the artefact that crosses each boundary named on the edge. Moving dashes indicate direction without an animated legend.Data flow
Use when: the diagram is about what moves between stages, and the transport or schema is as important as the stage.
[Producer] ==(Kafka)==> [Spark] ==(S3)==> [DW]
Sequence
Use when: several participants exchange messages and the ordering between them over time is the subject.
Actor Service DB
| | |
|--req---->| |
| |--query-->|
| |<--rows---|
|<--res----| |
await may be stale after it.State machine
Use when: a thing occupies exactly one of a fixed set of states and named events move it between them.
(Draft) --[Submit]--> (Review) --[Approve]--> (Live)
^ |
+-------[Reject]------+
Cancelled happens only when the task next checks.Swimlane
Use when: the same process crosses several actors and the handoff between them is where the cost or the risk is.
Lanes | Step 1 Step 2 Step 3
User | [Req] ------+
Ops | v
| [Proc] ---> [Done]
Futures¶
What this design system is not doing yet, and why. Recorded as decisions rather than a backlog, so a later reader can tell a deliberate omission from an oversight.
| Platform capability | Availability | What it would buy | Decision here |
|---|---|---|---|
animation-timeline: view() | Shipping in Chromium and Safari | Figures animate as they enter the viewport, with no scroll listener | In use behind @supports; elsewhere figures animate once on load |
@view-transition | Shipping for same-document; cross-document rolling out | Cross-page navigation could keep the header and nav fixed while the article changes | Not adopted: the site is nine static pages and the transition would be decoration |
| Container queries | Interoperable | A chart could choose its own label density from its own width instead of the viewport's | Used for the motion specimens; charts still key off the viewport |
text-wrap: balance / pretty | Interoperable | Headings and captions break without a one-word last line | Candidate; needs checking against the dense table captions first |
| Dark colour scheme | Available now | A second token block under prefers-color-scheme | Deliberately not shipped: the palette is tuned for contrast on paper-white and would need re-deriving, not inverting |
@scope | Shipping in Chromium and Safari | Component styles could be bounded without class prefixes | Not needed at this size; the whole stylesheet is under 20 KB |
| Scroll-driven progress indicator | Available now | A reading-progress rule on long reference pages | Candidate for the Swift article, which is the only page long enough to want one |
Availability describes browser support at the time of writing, August 2026. Every
row marked as in use is behind an @supports query or a media query, so no page depends on it.
The constraint that holds
No runtime framework and no JavaScript. Every interaction on this site — scroll-triggered figures, hover values, keyboard-scrollable tables, focus rings — is CSS and SVG. Anything that cannot be built that way is not built.
What would change that
Sorting and filtering the comparison matrices are the only features worth script, and only if the tables outgrow reading. Until then, ordering is editorial and stated in each caption.
How a figure gets added
A generator in viz.py, called with real data from atlas.py. A figure whose
numbers are typed in by hand rather than derived is a diagram, and is labelled as one.