Layout
Layout components arrange their children. They are containers — pass children to ->schema([...]),
and nest them freely. This page covers the three you reach for most; the disclosures
(Section & Collapsible) and the corner-pinned
Floating panel have their own pages.
Who decides width
Section titled “Who decides width”Containers do — never the content. A form, a heading, a paragraph and a table all fill whatever contains them, so the same definition looks right in a card, a tab, a grid column and a modal without knowing which one it landed in.
That leaves two places to set a measure:
- The page, via
#[AsPage(width: PageWidth::…)]. Caps the whole page and centres it in the layout slot, so a heading and the form beneath it stay aligned. - A container in the schema, via
Stack::make()->width(Width::…), when one zone of a page needs a narrower measure than the rest — a settings form beside a full-width table, say.
Forget both and content stretches to the full content width. That is the intended failure mode: a stretched form is obvious on sight, whereas a component quietly overriding its container’s layout is not.
Stack::make() lays children out in one direction with a consistent gap. It defaults to a vertical
column; pass ->direction('row') to lay them out horizontally.
Stack::make()->direction('row')->gap(Gap::Small)->schema([ Button::make('Save'), Button::make('Cancel')->emphasis(Emphasis::Ghost),]);- align:null
- direction:"row"
- float:null
- gap:"sm"
- height:null
- justify:null
- sticky:false
- width:null
- action:null
- buttonType:"button"
- effects:[]
- emphasis:null
- href:null
- icon:null
- label:"Save"
- method:null
- modal:null
- variant:null
- action:null
- buttonType:"button"
- effects:[]
- emphasis:"ghost"
- href:null
- icon:null
- label:"Cancel"
- method:null
- modal:null
- variant:null
Tune the layout with enum-typed options (see the enums reference):
->gap(Gap::ExtraSmall … ExtraLarge)— the space between children.->align(Align::…)and->justify(Justify::…)— cross-axis and main-axis alignment.->width(Width::…)and->height(Height::…)— sizing. The capped widths (Small…ExtraLarge) centre the stack; add->float(Side::Start)or->float(Side::End)to pin it to one edge instead.->float(Side::…)— float the stack to one side.->sticky()— pin the stack below the sticky chrome above it (a stickyTopbar, an earlier sticky stack) while the page scrolls. The stack paints the page background, keeps a small gutter once stuck, and publishes its own height as the sticky offset for its siblings, so a sticky vertical tab rail or another sticky stack further down lines up beneath it. A page header with the title and primary actions is the typical candidate.
Grid::make()->columns(…) lays children out in a responsive CSS grid:
->columns(3)— three equal columns from themdbreakpoint up, one column below.->columns(['default' => 1, 'md' => 2, 'xl' => 4])— an explicit count per breakpoint (default,sm,md,lg,xl,2xl), mobile-first: each value applies from its breakpoint up until a larger breakpoint overrides it.->columns('2fr 1fr 1fr 1fr')— a rawgrid-template-columnstrack list for unequal columns, e.g. one wide plus three narrow. Track lists work inside breakpoint maps too.
Any child component or field controls how many columns it covers with ->columnSpan(n)
(from md up), a breakpoint map, or ->columnSpanFull() to stretch across the whole row.
Grid::make()->columns(3)->schema([ Badge::make('Wide')->columnSpan(2), Badge::make('Narrow'), Badge::make('Full width')->columnSpanFull(),]);- columns:{"md":3}
- color:null
- columnSpan:{"md":2}
- label:"Wide"
- color:null
- label:"Narrow"
- color:null
- columnSpan:{"default":"full"}
- label:"Full width"
Responsive visibility
Section titled “Responsive visibility”Every component can opt out of rendering at a breakpoint range: ->hiddenFrom(Breakpoint::Md)
hides it at md and up, ->visibleFrom(Breakpoint::Md) shows it only from md up. The toggle is
CSS-only (a display: contents wrapper), so it is layout-transparent inside flex and grid parents —
use it for chrome that differs between mobile and desktop, like a logo mark that only appears in a
mobile topbar.
Card::make('Title', 'Description') wraps children in a bordered panel. Both arguments are optional —
omit them for a plain panel. Call ->tooltip('…') to attach an ⓘ info popover next to the title; its
content is trusted HTML and may include links.
Card::make('Team settings', 'Manage how your team appears.') ->tooltip('These settings affect everyone on the team.') ->schema([ Stack::make()->gap(Gap::Small)->schema([ Heading::make('Members', 2), Text::make('Three people have access to this team.'), Badge::make('3 active'), ]), Button::make('Invite member'), ]);- description:"Manage how your team appears."
- headerActions:[]
- title:"Team settings"
- tooltip:"These settings affect everyone on the team."
- align:null
- direction:null
- float:null
- gap:"sm"
- height:null
- justify:null
- sticky:false
- width:null
- copyable:false
- level:2
- text:"Members"
- tooltip:null
- align:null
- color:null
- copyable:false
- size:"md"
- text:"Three people have access to this team."
- color:null
- label:"3 active"
- action:null
- buttonType:"button"
- effects:[]
- emphasis:null
- href:null
- icon:null
- label:"Invite member"
- method:null
- modal:null
- variant:null