LayoutSpec. Containers (row, column, stack, grid, scroll_area) place children inside the rect the parent got.
Node kinds (NodeKind)
| Kind | Children | Layout behavior |
|---|---|---|
leaf | none | Draws content only (label, button, panel, image, etc.) |
row | yes | Horizontal flexbox: children left to right |
column | yes | Vertical flexbox: children top to bottom |
stack | yes | Children share the same area; later siblings paint on top |
grid | yes | Fixed column count (grid_columns); row-major placement |
spacer | none | Empty space; respects LayoutSpec sizing |
scroll_area | yes | Like column, but clips children to the viewport |
stack, grid, and scroll_area, set kind and children on a SurfaceNode struct:
scroll_area turns on scissor clipping automatically. Pair it with app-managed scroll offset and a UniformList when virtualizing long lists (see Input and overlays).
Size rules (SizeRule)
LayoutSpec.width and LayoutSpec.height independently.
| Rule | Row main axis (horizontal) | Column main axis (vertical) |
|---|---|---|
fit | Child natural width | Child natural height |
fill | Share leftover width by flex weight | Share leftover height by flex weight |
fixed | Exact width | Exact height |
Flex weights
When multiple siblings use.fill on the same axis, space splits by layout.flex (default 1):
row, the first child gets 200 px width; the second takes the rest.
LayoutSpec fields
Alignment (AxisAlign)
start, center, end, stretch.
In a row, align_y positions children vertically. stretch makes cross-axis .fill children expand to the container height.
In a column, align_x positions children horizontally.
Padding and gap
padding: inset applied before laying out childrengap: pixels between adjacent children on the main axis
ui.Padding.uniform(12) or per-edge { .north = 8, .south = 8, .east = 16, .west = 16 }.
Measure and place pipeline
- measureNode: bottom-up intrinsic sizes
- applySizing: map measured size + parent bounds +
LayoutSpecto final rect - placeNode: emit draws, register hits, recurse into children
| Content | Measured size |
|---|---|
label | text width + font height |
button | text + horizontal/vertical padding |
toggle | ToggleSpec.width x ToggleSpec.height |
slider / progress | min width from layout, fixed height from spec |
image | image dimensions + ImageFit rules |
Container recipes
Header + scrollable body + footer
Toolbar row
Overlay stack (dim + dialog)
Usekind = .stack when a panel and its scrim share one area, or queue a separate overlay in finish() for popups anchored to a control (preferred for dropdowns).
Settings-style form grid
grid with grid_columns = 2 gives equal-width columns. Each cell is a child node (often a form_row leaf).
stack vs overlays
| Approach | When |
|---|---|
stack | Layers in the same layout pass (background + foreground in one tree) |
queueOverlay | Popups, menus, tooltips that must float above everything and dismiss on outside click |
z and run through shouldDismissOverlays().
Focus scopes
Setfocus_scope = true on a container node to keep keyboard focus traversal inside a panel (modals, sidebar sections). Child hits inherit the scope id.
Related types
UniformList/UniformGrid: compute visible item ranges for virtualized children (Input and overlays)ScrollState: thumb position forscrollbarnodesElement/findElement: debug inspector rects afterrender
Next
- Nodes and controls: leaf content types and specs
- Surface Description: full frame lifecycle

