Reusable Doc Components
These render live on this page — so you can see exactly what ships. The inventory of everything authoring-related lives in Site Authoring Capabilities; this page is the visual companion.
When to reach for a visual (and which one)
Section titled “When to reach for a visual (and which one)”Add a diagram when a section conveys a PROCESS, a STRUCTURE/RELATIONSHIP, or a CHANGE-OVER-TIME — something with 2+ parts and the connections between them. Use prose or a table for a definition, a single fact, a linear narrative, or a flat list. One hero visual per concept/viewport; the caption states the takeaway. (Full rationale: the visual-primitives research.)
| Your content is… | Reach for | Not |
|---|---|---|
| Tiers / “how it’s layered” | LayerStack | a flat list |
| A step-by-step procedure | StepStack | a wall of prose |
| A horizontal pipeline (label-only) | StageFlow | StepStack (that’s vertical + text) |
| Phases over time / a roadmap | Timeline | a date list |
| ”Don’t do X, do Y” / before↔after | PatternPair | two separate paragraphs |
| Ownership / “what talks to what” | PackageBoundary, RuntimeOwnership | — |
| Branching decisions / “which path?” | DecisionTree, DecisionTreeWizard | bare Mermaid when you need edge labels + outcomes |
| Scannable hub of links | Starlight <CardGrid> / <LinkCard> | a bespoke SVG |
| An arbitrary relationship graph | a themed Mermaid graph | a hand-rolled SVG |
Setup: import with the ~ alias
Section titled “Setup: import with the ~ alias”Components only run in .mdx files. Import them with the ~ alias (~ → app/src) — no deep ../../../ paths:
---title: My architecture page---import RuntimeOwnership from '~/components/diagrams/RuntimeOwnership.astro';
<RuntimeOwnership />RuntimeOwnership
Section titled “RuntimeOwnership”A “single runtime is the execution authority” diagram, including the red bypass anti-pattern. It’s interactive by default — press Play flow to walk a request through the system step by step (each step highlights its node and captions the hop). Override title / caption for your own system, or pass interactive={false} for a purely static figure.
Runtime ownership
{/* interactive (default): shows the "Play flow" button */}<RuntimeOwnership title="Slidely runtime ownership" caption="Routes call one Mastra runtime; nothing imports steps directly."/>
{/* static: no button, no packets */}<RuntimeOwnership interactive={false} />PackageBoundary
Section titled “PackageBoundary”A “what talks to what” ownership map. Green nodes are reuse-as-is; yellow nodes are split capabilities to consolidate by moving ownership. It carries a subtle ambient flow along the primary path by default (auto-hidden under reduced motion); pass animate={false} to freeze it.
Package boundary map
<PackageBoundary title="Monorepo boundaries" caption="Solid = primary calls, dashed = deps." />
{/* no ambient motion */}<PackageBoundary animate={false} />StageFlow
Section titled “StageFlow”A left-to-right pipeline of stages — phase plans, deploy flows, content pipelines. Pass stages as an array; geometry is computed from the count so it stays responsive. tone (core / good / warn / bad) colours a node; ambient motion is on by default. Used live on the Laravel CodeCanyon playbook.
Example deploy pipeline
<StageFlow title="Example deploy pipeline" stages={[ { label: 'Local', sub: 'dev + tests', tone: 'core' }, { label: 'CI', sub: 'lint · build · test' }, { label: 'Staging', sub: 'smoke-test' }, { label: 'Production', sub: 'human-gated', tone: 'good' }, ]}/>LayerStack
Section titled “LayerStack”A vertical stack of architecture tiers — what sits in each layer, with optional inline chips for sub-components. Generic, props-driven sibling to RuntimeOwnership/PackageBoundary. tone: 'core' emphasizes the authoritative layer; a downward connector + ambient dot imply flow top→bottom (both optional, reduced-motion-safe).
How a request is layered
<LayerStack title="How a request is layered" layers={[ { label: 'Presentation', sub: 'Astro + Starlight', items: ['pages', 'components'] }, { label: 'Runtime', sub: 'owns execution', tone: 'core', items: ['agents', 'tools'] }, { label: 'Data', sub: 'storage + memory', items: ['vectors', 'kv'] }, ]}/>StepStack
Section titled “StepStack”An ordered, vertical sequence where each step needs a sentence — procedures, how-tos, lifecycles. Renders a real semantic <ol> (free a11y, wraps cleanly, mobile-friendly). Distinct from StageFlow, which is horizontal and label-only. tone: 'core' highlights the pivotal step; pass start to begin numbering elsewhere.
Publish a doc
-
Draft
Write the .mdx under src/content/docs/<category>/.
-
Theme
Use tokens only — never hardcode a colour.
-
Verify
Run npm run build; links + Pagefind must pass.
<StepStack title="Publish a doc" steps={[ { title: 'Draft', body: 'Write the .mdx under src/content/docs/<category>/.' }, { title: 'Verify', body: 'Run npm run build; links must pass.', tone: 'core' }, ]}/>Timeline
Section titled “Timeline”A horizontal axis of phases over time with status. status drives colour and shape/label (never colour alone): done = muted solid, current = accent solid + pulse ring, future = dashed hollow. Maps to the status: current | target | future content model. Labels alternate above/below to avoid collisions; the pulse hides under reduced motion.
ZajLibrary rollout
<Timeline title="ZajLibrary rollout" phases={[ { label: 'P1 Foundation', sub: 'shipped', status: 'done' }, { label: 'P2 Directory', sub: 'in progress', status: 'current' }, { label: 'P3 AI chat', sub: 'backlog', status: 'future' }, ]}/>DecisionTree
Section titled “DecisionTree”A full branching map — print-friendly, zoomable, token-themed. Pass a graph object (rootId, nodes, edges, optional rule). Use instead of Mermaid when you need edge labels, outcome links, and the workbook look. Live on Operations workflows.
| Prop | Purpose |
|---|---|
graph | Required DecisionTreeGraph |
highlightPath | Node ids to emphasize (wizard uses this) |
dimUnhighlighted | Fade branches off the path |
activeNodeId | .is-active ring on the current step |
animatePath | Ambient dot along highlightPath (reduced-motion safe) |
compact, direction | Layout tuning (tb default, lr optional) |
Example deploy gate
import DecisionTree from '~/components/diagrams/DecisionTree.astro';import { opsWorkflowGraph } from '~/lib/graphs/ops-workflow-graph';
<DecisionTree graph={opsWorkflowGraph} title="Post-setup router" />DecisionTreeWizard
Section titled “DecisionTreeWizard”Same graph schema as DecisionTree, plus a click-through panel: breadcrumb, choices, recommendation card with CTA, optional persistKey for sessionStorage. The map above the panel dims off-path branches and highlights your route.
<DecisionTreeWizard graph={opsWorkflowGraph} persistKey="ops-wf" />ContentDecisionTree is a thin preset over DecisionTree with the content-IA graph — prefer DecisionTree directly when authoring new trees.
PatternPair
Section titled “PatternPair”Two side-by-side panels contrasting a bad/old approach with a good/new one — the highest-leverage place for a teaching visual (explanation, “why”, migration “don’t do X, do Y”). Content goes in the before / after slots (any MDX). The distinction is textual, never colour-only; collapses to one column on mobile.
Theming components
Avoid
Hardcoded colour in a component:
color: #ffffff;Breaks in light mode; invisible to the theme.
Prefer
A brand token from tokens.css:
color: var(--zaj-strong);Correct in light and dark, zero maintenance.
<PatternPair title="Theming components" beforeLabel="Avoid" afterLabel="Prefer"> <Fragment slot="before">Hardcoded `#fff` in a component.</Fragment> <Fragment slot="after">`var(--zaj-strong)` from tokens.css.</Fragment></PatternPair>DiagramFigure + the .zaj-diagram-* classes
Section titled “DiagramFigure + the .zaj-diagram-* classes”For a bespoke diagram, wrap your own inline <svg> in DiagramFigure and build it from the global primitives. Everything resolves to brand tokens, so it’s correct in light and dark with zero hardcoded hex.
Orthogonal connectors (90° elbows — required)
Section titled “Orthogonal connectors (90° elbows — required)”Flow arrows and dependency lines use Manhattan routing — horizontal and vertical segments only, meeting at right angles. This matches the editorial workbook look (think circuit / org-chart clarity, not diagonal spaghetti).
| Do | Don’t |
|---|---|
d="M x1 y1 L x1 y2 L x2 y2" elbow paths | diagonal L from corner to corner |
H / V shorthand in SVG paths | cubic-bezier C curves on connectors |
Mermaid with site default stepAfter curves | default bezier Mermaid edges (unless overridden) |
| Tree fan-out: trunk → shared bus → vertical drops | starburst diagonals from one parent to many children |
Shared bus pattern (decision trees, multi-branch splits):
<!-- trunk down, horizontal bus, vertical drops (arrows on drops only) --><path class="zaj-diagram-path" d="M 640 228 L 640 266" /><path class="zaj-diagram-path" d="M 165 266 L 1155 266" /><path class="zaj-diagram-path" marker-end="url(#arrow)" d="M 165 266 L 165 304" />Mermaid in .md inherits flowchart: { curve: 'stepAfter' } from astro.config.mjs. For a one-off override: %%{init: {'flowchart': {'curve': 'stepAfter'}}}%% at the top of the fence.
Arrowheads: import DiagramArrowDefs.astro — do not hand-size markers. Shared markers use markerWidth="2.2" (strokeWidth units) so tips stay proportional to the 3px connector.
Lane discipline & readability (object-avoiding routes)
Section titled “Lane discipline & readability (object-avoiding routes)”Orthogonal angles alone are not enough — connectors must read as a map, not spaghetti. Industry practice (orthogonal / Manhattan edge routing, yFiles, yWorks) boils down to:
| Rule | Target | Why |
|---|---|---|
| Object-avoiding | Lanes run in gutters ≥ 24px outside node bounding boxes | Lines never disappear under boxes |
| Face anchors | Exit/enter at midpoints of top/right/bottom/left — not corners | Corners collide and confuse direction |
| Lane stagger | Parallel edges offset ~18px on separate y (or x) values | Agents vs tools both leaving right no longer stack |
| Minimize crossings | Prefer separate gutters; fan-out uses a shared bus + vertical drops | Crossings only at lane junctions, not through nodes |
| Z-order | <g class="zaj-diagram-edges"> → labels → <g class="zaj-diagram-nodes"> → motion | Boxes always win; labels sit on a .zaj-diagram-annotation-bg pill |
| Label legibility | Callout text on a token-mapped background rect | Text stays readable over busy lanes |
SVG layer template:
<g class="zaj-diagram-edges" aria-hidden="true"> <!-- every <path class="zaj-diagram-path"> — each in its own lane --></g><g class="zaj-diagram-labels" aria-hidden="true"> <g transform="translate(x y)"> <rect class="zaj-diagram-annotation-bg" … /> <text class="zaj-diagram-annotation">…</text> </g></g><g class="zaj-diagram-nodes">…</g>Helpers: ~/lib/diagram-routing.ts exports anchor(), elbowPath(), and lane constants — use when a diagram has more than a handful of edges.
Pre-publish trace: mentally follow every path from arrowhead to arrowhead. If a segment passes through any <rect> interior, reroute via a gutter. If two paths share more than one segment, stagger or merge with a bus.
RuntimeOwnership is the reference implementation after the 2026-06 lane pass — copy its gutter pattern for hub-and-spoke layouts.
Minimal custom flow
import DiagramFigure from '~/components/diagrams/DiagramFigure.astro';
<DiagramFigure title="Minimal custom flow" caption="…"> <svg class="zaj-diagram" viewBox="0 0 620 140" role="img" aria-labelledby="t d"> <title id="t">…</title> <desc id="d">…</desc> <path class="zaj-diagram-path" d="…" /> <g class="zaj-diagram-node is-core" transform="translate(250 18)"> <rect rx="8" width="180" height="84" /> <text x="16" y="34">Runtime</text> <text x="16" y="56" class="sub">the authority</text> </g> </svg></DiagramFigure>Adding motion to a hand-authored SVG
Section titled “Adding motion to a hand-authored SVG”Ambient loop (no JS). Add a <g class="zaj-diagram-flow"> of dots driven by SMIL <animateMotion> — reference the geometry with the path= attribute. The .zaj-diagram-flow group is auto-hidden under prefers-reduced-motion: reduce, so the static diagram is the floor for everyone:
<g class="zaj-diagram-flow" aria-hidden="true"> <circle class="is-accent" r="6"> <animateMotion dur="2.4s" repeatCount="indefinite" path="M 184 118 L 360 118" /> </circle></g>Click-to-play (uses the bundled script). Opt a wrapper in with data-zaj-flow, add a [data-zaj-play] button + a [data-zaj-step] caption, and give each .zaj-diagram-packet a data-node (the data-node-id to highlight) and a data-label. The shared script (shipped by RuntimeOwnership.astro) chains the packets, highlights nodes, and falls back to a no-travel highlight under reduced motion. Easiest path: just use <RuntimeOwnership /> as the template.
Class cheatsheet
Section titled “Class cheatsheet”| Class | On | Variants |
|---|---|---|
.zaj-diagram | the <svg> | pair with a viewBox, never fixed px size |
.zaj-diagram-path | <path> connectors | .is-dashed, .is-bad |
.zaj-diagram-edges | <g> wrapping all connector paths | render before nodes |
.zaj-diagram-nodes | <g> wrapping node groups | keeps boxes above edges |
.zaj-diagram-annotation-bg | <rect> behind callout text | pair with .zaj-diagram-annotation |
.zaj-diagram-node | <g> wrapping <rect> + <text> | .is-core, .is-good, .is-warn, .is-bad |
.sub | secondary <text> in a node | — |
.zaj-diagram-annotation | callout <text> | — |
.zaj-diagram-legend | a <span> of <span> chips | .is-good, .is-warn, .is-core, .is-bad |
Related
Section titled “Related”- Site Authoring Capabilities — the full inventory.
- Writing Style & Quality Bar — voice + QC checklist.