11. The Layout Model
layout: | Engine | Arranges its children as | Wiring strategy | Lowers its subtree? |
|---|---|---|---|---|
flow (default) | 1D flex | boxes / text in a row or column | orthogonal router | no — arranges in place |
grid | 2D grid | boxes / text in tracks | orthogonal router | no — arranges in place |
stack | datum | every child’s origin on one datum (SPEC 12) | orthogonal router | no — arranges in place |
tree | rooted hierarchy | topics in generations (SPEC 12) | router (orthogonal / natural) | no — arranges in place |
sequence | time axis | participants + messages + frames + notes (SPEC 13) | time-rows → the straight strategy | yes |
chart | data plane | series + axes + bands + marks (SPEC 14) | layout-time data→pixels | yes |
pie | part-to-whole | slices (SPEC 14) | layout-time value→angle | yes |
drawing | datum / geometry | geometry + annotations + mates (SPEC 15) | layout-time dims / leaders | yes |
floorplan | the drawing engine, architectural dialect | walls + openings + fixtures + annotations (SPEC 15.11) | layout-time dims / leaders | yes |
schematic | circuit sheet | anchors on tracks + satellites at pins (SPEC 16) | orthogonal router, fixed ports | no — 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):
-
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. -
The wiring strategy realises a scope’s links.
flow/grid/tree— andschematic, whose wires land on fixed ports (SPEC 16.5) — hand their links to the router (SPEC 9, ROUTING.md);sequencefixes each message’s geometry (column x, row y) and hands it to thestraightstrategy; adrawing— and itsfloorplandialect (SPEC 15.11) — lowers each link to a dimension, leader, or mate (SPEC 15);chart/piehave no links. One scope, one strategy — set by the scope’slayout(withrouting:selectingorthogonal,natural, orstraightfor 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). Asequencemessage is thus the one place a link’s order is its geometry, not a routing problem. -
A layout-owning engine lowers to primitives in the layout phase.
flow/gridarrange their children where they sit.sequence/chart/pie/drawing(floorplanincluded) 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).