Skip to main content

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, not chat-session-screen.mdx). The pilot is ethos-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_position is an integer assigned intentionally — the pilot is 1; subsequent waves take 2, 3, ... so the sidebar ordering is editorial, not alphabetical accident. The conventions page itself uses 99 so 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):

RoleTokenHexUsed for
Actor 1 (Client)--signal-blue#4F7CFFfirst actor box (primaryColor)
Actor 1 text--paper-0#FFFFFFtext on first-actor box
Actor 1 border--signal-blue-deep#2B49B8first-actor border
Actor 2 (API)--aletheia#7A4FDBsecond actor box (secondaryColor)
Actor 2 text--paper-0#FFFFFFtext on second-actor box
Actor 3 (User / external)--signal-ok#1F8A5Athird actor box (tertiaryColor)
Actor 3 text--paper-0#FFFFFFtext on third-actor box
Edges / arrows--ink-3#667085signal / arrow color
Note background--signal-warn-tint#FBEFD8Note over … background
Note border--signal-warn#B5651DNote over … border
Note text--ink-1#0F1720Note 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 in docusaurus.config.tsthemeConfig.mermaid.options.themeVariables in the same commit. Otherwise rendered diagrams will drift from the rest of the site and no automated check will catch it.

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 matching docs/client/<surface>/ page in prose.
  • ## API endpoints — a bullet per endpoint depicted, naming verb + path inline and linking to /api/ (no ?op= query, no #operationId anchor — 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.

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:

  1. 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.
  2. Mermaid accTitle: and accDescr: 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: and accDescr:.
  • 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: true frontmatter flag is set and the missing target is named in the body.
  • npm run typecheck && npm run build exits 0; no new broken-link warnings, no Mermaid parse errors.
  • Pre-publication checklist (seo-conventions.md §7) passes for the new page.