# Pattern // diagram-static-SEQ

A reusable static-artifact scaffold for diagrams whose natural topology is an **ordered, top-to-bottom sequence of steps joined by arrows** — pipelines, workflows, lifecycles, doctrine chains, and similar ASK-family process diagrams that inherit the [design-system-ASK](../../README.md) visual language. **`diagram-static-SEQ`** is the **sequence** Class A static pattern: succession, not hierarchy.

This is a **Class A static** diagram scaffold (system / architecture diagram templates), distinct from the **Class B** project-output artifact pattern. It sits alongside `patterns/diagram-static-H/` (horizontal left→right cascade), `patterns/diagram-static-V/` (vertical centered spine), and `patterns/diagram-static-FLOW/` (convergence flow — many sources converging into a resolved spec, realized, evaluated, governed, fed back). H / V / SEQ share the existing tree/sequence data grammar and export contract. FLOW is a distinct convergence-flow grammar (one shared source model, two render modes), but shares the same visual contract, PNG export discipline, and inheritance-by-reference posture.

> **Provenance.** Graduated from an asset-pipeline-ASK reference implementation (handoff `2026-06-11_asset-pipeline-ASK_to_design-system-ASK_sequence-scaffold-handoff`). This copy is canonical; the AP scratch original is a reference, not a source of truth. Consumers — AP included — vendor this copy by reference.

## When to use this vs `diagram-static-H` / `diagram-static-V`

- **`diagram-static-H`** — horizontal *tree*: parent→child hierarchy expanding left → right.
- **`diagram-static-V`** — vertical *tree*: parent→child hierarchy as a centered top→down spine.
- **`diagram-static-SEQ`** (this pattern) — vertical *sequence*: ordered steps joined by **arrows**, left-aligned. The relation between boxes is **succession** (step 2 follows step 1), not hierarchy (step 2 is not a child of step 1).

The trees render relationship; the sequence renders direction. A numbered process forced into a tree scaffold reads as a hierarchy it isn't — that is exactly the misfit this pattern exists to remove. Pick the one whose geometry matches the diagram; do not fork one to fake the other.

## What this pattern is

A small consumption pattern. Eight files:

