Stack
Stack puts space between things. It is a flex container with one job, and it is the answer to most "how do I space these out" questions — vertically by default, horizontally with direction: 'row'.
Gaps come from the spacing scale, so the rhythm of a page stays consistent without anyone picking pixel values.
Basic stack
stack({ gap: 'md' },
card(cardBody('First')),
card(cardBody('Second')),
card(cardBody('Third')),
)Direction
stack({ direction: 'row', gap: 'md' },
button('One'),
button({ variant: 'outline' }, 'Two'),
button({ variant: 'outline' }, 'Three'),
)Gap
A token name ('xs' … '3xl'), a number of spacing units, or a raw CSS length.
stack({ gap: 'lg' },
stack({ direction: 'row', gap: 'xs' }, chip('xs'), chip('xs'), chip('xs')),
stack({ direction: 'row', gap: 'md' }, chip('md'), chip('md'), chip('md')),
stack({ direction: 'row', gap: 6 }, chip('6 units'), chip('6 units')),
stack({ direction: 'row', gap: '3rem' }, chip('3rem'), chip('3rem')),
)Alignment
align and justify take raw flexbox values, so anything CSS understands works.
stack({ gap: 'md' },
stack({ direction: 'row', gap: 'sm', justify: 'space-between', style: 'background: var(--su-surface-2); padding: 0.6rem; border-radius: 0.5rem' },
chip('start'),
chip('end'),
),
stack({ direction: 'row', gap: 'sm', justify: 'center', align: 'center', style: 'background: var(--su-surface-2); padding: 0.6rem; border-radius: 0.5rem' },
button({ size: 'sm' }, 'Centred'),
chip('and aligned'),
),
)Wrapping
A row of chips or buttons that might not fit needs wrap — without it they squash instead of moving to the next line.
stack({ direction: 'row', gap: 'sm', wrap: true },
...['routing', 'data', 'islands', 'images', 'pagefind', 'lighthouse', 'sitemap', 'rss'].map(
(name) => chip({ color: 'neutral' }, name),
),
)Inline
inline makes the stack an inline-flex, so it sits in a line of text rather than taking the full width.
Built with
text(
'Built with ',
stack({ direction: 'row', gap: 'xs', inline: true, align: 'center' },
chip({ color: 'primary', size: 'sm' }, 'sitelo'),
chip({ color: 'neutral', size: 'sm' }, 'vite'),
),
' and nothing else.',
)As another element
stack({ as: 'nav', direction: 'row', gap: 'sm' },
navLink({ href: '/docs' }, 'Docs'),
navLink({ href: '/ui', current: true }, 'UI'),
navLink({ href: '/examples' }, 'Examples'),
)Props
| Prop | Type | Default | Description |
|---|---|---|---|
direction | 'row' | 'column' | 'column' | Main axis. |
gap | Space | 'md' | Space between children. |
align | string | 'stretch' | Any align-items value. |
justify | string | 'flex-start' | Any justify-content value. |
wrap | boolean | string | false | true means wrap; a string is passed through as flex-wrap. |
inline | boolean | false | Renders as inline-flex. |
as | string | 'div' | Element to render, e.g. nav or ul. |