Diagram conventions
The single source of structural truth for pages under
/docs/architecture/diagrams/. Follow this page when adding a
new diagram. If a structural decision is not covered here, raise it in
the dx- Spine before authoring — silent deviation makes the next
wave's audit harder.
When to write a diagram
Write a diagram when the prose alone cannot convey the temporal or compositional shape of a journey — when the answer to "in what order does this happen?" or "what depends on what?" is the load-bearing content. A diagram is not a substitute for the prose pages it links; it is a complement that earns its place by being faster to read than the prose for a specific class of reader question.
Diagram types
Three supported types. The type picks itself once you state the reader question the diagram answers.
Sequence diagram
Use when the diagram answers "what calls what, in what order?" — a time-ordered exchange between a client surface and one or more backend endpoints. The default for journey diagrams.
Pilot example: Ethos onboarding — seed selection.
Starter snippet:
Flow diagram
Use when the diagram answers "what states does this screen cycle through and what triggers each transition?" — a branched state machine on a single surface. Useful for screens with three or more UI states and non-trivial transition rules.
Example will land in a wave-2 diagram (this delivery ships the sequence pilot only).
Starter snippet:
Component diagram
Use when the diagram answers "what depends on what?" — a static
composition of surfaces, hooks, and endpoints. Useful when several
screens share an endpoint or when a hook fan-outs across surfaces.
Mermaid's flowchart is the rendering primitive; the intent is
structural, not temporal.
Example will land in a wave-2 diagram.
Starter snippet:
File naming
- Folder:
docs/architecture/diagrams/. - File:
<journey-kebab-slug>.mdx. The slug names the journey, not the screen — a diagram that spans multiple screens reads naturally this way (e.g.chat-session-bootstrap.mdx, notchat-session-screen.mdx). The pilot isethos-onboarding-seed-selection.mdx. - No leading underscore. Docusaurus 3.x treats leading-underscore
basenames as partials (importable but not routable), and any
sidebar entry depending on such a file breaks the build with a
"Broken link" error (dx-015 Finding #1 documents the original
encounter). Use plain
kebab-case.mdx. sidebar_positionis an integer assigned intentionally — the pilot is1; subsequent waves take2,3, ... so the sidebar ordering is editorial, not alphabetical accident. The conventions page itself uses99so it sorts last.
Palette mapping
The colors in every diagram are pinned to the design tokens in
src/css/tokens.css.
Mermaid's themeVariables is a JavaScript object passed at init time —
hex literals must be embedded directly (Mermaid does not resolve CSS
var(--token) references). The mapping below is the single audit
target: the same hex values appear in
docusaurus.config.ts
inside themeConfig.mermaid.options.themeVariables, with inline
comments naming each source token.
Light mode (Mermaid base theme + our overrides):
| Role | Token | Hex | Used for |
|---|---|---|---|
| Actor 1 (Client) | --signal-blue | #4F7CFF | first actor box (primaryColor) |
| Actor 1 text | --paper-0 | #FFFFFF | text on first-actor box |
| Actor 1 border | --signal-blue-deep | #2B49B8 | first-actor border |
| Actor 2 (API) | --aletheia | #7A4FDB | second actor box (secondaryColor) |
| Actor 2 text | --paper-0 | #FFFFFF | text on second-actor box |
| Actor 3 (User / external) | --signal-ok | #1F8A5A | third actor box (tertiaryColor) |
| Actor 3 text | --paper-0 | #FFFFFF | text on third-actor box |
| Edges / arrows | --ink-3 | #667085 | signal / arrow color |
| Note background | --signal-warn-tint | #FBEFD8 | Note over … background |
| Note border | --signal-warn | #B5651D | Note over … border |
| Note text | --ink-1 | #0F1720 | Note over … text |
Dark mode (Mermaid dark theme + same overrides):
The vivid signal colors (primaryColor, secondaryColor,
tertiaryColor, the Note accents) carry over unchanged from light —
they already meet contrast on dark backgrounds per the existing
tokens.css design. Surface and text colors come from Mermaid's
built-in dark theme and are not overridden, so the diagram
background and labels adapt automatically to the active color mode.
If you change a value used here in
src/css/tokens.css, update both this table and the matching entry indocusaurus.config.ts→themeConfig.mermaid.options.themeVariablesin the same commit. Otherwise rendered diagrams will drift from the rest of the site and no automated check will catch it.
Cross-link contract
Every diagram page closes with a "Cross-links" group of three sections, in this order:
## Client surface— a bullet per screen actor depicted, linking to the matchingdocs/client/<surface>/page in prose.## API endpoints— a bullet per endpoint depicted, naming verb + path inline and linking to/api/(no?op=query, no#operationIdanchor — per the dx-012 rule that the/api/page is a single-page app whose deep anchors rot across OpenAPI regenerations).## Related guides— optional; bullets pointing at any/docs/guides/<guide>/page whose end-to-end flow passes through the depicted journey.
Reverse half: every diagram's matching client-surface page must
also gain a bullet under its existing ## Related architecture
section linking back to the diagram. Closing the triangle is part of
the same PR as adding the diagram — it is not a follow-up.
Dangling cross-links
If the matching client-surface page for a candidate diagram is still
"Pending" in the
Client surface inventory, defer
the diagram: add the journey to the diagrams Roster
as a Deferred row naming the journey and the blocking surface, with
no .mdx file created until the surface page exists.
Partial cover (one of two depicted screens is "Covered"): the diagram
may ship if the dangling cross-link points at the inventory row
(/docs/client/#screen-inventory) rather than the missing surface
page. In that case the diagram's frontmatter declares
incomplete_cross_links: true and the body names the missing target
explicitly — a later wave finds these with:
grep -l 'incomplete_cross_links: true' docs/architecture/diagrams/
A diagram cannot ship with a literal broken link
(onBrokenLinks: 'throw' is repo-wide).
Accessibility
Two complementary measures, both required:
- One-sentence prose caption immediately above the Mermaid block, restating the journey in words. The caption is the author's own — not boilerplate — and short enough to read as alt-text equivalent. It is rendered unconditionally (even when Mermaid has not yet hydrated, or JavaScript is disabled), so the page still conveys what diagram is meant to be there.
- Mermaid
accTitle:andaccDescr:directives as the first two non-blank lines inside the Mermaid block. theme-mermaid emits these into the rendered SVG's<title>and<desc>for assistive tech.
A starter caption template: "How a user goes from <starting screen>
to <ending screen> through <central call>."
Authoring checklist
Before opening a PR with a new diagram:
- Frontmatter complete:
title,description(≤200 chars),sidebar_position(integer, not auto),last_reviewed(ISO date). - Diagram type matches the reader question the page answers (see "Diagram types" above).
- Mermaid block begins with
accTitle:andaccDescr:. - Prose caption (one sentence) sits immediately above the Mermaid block.
- Every color used is in the palette table above. No raw hex literals inside the Mermaid block.
- Cross-link triangle is closed in this PR: diagram → client
surface(s), diagram →
/api/, and client surface(s) → diagram. - If any cross-link target is "Pending" / "Deferred", the
incomplete_cross_links: truefrontmatter flag is set and the missing target is named in the body. -
npm run typecheck && npm run buildexits 0; no new broken-link warnings, no Mermaid parse errors. - Pre-publication checklist (
seo-conventions.md§7) passes for the new page.