Stack
The layout arranger — lanes in one of two grid engines, with direction, reverse, alignment, band padding and a measure cap.
Each example measures itself, so its gutters are to scale rather than to the page's. The arrangements are exact.
Stack arranges Slots into lanes. Two engines share the component via pattern: ratio (a module built from your weights + a real gap, the default) and snap (the 25-column grid). direction is ratio-only; reverse works in both; maxWidth caps and centres the measure while the Section band stays full-bleed.
Stack is also the only place spacing lives — gap, paddingX, paddingTop and paddingBottom, four independent dials across both axes; Section owns none of it, which is what lets one Section hold several Stacks. All four are fluid and container-relative, and the Section is the only thing they measure against — a maxWidth cap sets a width, not a new container.
Edit every prop live in Properties. The two engines behind pattern have their own pages under Patterns: Ratio and Snap.
Nesting
The inner Stack sets only lanes, and the rest is default rather than inherited.
Its edges still align with the Slot above it and the space between its lanes still matches the space above it. One rule does that: a nested Stack declares no band metrics — it can still set every one of them, but it does not have to. paddingX, paddingTop and paddingBottom resolve to none when nested, and gap takes the band default, which is what the outer Stack is on too.
So depth carries no meaning — four levels down is indistinguishable from one. A Section resets to band-level, and an explicit value always wins: asking for a value means asking for that value.
Aligned rows
Rows whose weight totals divide share their lane edges.
Because a weight is a column count, not a share of what is left over. Both rows total three, so both lay out three equal tracks and 2|1 simply spans two of them. One total need only be a whole multiple of the other, so a page written in twelfths (8|4, 4|4|4, 6|6) lines up throughout.
The price is that a wide lane also owns the gutter it spans, so 2|1 renders nearer 2.14:1 than 2:1 — the same arithmetic every print grid has always used. Equal splits are unaffected: 1|1|1 is exactly thirds.
Lanes and children
The weights are dealt out to the children in DOM order.
Both rows are lanes="2|1". The first holds one Slot, so it spans two of the three tracks and the third is simply never filled — no element, no spacer, just a track nobody was placed into. That is the only way to get a left-aligned narrower measure, since maxWidth always centres what it caps.
The second holds four, and the weights cycle onto a new row. A row fills when the weights run out, not when the children do — 2|1 takes two children per row, so the third starts row two. One gap feeds both axes, so the space between rows matches the space between lanes with nothing set.
Reverse
The content order flips; the lane sizes do not.
Same two Slots in the same DOM order in both rows. The top row is the default; the bottom sets reverse and renders B|A.
By breakpoint it takes the same mobile-first object as lanes, which is why this example sets it outright: a still picture cannot show a breakpoint. The mobile reorder, where the secondary lane leads on small screens, is reverse={{ base: true, md: false }} beside lanes={{ base: 1, md: "1|1" }}. Both routes are grid-native — an array reversal for true, grid order per breakpoint — so there is no flex-direction swap and align keeps working.
Lanes by breakpoint
A lane set is a responsive dial; direction is a decision.
The top row is lanes={{ base: 1, sm: "1|1" }}: one column to start with, two lanes once its Section reaches 640px. Narrow the window to see it move — and note it queries the Section, not the viewport, which is why the same component answers correctly in a sidebar and in a full page.
Mobile-first, like Tailwind: base is the narrow case and each breakpoint applies at that width and up — sm 640, md 768, lg 1024, xl 1280, 2xl 1536. The same object works for reverse here and for Grid’s maxColumns, so one rule covers every value that changes with width.
The bottom row is already one column and always will be, because it sets direction="vertical" instead. A primitive should not silently restructure itself, so a Stack changes with width only where it is given a lane set — opinionated mobile behaviour belongs in templates.