- `README.md` — this file
- `diagram-static-SEQ.html` — the shell page (header, canvas, legend, HUD, caption)
- `diagram-static-SEQ.source.js` — the sequence data, expressed as a single `window.TREE_DIAGRAM` literal (a single-child chain)
- `diagrams-static-SEQ-engine.js` — **the sequence placement + pan/zoom engine** (the only file that genuinely differs from the H / V siblings). It measures against the actual Inter / JetBrains Mono fonts, waiting for them to load first, and adds the CSS `letter-spacing` that `canvas.measureText` ignores, so labels never overflow their boxes. **If you change a `letter-spacing` value in `diagrams.css`, update the matching `LS_*` constant in the engine.**
- `diagrams-fit.js` — **DS-owned shared fit support.** Computes the default zoom-to-fit transform: it measures the *visible* caption / legend and HUD glass panels and, **only when the figure would actually collide with one**, centres it in the edge-safe region that remains. A placement that already clears the chrome is kept exactly as-is — reservation is overlap-gated, so a figure is never shrunk to avoid chrome it does not reach, so a wide, short figure no longer renders its top band underneath the corner panels. Panel heights are measured live, never hard-coded, and hidden or zero-area panels reserve nothing. **Load it immediately BEFORE the engine** — the engine throws a named error if it is missing rather than silently falling back to the old geometry. **Byte-identical to the copies in the sibling patterns** — shared by convention, not a runtime import; re-vendor it alongside the engine. With no visible panels the prior fit formula is preserved exactly while each available axis is at least twice its requested total clearance. On a more constrained axis (a canvas smaller than twice its clearance) the clearance degrades proportionally and consumes at most half the available space. Within a fixed available rectangle and panel-reservation state, reducing that axis cannot increase its clearance-limited scale contribution — so the Fit stays on-canvas and positive where the old absolute-clearance model produced a non-positive or direction-reversing result.
- `diagrams-text-layout.js` — **DS-owned shared text-layout support, a GENERATED MIRROR.** Canonical source is `patterns/_diagram-shared/diagrams-text-layout.js`; this copy is emitted by `tools/sync-diagram-shared.mjs` and is **byte-identical** to it. It owns line breaking (delimiters matched longest-first, `//` before `/`, breaking after the delimiter; force-break only a token that still exceeds its cap alone; an authored newline is a hard break), measurement, **role metrics — per-role cap, line height and the has-note predicate** — and tspan emission. This engine keeps **no** cap table, **no** line-height table and **no** rendered-secondary predicate of its own: it requests layout by role and receives the resolved metrics, because three engines each holding a private copy is the divergence the shared contract exists to remove — the engine keeps source grammar, topology, placement, connector geometry and the final SVG envelope, because H, V and SEQ have genuinely different geometry contracts. **Load it immediately BEFORE the engine**, after `diagrams-fit.js`; the engine throws a named error if it is missing or its interface is incomplete, rather than silently falling back. **Never hand-edit this file** — `node tools/sync-diagram-shared.mjs --check` exits non-zero on any divergence, so an edit here fails loudly instead of surviving as a silent fork. Its declared target set is H, V and SEQ; `diagram-static-FLOW` and `diagram-interactive-spine` are explicitly excluded.
- `diagrams.css` — diagram-specific style layer (page chrome + SVG nodes/edges) plus two diagram-only token additions (`--node-fill`, `--line-strong`) and the `--diagram-*` legibility tokens; inherits Tier 1 + Tier 2 from the local `colors_and_type.css` mirror. **Byte-identical to the `diagram-static-H` / `diagram-static-V` copies** — shared by convention, not a runtime import. The sequence engine adds exactly one class to the shared vocabulary: `.edge-arrowhead`.
- `export-png.js` — 3840×2880 PNG export with header, caveat, legend, and the rendered diagram. **Byte-identical to the `diagram-static-H` / `diagram-static-V` copies** — it is geometry-agnostic (it serializes the rendered SVG and scales it into the export frame), so the same export serves all three patterns. **Exported chrome resolves the semantic diagram roles first** — `--diagram-ink` then `--fg-1`, `--diagram-muted` then `--fg-2`. `diagrams.css` aliases the semantic roles to the foundation ramp by default, so ordinary consumers export exactly as before; a consumer that legitimately rebinds those roles for its own surface now gets the same treatment in the export that it already gets live, instead of a corrected page and an uncorrected raster.

**Load order is a contract, not a convention.** The page loads
`<source> → diagrams-fit.js → diagrams-text-layout.js → engine → fonts-embedded.js → export-png.js`.
Both support carriers fail **closed**: an engine loaded without either throws a named error and
renders nothing, so a partial re-vendor is visible immediately rather than looking current while
carrying old geometry. The text-layout check tests the **role-aware interface and the declared target set**,
not just that the global exists — a mirror can be complete, load cleanly, and still be the wrong
member (vendored from a sibling plane, or from a later version that dropped this pattern), and
that case passes a truthiness test and fails nowhere.

**What not to edit here.** `diagrams-text-layout.js` is a generated mirror — edit the canonical in
`patterns/_diagram-shared/` and re-emit. `diagrams-fit.js` and `diagrams.css` are shared by
convention and are byte-identical across the sibling patterns; re-vendor them alongside the engine
rather than diverging one copy. A consumer receipt resolves a vendored mirror's bytes to the
canonical plus the owner commit it was emitted from.

**Zoom floor tracks Fit.** The ordinary zoom-out floor is this pattern's historical base scale, but the panel-aware fit can legitimately land below it on a constrained viewport. When it does, the live floor becomes the fitted scale, so zoom-out is a no-op at Fit rather than *increasing* the scale (which would reverse the control's direction). Fit itself is never clamped — clamping it would restore the panel collision the helper exists to avoid.

The pattern is **not** a component library, a generator, a build pipeline, an npm package, or a project-specific diagram. It is a starting point for downstream diagrams that consume design-system-ASK foundations.

## Placement model

The engine is `diagrams-static-V-engine.js` with exactly three geometry changes:

