Status: normative implementation contract. See the linked app reference for current API guidance.
Grid layout contract
3. The facts channel
Cell/row metadata rides Flex.grid: GridFacts { is_row, row_valign, col_span, align }
(day-core tree.rs), the shipped form of §7.2’s ChildRef facts surface, read via
LayoutOps::flex_of and inert to every other layout. grid_row builds a layout-only node
with is_row set, carrying a horizontal StackLayout that only runs when the row is not
inside a grid; a stray grid_row degrades to a plain row. .grid_span/.grid_align build
their piece and merge facts onto its root node (TreeOps::set_grid_facts): no wrapper node.
Position-free. Grid facts are outer-layout facts (docs/api-style.md
“Modifier classes”): they describe the piece to the grid it sits in, so they land on the
outermost node whatever wrappers follow them. label(res::str::sample_label()).padding(4.0).grid_span(2) and
label(res::str::sample_label()).grid_span(2).padding(4.0) are the same cell.
4. Layout algorithm (normative)
One geometry() function serves both measure and place:
- Collect: flatten group anchors; classify children into rows (
is_row) and full-width cells; assign column indices from spans. - Pass A (ideals): measure every span-1 cell unconstrained; rigid cells set their
column’s ideal (max),
grow_wcells flag their column flexible (their ideal is recorded only when the grid itself is width-unconstrained). Spanning rigid cells then distribute any deficit (once, closed-form). Full-width rigid cells record the grid-wide ideal; a full-widthgrow_wcell makes the grid width-flexible. - Resolve columns: with a width proposal and flexible columns, leftover = proposal − rigid ideals − gutters, split evenly among the flexible columns; otherwise every column takes its ideal. Grid width = the proposal when anything is flexible, else max(columns+gutters, full-width ideal).
- Pass B (heights): measure every cell once at its final span width (
Proposal::new(Some(w), None)); row height = max cell height;grow_hrows stretch additively under a height proposal (stretching never re-measures). - Place: re-run
geometryatProposal::exact(bounds); every per-cell proposal is bit-identical to the measure pass (onlyp.widthaffects cell proposals), so placement measures purely from the engine’s cache and positions each cell by the resolved alignment. Row nodes are transparent carriers and are never placed.
5. The performance contract
Exactly two measure proposals per cell per layout (unconstrained and at-final-width), zero measures during place, and no iterative negotiation. Flexible widths and span deficits are single closed-form divisions, so there is no candidate-width probing and no quadratic re-measurement. Incremental updates re-enter at the boundary; clean cells answer both proposals from the engine’s per-node cache, so a one-cell change costs ~2 real measures. Everything else is O(rows × columns) arithmetic per pass.
This is pinned as a golden test (day-pieces/tests/grid.rs::grid_measure_calls_bounded, per
§7.4’s “measure-call counts are part of the golden tests”): a 600-cell boot stays ≤ 2·cells +
slack mock measures, and a single-cell update stays ≤ 6 with exactly one native mutation op.
The grid is eager: every cell realizes a node. The showcase stress page runs 800+ cells
comfortably inside a scroll; thousands-of-rows datasets belong in list (recycling) or a
future lazy grid (§7).
Verification scope
This retained technical material has not received a complete signature, default, lifecycle, and native-runtime review against the 2026-10-22 working tree. Treat those details and unmarked code examples as unverified for this revision. The technical-review ledger records its status. Code blocks are API excerpts or integration fragments unless explicitly designated in the example catalog. No current all-platform pass or screenshot attribution is established by this page.