# Diagram

A flow or architecture figure, drawn out of the system’s own parts.

- Group: Display
- Import: `import { Diagram } from '@misoto22/folio'`
- Page: https://ui.misoto22.com/components/diagram/
- Related: card, figure-band

## When to reach for it

A picture of structure, in a page rather than in a terminal. Nesting is containment and an edge is a step between siblings — a diagram that needs arbitrary wiring wants a drawing, not this.

## Anatomy

- **Frame** (required) — The <figure role="group"> and the panel inside it: a --rule hairline on the --radius-lg corner over --paper-2, with fluid padding and overflow-x-auto on a hairline scrollbar — so a wide figure scrolls inside its own box instead of widening the page.
- **Leaf plate** — A node with NO children: a bordered card on --paper, or filled with --accent when accent is set. Its label breaks inside itself, because an identifier like TenantMainMiddleware has no break opportunity and would otherwise run into the plate’s edge.
- **Container band** — A node WITH children: a labelled hairline — ink at the top rank, --rule-2 below it — with its children underneath and no frame of its own. That is the whole design: drawing a container as another box puts three borders around anything two levels deep.
- **Node note** — node.note, a mono line beside a band’s label or under a plate’s. One short line, a step back from the name.
- **Edge mark** — The arrow between two adjacent siblings an edge names, with the edge’s label under it. aria-hidden, and rotated a quarter turn below the sm breakpoint, where a row of nodes stacks into a column and the arrow has to point the way the layout actually runs.
- **Caption** — spec.caption, printed under the frame — as a <div>, not a <figcaption>, because an article stylesheet styles figcaption unlayered and an unlayered rule beats a utility whatever the specificity.

## Best practices

### Do

- Write an edge from a node to the node immediately after it in the same rank, in that order. An edge between non-adjacent nodes, or one written to→from, draws no arrow — and now says so in the console rather than leaving the author to spot a missing arrow in a picture that otherwise looks finished.
- Keep ids unique across the whole spec. Each edge is now spent at the first pair that matches it, so a reused pair no longer draws the arrow twice — but the arrow lands on whichever pair comes first, which is a diagram asserting something nobody wrote.
- Give the spec a caption or a label. The figure’s role="group" is named by whichever is present, and with neither the reader is told there is a group and never told what of.

### Don’t

- accent is read only in the plate branch, so setting it on a node with children compiles, type-checks and paints nothing — a container is a band, and a band has no fill to take. Development says so; a production build does not.
- direction is read only from a node that HAS children. Set on a leaf it is ignored, because the axis a leaf sits on belongs to its parent — and, like accent on a band, it is reported in development rather than silently dropped.
- Do not put six nodes across the top rank. A row is flex-col below sm and only flex-row above it, so a figure that reads as a pipeline on a desktop is six stacked plates and five arrows on a phone.

## Accessibility

- A <figure> with role="group", named by its caption, so the whole picture is one thing a reader can skip.
- Arrows are aria-hidden: assistive tech reads the nodes in document order and has no use for a glyph pointing at the next one.
- Server-rendered markup, not a canvas — every label is real text a screen reader and a search engine can read.
- A spec the renderer cannot honour — an unmatched edge, a duplicate id, accent on a band, direction on a leaf — prints a named warning in development, because the alternative is a confident picture of something else.

## Diagram

A flow or architecture diagram, drawn out of the system's own parts. The alternative it replaces is a fenced block of box-drawing characters — `┌──────┬──────┐` — which is a picture rendered in a font chosen for code. It inherits the code block's frame and scrollbar, so a diagram reads as terminal output; it cannot wrap, so on a phone it either overflows or is scaled to nothing; the box edges are text, so a screen reader reads the rules out loud; and none of it responds to the theme. The other alternative is a diagramming library, which is several hundred kilobytes of layout engine, renders after hydration, and draws in its own palette. This is neither: hairline frames on the radius scale, mono labels, the muted step for anything supporting — so a diagram belongs to the page it sits on. It server-renders, because it is markup. NESTING IS CONTAINMENT, which is what most architecture diagrams actually describe: this is inside that, and these two sit beside each other. Edges are for the sequence between siblings, not for arbitrary wiring — a diagram that needs arbitrary wiring is a diagram that wants a drawing, and this will not pretend otherwise. It takes a spec rather than markup, so a fenced ```diagram block in an article and a hand-written figure on a page are one renderer and one look — and a wrong diagram is corrected by editing data.

### Props

- `spec` (required) — `DiagramSpec`.
- `className` — `string`.

## Example — default

```tsx
import { Diagram } from '@misoto22/folio'

<Diagram
  spec={{
    caption: 'One request, from the edge to the row it reads.',
    edges: [
      { from: 'edge', to: 'app', label: 'HTTPS' },
      { from: 'app', to: 'data', label: 'SQL' },
    ],
    nodes: [
      { id: 'edge', label: 'Edge', note: 'CDN + WAF', footnote: 'Cached for 60s' },
      {
        id: 'app',
        label: 'Application',
        note: 'one process',
        direction: 'column',
        children: [
          { label: 'Router' },
          { label: 'Handlers', accent: true, note: 'where the work is' },
          { label: 'Serialisers' },
        ],
      },
      { id: 'data', label: 'Postgres', note: 'primary' },
    ],
  }}
/>
```

## Example — containment not wiring

```tsx
import { Diagram } from '@misoto22/folio'

<Diagram
  spec={{
    direction: 'column',
    caption: 'What the package contains, and who contains the package.',
    nodes: [
      {
        label: 'Consumers',
        note: 'two hosts',
        children: [
          { label: 'misoto22.com', note: 'the public site' },
          { label: 'Admin console', note: 'behind a login' },
        ],
      },
      {
        label: '@misoto22/folio',
        note: 'one package',
        direction: 'column',
        children: [
          {
            label: 'Components',
            note: 'React, server-rendered',
            children: [
              { label: 'Display' },
              { label: 'Forms' },
              { label: 'Overlays' },
            ],
          },
          { label: 'Tokens', note: 'CSS and TypeScript, one source' },
        ],
      },
    ],
  }}
/>
```

## Example — a column of ranks

```tsx
import { Diagram } from '@misoto22/folio'

<Diagram
  spec={{
    direction: 'column',
    caption: 'A search query, top to bottom.',
    edges: [
      { from: 'query', to: 'embed', label: 'text' },
      { from: 'embed', to: 'search', label: '1024-d vector' },
    ],
    nodes: [
      { id: 'query', label: 'Query', note: 'typed by a reader' },
      { id: 'embed', label: 'Embedding', note: 'Voyage 3.5-lite' },
      {
        id: 'search',
        label: 'pgvector',
        note: 'top 5 by cosine',
        direction: 'row',
        children: [
          { label: 'Posts' },
          { label: 'Projects' },
          { label: 'Profile' },
        ],
      },
    ],
  }}
/>
```

## Example — the node it is about

```tsx
import { Diagram } from '@misoto22/folio'

<Diagram
  spec={{
    label: 'Where an answer is assembled',
    edges: [
      { from: 'retrieval', to: 'model', label: 'context' },
      { from: 'model', to: 'answer', label: 'tokens' },
    ],
    nodes: [
      { id: 'retrieval', label: 'Retrieval', note: 'five chunks' },
      {
        id: 'model',
        label: 'Model',
        note: 'streamed',
        accent: true,
        footnote: 'The only step that leaves the machine',
      },
      { id: 'answer', label: 'Answer', note: 'with citations' },
    ],
  }}
/>
```
