11. The Layout Model

layout:EngineArranges its children asWiring strategyLowers its subtree?
flow (default)1D flexboxes / text in a row or columnorthogonal routerno — arranges in place
grid2D gridboxes / text in tracksorthogonal routerno — arranges in place
stackdatumevery child’s origin on one datum (SPEC 12)orthogonal routerno — arranges in place
treerooted hierarchytopics in generations (SPEC 12)router (orthogonal / natural)no — arranges in place
sequencetime axisparticipants + messages + frames + notes (SPEC 13)time-rows → the straight strategyyes
chartdata planeseries + axes + bands + marks (SPEC 14)layout-time data→pixelsyes
piepart-to-wholeslices (SPEC 14)layout-time value→angleyes
drawingdatum / geometrygeometry + annotations + mates (SPEC 15)layout-time dims / leadersyes
floorplanthe drawing engine, architectural dialectwalls + openings + fixtures + annotations (SPEC 15.11)layout-time dims / leadersyes
schematiccircuit sheetanchors on tracks + satellites at pins (SPEC 16)orthogonal router, fixed portsno — arranges in place

Defaults. Every container — the root included — defaults to layout: flow with direction: row and gap: 36 — source order flows the way it reads, so cat -> dog -> bird runs left to right (a closed shape’s or a |topic|’s children are card content, not arranged nodes, so those stack instead — direction: column, gap: 12 — and an icon sits over its label; |block|, |group|, and the frameless wrappers are containers and keep the flow pair); padding defaults per SPEC 10.5, the root’s framing the whole rendered scene — links and labels included — out to the SVG edge.

Three seams every engine plugs into

The engines differ, but three contracts are shared — which is why a new layout is a small, bounded addition (Part III formalises each):

  1. The smart label extends. The one label rule (SPEC 3) — each type places its "X" — is inherited by every layout (title, legend, axis title, header, guard; SPEC 13, SPEC 14). No layout invents a label syntax.

  2. The wiring strategy realises a scope’s links. flow / grid / tree — and schematic, whose wires land on fixed ports (SPEC 16.5) — hand their links to the router (SPEC 9, ROUTING.md); sequence fixes each message’s geometry (column x, row y) and hands it to the straight strategy; a drawing — and its floorplan dialect (SPEC 15.11) — lowers each link to a dimension, leader, or mate (SPEC 15); chart / pie have no links. One scope, one strategy — set by the scope’s layout (with routing: selecting orthogonal, natural, or straight for the routed ones), and it governs that scope’s own links only — an ordinary |row| / |grid| nested inside a sequence or drawing still hands its internal links to the router (a schematic is the one scope whose link laws also reach links written in its nested ordinary containers — placement never cascades, SPEC 16). A sequence message is thus the one place a link’s order is its geometry, not a routing problem.

  3. A layout-owning engine lowers to primitives in the layout phase. flow / grid arrange their children where they sit. sequence / chart / pie / drawing (floorplan included) instead read their whole subtree and emit an ordinary primitive tree — |block|s, |line|s, |path|s, text — at baked coordinates (SPEC 19). So the cascade, palette, theming, gradients, --static, fmt, and determinism all apply to a chart, a sequence, or a drawing with no engine-specific render code — a chart is a diagram once lowered.

The container is still a box. An engine owns where its children go, but the container node itself is an ordinary box: its own fill, stroke, stroke-width, radius, opacity, shadow, rotate, and href paint in every layout — a chart, a sequence, or a pie can carry a background, a frame, or a link like any |box|.

Universal container properties

The container property set — which engine honours which is SPEC 17’s matrix, the authoritative table.

gap is honoured everywhere but means what the engine needs: inter-child spacing in flow / grid, generation distance × sibling separation in a tree (SPEC 12), the plot-to-title/legend gutter in a chart / pie (default 15), and the message pitch / participant spacing in a sequence (default 32); a drawing places by datum and ignores it (its mates read a scoped gap: of their own — SPEC 15.5). direction, align, justify, and gap-fill are the flow / grid arranger’s knobs — a stack, sequence, chart / pie, or drawing container places its own children and ignores them, and a tree reads direction and gap alone. padding is honoured wherever it has meaning (the matrix, SPEC 17).

Nested boxes are unaffected. These knobs govern a container engine’s placement of its own children; an ordinary box nested inside any layout still lays out its own content by the box model. So a participant box in a sequence — an ordinary box — honours padding, align, justify, and gap-fill on its own content, even though the sequence engine placed the participant on the time axis. (A chart / pie consumes its children into marks, so this case does not arise there — SPEC 14.)

gap-fill (default none) fills a flow’s or grid’s interior gutters — the gap regions between children — with a colour, thickness = the gap (gap: 1; gap-fill: --stroke paints hairline rules). Per-axis gap picks which rules show (gap: 1 0 row rules, 0 1 column). Gutters are interior only — the outer frame is the container’s own stroke, never doubled — and span-aware in a grid (skipping pinned and spanning cells). This is what makes |table| plain grid + gap: 1 + gap-fill: --stroke, not a magic type (SPEC 8).