Skip to content
prod e051e98
Browse

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 forNot
Tiers / “how it’s layered”LayerStacka flat list
A step-by-step procedureStepStacka wall of prose
A horizontal pipeline (label-only)StageFlowStepStack (that’s vertical + text)
Phases over time / a roadmapTimelinea date list
”Don’t do X, do Y” / before↔afterPatternPairtwo separate paragraphs
Ownership / “what talks to what”PackageBoundary, RuntimeOwnership
Branching decisions / “which path?”DecisionTree, DecisionTreeWizardbare Mermaid when you need edge labels + outcomes
Scannable hub of linksStarlight <CardGrid> / <LinkCard>a bespoke SVG
An arbitrary relationship grapha themed Mermaid grapha hand-rolled SVG

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 />

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

Runtime ownership diagram Server routes call a single runtime. The runtime coordinates workflows, agents, tools, storage, and observability. A red dashed line shows the anti-pattern where routes bypass the runtime and import step functions directly. User Prompt editor · chat · live cards Server Route auth · workspace · credits Runtime Registry brain: knows every part runManager commands Workflows steps · checkpoints Agents orchestrator + workers Tools search · image · validate Storage + DB snapshots · events · ledger Observability Langfuse · logs · cost
Server routes call a single runtime registry (the "brain"), which coordinates workflows, agents, tools, storage, and observability. The red dashed line is the anti-pattern: a route reaching past the runtime to import a step or tool directly. Runtime authority Anti-pattern — bypasses the runtime
{/* 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} />

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

Package boundary map The web app calls server API routes, which call the LLM platform. The LLM platform uses shared contracts, database, blocks, surfaces, export, and observability packages or split locations. apps/web editor · hooks · UI present server/api auth · stream · run control present llm-platform runtime authority workflows · agents · tools shared schemas · contracts db package + app schema observability split today blocks schemas + registry renderers in web exports existing package rename candidate surfaces vocab + web shells split today
Top row is product/runtime flow; the lower clusters are supporting packages. Solid lines are primary calls, dashed lines are dependencies. Yellow nodes are "present but split" — consolidate by moving ownership, never by adding a new package. Reuse — path exists Split — consolidate ownership Authority
<PackageBoundary title="Monorepo boundaries" caption="Solid = primary calls, dashed = deps." />
{/* no ambient motion */}
<PackageBoundary animate={false} />

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

Example deploy pipeline 1. Local (dev + tests) → 2. CI (lint · build · test) → 3. Staging (smoke-test) → 4. Production (human-gated) Local dev + tests CI lint · build · test Staging smoke-test Production human-gated
Each stage gates the next.
<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' },
]}
/>

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

How a request is layered Layer 1: Presentation (Astro + Starlight) — pages, components; Layer 2: Runtime (owns execution) — agents, tools, workflows; Layer 3: Data (storage + memory) — vectors, kv, blobs Presentation Astro + Starlight pages components Runtime owns execution agents tools workflows Data storage + memory vectors kv blobs
Each tier owns its concern; the runtime is the execution authority.
<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'] },
]}
/>

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

  1. Draft

    Write the .mdx under src/content/docs/<category>/.

  2. Theme

    Use tokens only — never hardcode a colour.

  3. Verify

    Run npm run build; links + Pagefind must pass.

The build gate is non-negotiable — it's where wiring breaks surface.
<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' },
]}
/>

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

ZajLibrary rollout Phase 1 (done): P1 Foundation — shipped; Phase 2 (current): P2 Directory — in progress; Phase 3 (future): P3 AI chat — backlog DONE P1 Foundation shipped NOW P2 Directory in progress NEXT P3 AI chat backlog
P2 is in flight; P3 is backlog.
<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' },
]}
/>

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.

PropPurpose
graphRequired DecisionTreeGraph
highlightPathNode ids to emphasize (wizard uses this)
dimUnhighlightedFade branches off the path
activeNodeId.is-active ring on the current step
animatePathAmbient dot along highlightPath (reduced-motion safe)
compact, directionLayout tuning (tb default, lr optional)

Example deploy gate

Example deploy gate Decision tree with 4 steps and 2 outcomes: Staging first, Hotfix path. Deploy today? code is merged Destructive migration? Staging first WF-SHIP Open guide → Hotfix path WF-HOTFIX Open guide → When in doubt, staging first.
Static map — good for reference and print.
import DecisionTree from '~/components/diagrams/DecisionTree.astro';
import { opsWorkflowGraph } from '~/lib/graphs/ops-workflow-graph';
<DecisionTree graph={opsWorkflowGraph} title="Post-setup router" />

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.

Path on the map

Path on the map Decision tree with 4 steps and 2 outcomes: Staging first, Hotfix path. Deploy today? code is merged Destructive migration? Staging first WF-SHIP Open guide → Hotfix path WF-HOTFIX Open guide → When in doubt, staging first.
<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.

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.

Tokens flip under [data-theme]; hardcoded hex doesn't.
<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).

DoDon’t
d="M x1 y1 L x1 y2 L x2 y2" elbow pathsdiagonal L from corner to corner
H / V shorthand in SVG pathscubic-bezier C curves on connectors
Mermaid with site default stepAfter curvesdefault bezier Mermaid edges (unless overridden)
Tree fan-out: trunk → shared bus → vertical dropsstarburst diagonals from one parent to many children

Shared bus pattern (decision trees, multi-branch splits):

inline SVG
<!-- 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:

RuleTargetWhy
Object-avoidingLanes run in gutters ≥ 24px outside node bounding boxesLines never disappear under boxes
Face anchorsExit/enter at midpoints of top/right/bottom/left — not cornersCorners collide and confuse direction
Lane staggerParallel edges offset ~18px on separate y (or x) valuesAgents vs tools both leaving right no longer stack
Minimize crossingsPrefer separate gutters; fan-out uses a shared bus + vertical dropsCrossings only at lane junctions, not through nodes
Z-order<g class="zaj-diagram-edges"> → labels → <g class="zaj-diagram-nodes"> → motionBoxes always win; labels sit on a .zaj-diagram-annotation-bg pill
Label legibilityCallout text on a token-mapped background rectText stays readable over busy lanes

SVG layer template:

inline SVG
<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

Request to runtime to store flowA client calls the runtime authority, which writes to storage.Clientbrowser · APIRuntimethe authorityStoragesnapshots
Built by hand from .zaj-diagram-* classes — the same primitives the components use.
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>

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.

ClassOnVariants
.zaj-diagramthe <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 pathsrender before nodes
.zaj-diagram-nodes<g> wrapping node groupskeeps boxes above edges
.zaj-diagram-annotation-bg<rect> behind callout textpair with .zaj-diagram-annotation
.zaj-diagram-node<g> wrapping <rect> + <text>.is-core, .is-good, .is-warn, .is-bad
.subsecondary <text> in a node
.zaj-diagram-annotationcallout <text>
.zaj-diagram-legenda <span> of <span> chips.is-good, .is-warn, .is-core, .is-bad