Layout components
Containers that place other elements, plus each, which repeats elements once per item of a list.
Generated from the component definitions in src/components, the same source the parser validates against and the model guide is built from. Every element also accepts if=, tip=. Each example below is rendered live by the SayShape engine.
column
Stacks its children vertically.
| Prop | Type | Description |
|---|---|---|
gap | number | Space between children in pixels (default 8). |
pad | number | Inner padding in pixels (default 0). |
Children: any elements.
column gap=6
text "First"
text "Second"row
Places children side by side, splitting the width equally. On narrow screens they wrap; sibling cards share a height per line by default.
| Prop | Type | Description |
|---|---|---|
gap | number | Space between children in pixels (default 10). |
pad | number | Inner padding in pixels (default 0). |
align | top / center / bottom | Vertical alignment of children: top, center or bottom. |
wrap | boolean | Move children onto more lines when they don't fit side by side (default true). Set false to always keep them on one line. |
Children: any elements.
row gap=10
button "Cancel" variant=secondary
button "Save"overlap
Puts its later children on top of its first one, at a corner, an edge or the middle: a Sale badge on a picture, a count on an icon, a label over a card. The first child sets the size.
| Prop | Type | Description |
|---|---|---|
at | top-left / top / top-right / left / center / right / bottom-left / bottom / bottom-right | Where the children on top sit: top-left, top, top-right, left, center, right, bottom-left, bottom or bottom-right. |
inset | number | How far in from the edges they sit, in pixels (default 8). |
Children: any elements.
overlap at=top-right
card
text "Trail runner socks" weight=semibold
text "Three pairs" tone=muted
badge "Sale" tone=badgrid
Places children in responsive equal columns. Cards in the same grid row automatically share a height, keeping dashboard panels aligned.
| Prop | Type | Description |
|---|---|---|
min | number | Narrowest a column may be, in pixels (default 200). |
gap | number | Space between children in pixels (default 14). |
Children: any elements.
grid min=140 gap=10
stat label="Orders" value=12
stat label="Returns" value=1
stat label="Rating" value=4.8card
A surface that groups related content, stacked vertically. Give it on= to make the whole card pressable.
| Prop | Type | Description |
|---|---|---|
gap | number | Space between children in pixels (default 14). |
pad | number | Inner padding in pixels (default 22). |
variant | raised / hero / tinted / outline | raised (default), hero (an accent wash for the one card that leads the screen), tinted (a quiet accent fill for a side group) or outline (a border only). |
on | action | Actions to run when the card is pressed, e.g. emit:changed. Separate several with ; |
Children: any elements.
card variant=hero
text "Order summary" style=title
text "2 items" tone=muted
card on=emit:open_order
text "Trail runner socks" weight=semibold
text "Arrives Thursday" tone=mutedlist
A bordered list of rows, separated by lines. Use list-item children; any other element (a row, a check) becomes a row of its own.
Children: any elements.
list
list-item "Trail runner socks" detail="Merino blend" trailing="$24"
list-item "Rain shell" detail="Packs into its pocket" trailing="$129" on=emit:open_shelllist-item
One row of a list: a title, an optional detail line and value on the right, and anything inside it (a badge, a check, a row) under the title. Give it on= to make it pressable.
| Prop | Type | Description |
|---|---|---|
"text" | text, required | The row's title. |
detail | string | A second, quieter line under the title. |
trailing | string | A short value on the right, like a price or a count. |
icon | string | An icon before the text, by name from icon's list, e.g. icon=check-circle. |
on | action | Actions to run when the row is pressed, e.g. emit:changed. Separate several with ; |
Children: any elements.
Only inside: list.
list
list-item "Rain shell" detail="Packs into its pocket" trailing="$129" icon=package on=emit:open_shellaccordion
A heading that shows or hides the content under it when pressed. Stack several for an FAQ.
| Prop | Type | Description |
|---|---|---|
"text" | text, required | The heading. |
bind | state, required | A true/false state name: true while open. |
gap | number | Space between children in pixels (default 10). |
on | action | Actions to run after it opens or closes, e.g. emit:changed. Separate several with ; |
Children: any elements.
state returns=true
accordion "Returns" bind=returns
text "Free returns within 30 days, unworn."
accordion "Shipping" bind=shipping
text "Ships in two business days."divider
A thin horizontal line separating sections.
dividerspacer
Empty vertical space.
| Prop | Type | Description |
|---|---|---|
size | number | Height in pixels (default 8). |
spacer size=16each
Repeats what's inside it once per item of a list, in place, with no look of its own; inside, the current item is called item (or the name given by as=). if= on each keeps only the items it's true for: each items=tasks as=t if={owner == 'All' || t.owner == owner}.
| Prop | Type | Description |
|---|---|---|
items | expression, required | The list to repeat over, like todos or {cart}. |
as | name | Name for the current item, used inside: as=t makes {t.text} work. |
index | name | Name for the item's position, counting from 0. |
key | expression | A value unique to each item, like {t.id}, so focus stays with the item when the list changes. Defaults to the position. |
Children: any elements.
state gear=[{name: "Tent", kg: 2.1}, {name: "Stove", kg: 0.4}, {name: "Headlamp", kg: 0.1}]
column gap=6
each items=gear as=g index=i key={g.name}
text "{i + 1}. {g.name}, {g.kg} kg"timeline
Steps in order with a line joining them: itineraries, how-to guides, order tracking, project history. Give steps a status only to show progress, like where an order is or which step the person has reached.
| Prop | Type | Description |
|---|---|---|
gap | number | Space between children in pixels (default 20). |
Children: step.
timeline
step "Day 1: Sedona" detail="Cathedral Rock at sunrise"
step "Day 2: Flagstaff" detail="Lowell Observatory at night"
step "Day 3: Grand Canyon" detail="South Rim, Mather Point"
# Showing progress: a status on every step
timeline
step "Ordered" time="Oct 18" status=done
step "Out for delivery" detail="Arrives by 8 PM" status=current
step "Delivered" status=upcomingstep
One step of a timeline: a title, an optional detail line and time, and whether it's done, current or upcoming.
| Prop | Type | Description |
|---|---|---|
"text" | text, required | The step's title, e.g. Shipped. |
status | done / current / upcoming | Only to show progress: done for finished steps, current for where the person is now, upcoming for what's left (muted). Leave it out on every step for a plan, itinerary or how-to that hasn't started. |
detail | string | A quieter line under the title, like a place or a note. |
time | string | A short date or time on the right, e.g. Oct 18 or 9:12 AM. |
icon | string | An icon in the step's marker instead of the check or dot, by name from icon's list, e.g. icon=truck. |
Only inside: timeline.
timeline
step "Shipped" detail="Left the Phoenix warehouse" time="Oct 19" status=done icon=truckcarousel
Cards side by side, a page at a time, with arrows and dots: a few options to browse, like products, hotels or recipes. Put a card per option inside it (or each over a list).
| Prop | Type | Description |
|---|---|---|
bind | state, required | The state name holding the page shown, starting at 0. |
min | number | The narrowest a card may be (default 220); as many fit side by side as the width allows. |
gap | number | Space between children in pixels (default 12). |
on | action | Actions to run after the page changes, e.g. emit:changed. Separate several with ; |
Children: any elements.
state page=0
carousel bind=page
card
text "Desert Rose Inn" style=lead weight=semibold
text "$189 a night · 4.6 stars" tone=muted
card
text "Saguaro Lodge" style=lead weight=semibold
text "$149 a night · 4.3 stars" tone=muted
card
text "Camelback Suites" style=lead weight=semibold
text "$229 a night · 4.8 stars" tone=mutedscroll
A region that shows its children up to a fixed height and scrolls the rest, for a long list inside a card: messages, activity, results. Everything inside is still read and copied.
| Prop | Type | Description |
|---|---|---|
"text" | text | What it holds, for screen readers, e.g. Recent activity. Not drawn. |
size | sm / md / lg | How tall it gets before it scrolls: sm (about 6 rows of controls), md (about 9) or lg (about 12). |
gap | number | Space between children in pixels (default 8). |
pad | number | Inner padding in pixels (default 0). |
Children: any elements.
state events=[{title: "Order NW-2041 shipped", when: "09:12"}, {title: "Refund approved", when: "08:40"}, {title: "New review: 5 stars", when: "Yesterday"}, {title: "Stock low: Ridge 28 L", when: "Yesterday"}, {title: "Invoice sent", when: "Mon"}, {title: "Order NW-2038 delivered", when: "Mon"}, {title: "Price change: Summit pack", when: "Sun"}, {title: "New customer: Lumen Cinema", when: "Sat"}]
scroll "Recent activity" size=sm
list
each items=events as=e
list-item "{e.title}" detail="{e.when}"