1. **Standalone lead box** — the root/title box stands on its own with a larger gap and **no connector** to the first step. A sequence's title is not the parent of its steps.
2. **Arrowed sequence connectors** — consecutive steps are joined by a vertical line ending in an **arrowhead**, encoding direction/succession (vs. the trees' plain parent→child edges). Arrowheads are explicit `<path class="edge-arrowhead">` triangles, **not** SVG `marker` elements — markers rasterize unreliably through the `data:image/svg+xml → <img> → canvas` PNG path, so explicit triangles keep the Class A export faithful.
3. **Left-aligned geometry** — all boxes share one left edge; label + note text inside each box is left-aligned (`text-anchor: start`); the connector runs at a small indent (`EDGE_INDENT`) from the shared left edge, not down a horizontal center.

Everything else — public contract (`window.DIAGRAMS.render(TREE)`), data grammar (`{ kind, label, note?, tag?, status?, children? }`, with `status: 'earned'|'held'|'legacy'` honored on step boxes and connectors), CSS classes, PNG export, font-load gate, pan/zoom — is identical to the siblings.

The engine **flattens the node tree depth-first into a linear run**, so the same `*.source.js` data renders as a centered spine in `diagram-static-V` or as a sequence here. Branching input warns to console and falls back to depth-first order — trees belong to the H / V scaffolds.

## How to use it

1. Copy the **complete eight-file bundle** in `patterns/diagram-static-SEQ/` into your consuming project (typically under `docs/diagrams/` or similar). **All eight, including `diagrams-text-layout.js`** — the engine fails closed on a missing support carrier, so a seven-file copy renders nothing and names the file it wants.
2. Sync `colors_and_type.css`, fonts, and any required project-approved assets from design-system-ASK into a local mirror alongside the diagram bundle (for example `./_dsa-tokens/colors_and_type.css`, `./_dsa-tokens/fonts/*.woff2`, and `./_dsa-tokens/fonts-embedded.js` — the embedded-font carrier that lets `PNG page` / `PNG diagram` export offline from a `file://` page, no server), pinned to a known upstream commit SHA. The HTML expects `./_dsa-tokens/colors_and_type.css`; adjust the path if your mirror lives elsewhere.
3. Rename `diagram-static-SEQ.html` and `diagram-static-SEQ.source.js` to match your project (e.g. `[your-project]_pipeline-chain.html` and `[your-project]_pipeline-chain.source.js`); update the `<script src>` reference in the HTML accordingly.
4. Edit `diagram-static-SEQ.source.js`: replace the placeholder chain with your project's actual sequence, expressed as a single-child chain under a `root` node.
5. Edit `diagram-static-SEQ.html` chrome: update `.mark`, `.title-block`, `.stamp`, `.caption`, `<title>`, and `<meta name="description">` to your project's values. Do not edit the canvas / HUD / corner-tick structure.
6. Open the resulting HTML directly in a browser, or via static hosting / GitHub Pages. Use the `PNG page` button in the HUD to export a 3840×2880 chromed render at the current resolved theme, or `PNG diagram` for the canvas-only export (see PNG export below).

## PNG export

### The two exports

The HUD exposes two PNG exports, both rasterizing the live render through one path (`exportPng({ mode })`):

- **`PNG page`** — the chromed poster: a 3840×2880 render with header, legend, caption, and ticks on the resolved gradient field. Auto-export route `?export=png`; filename carries the theme (see Artifact naming below). The legend is reproduced from the live DOM as an ordered list of rows, dividers, and group headings, with each swatch drawn from its **computed** presentation (fill, border color / width / style, radius) — so a consumer's semantic legend (colored role swatches, a downstream-group separator, a group heading) exports faithfully; a plain row-only legend lays out exactly as before.
- **`PNG diagram`** — the diagram canvas only: no header, HUD, caption, legend, or ticks, on the resolved gradient field, at the diagram's natural (variable) aspect. Auto-export route `?export=png-diagram`; filename `<slug>-diagram-<theme>.png`.

The diagram bounds are the **engine-authored SVG `width` / `height` / `viewBox`**, so return loops, arrowheads, edge labels, and landscape/portrait geometry are preserved without clipping. `PNG diagram` replaces the old chrome-free `.clean.html` + Puppeteer two-build workaround — a clean diagram PNG now comes straight from the full diagram HTML.

### Artifact naming

The `PNG page` button exports at the page's resolved theme, and the **exported filename carries that theme** — `<base>_source-vN_render-vN-light.png` or `…-dark.png` (theme resolved by the same precedence as the CSS: an explicit `data-theme` on `<html>` wins, otherwise the OS `prefers-color-scheme`). The suffix keeps a light and a dark export of the same diagram from colliding in Downloads, scratch, review folders, or handoff contexts.

That suffix is a property of **raw exporter output**, not of repo-committed artifacts:

- A repo-committed **canonical raster** — a single chosen representative image, e.g. `your-project_<diagram>.png` — **may keep a semantic, unsuffixed filename**. In repo context the filename already says what the image is, and only one variant is committed.
- If a repo commits **both** the light and dark variants, the `-light` / `-dark` suffixes are **required** to tell them apart.

Example: `asset-pipeline-ASK_discretion-chain.png` is a valid single chosen canonical (dark) raster; if both variants were ever committed they would be named `…-light.png` / `…-dark.png`.

### Theme by embedding surface

Every diagram package generates and retains both theme variants (`-light` and `-dark`). The embedding surface selects the default:

- **Repository documentation and operator-system diagrams default to dark.**
- **Substack and other published long-form editorial diagrams default to light.**
- A stated local exception may override the default for a specific figure.

This rule selects which existing render is embedded. It does not suppress, rename, or replace the alternate-theme export.

## What downstream must replace

- All sequence data in `diagram-static-SEQ.source.js` (root label, step labels, notes, tags, statuses)
- All chrome strings in `diagram-static-SEQ.html`: `.mark`, `.title-block .t`, `.title-block .s`, `.stamp`, `.caption`, legend rows
- The `<title>` and `<meta name="description">` tags
- The file names (`diagram-static-SEQ.html` and `diagram-static-SEQ.source.js`) if you prefer project-specific names

## What not to edit

- `diagrams-static-SEQ-engine.js`, `diagrams.css`, `export-png.js`, `diagrams-fit.js` (the shared engine + style + export — modifications break inheritance)
- The script load order in the shell — `diagrams-fit.js` must load before the engine; the engine hard-fails if it is absent
- The diagram-only token overlays (`--node-fill`, `--line-strong`) and Tier 1 + Tier 2 references inside `diagrams.css`
- The canvas / HUD / corner-tick / glass-panel structure in the HTML
- The light / dark theme model: explicit `data-theme="dark"`, explicit `data-theme="light"`, and `prefers-color-scheme` auto-resolve all need to keep working

## Static vs dynamic inheritance

Static diagram artifacts inherit at generation time and freeze for audit. The Tier 1 + Tier 2 tokens, type families, theme bridges, and font files come from the local mirror (`./_dsa-tokens/colors_and_type.css` plus `./_dsa-tokens/fonts/`). `diagrams.css` in this bundle is the diagram-specific compiled style layer and is not a substitute for the upstream tokens. There is no runtime fetch from the design-system-ASK repo and no Google Fonts CDN dependency. Re-sync the local mirror when upstream tokens or fonts change; bump the `source-vN` / `render-vN` stamp in the HTML each time.

Production code, by contrast, may use a live dependency or import model.

## Source-truth boundary

design-system-ASK supplies the foundations (Tier 1 + Tier 2). The diagram is **illustrative**, not source truth — the consuming repo's prose (`README.md`, `docs/architecture.md`, doctrine files, etc.) remains authoritative. If the diagram and the repo prose diverge, trust the prose and refresh the diagram. Do not modify the repo prose to match a stale diagram.

The consuming project supplies its own Tier 3 identity, its own source-truth posture, and the diagram's content and structure. Hosting this scaffold in design-system-ASK does not make this repo the owner of downstream diagram content.

## Class A vs Class B

This is Class A (system / architecture diagram templates), alongside `patterns/diagram-static-H/`, `patterns/diagram-static-V/`, and `patterns/diagram-static-FLOW/`, plus the interactive `patterns/diagram-interactive-spine/`. Class B (project-output artifact templates) is a separate pattern at `patterns/output-artifact/`. The classes stay distinct; do not fuse. All inherit Tier 1 + Tier 2 from design-system-ASK, but they serve different artifact classes.
