Stack
Vertical rhythm between siblings, so spacing is owned in one place.
@luc/ui/layoutExamples
Vertical rhythm
Spacing between siblings, owned in one place. The scale is shared with `Grid` and `Inline`, and the only thing a `Stack` decides is the gap.
Return
Volatility
Sharpe
Return
Volatility
Sharpe
Return
Volatility
Sharpe
Return
Volatility
Sharpe
Return
Volatility
Sharpe
<Stack gap="xs">…</Stack>
<Stack gap="sm">…</Stack>
<Stack gap="md">…</Stack>
<Stack gap="lg">…</Stack>
<Stack gap="xl">…</Stack>Alignment
`stretch` is what a bare flex column already does; naming it is what lets a footer sit at the bottom of a row of unequal cards.
Short
Wider cell
Short
Wider cell
Short
Wider cell
Short
Wider cell
<Stack align="start">…</Stack>
<Stack align="center">…</Stack>
<Stack align="end">…</Stack>
<Stack align="stretch">…</Stack>Nesting
Two gaps, two jobs: the outer `lg` separates topics, the inner `xs` keeps a label with the value it names. It is the one pattern that replaces three ad-hoc margins on every page.
Lookback
200 weeks
Rebalance
Monthly
Universe
US large cap
<Stack gap="lg">
<Stack gap="xs">
<Text variant="caption" tone="muted">Lookback</Text>
<Text variant="body">200 weeks</Text>
</Stack>
</Stack>A semantic list
`as` changes the element and nothing else, so a stack of factors can be a real `<ul>` with real `<li>` rows — which is what lets a screen reader count them out loud.
Momentum
12-1 total return
Value
Book to price
Quality
Gross profitability
<Stack as="ul" gap="sm">
<li>
<Stack gap="2xs">
<Text variant="label">Momentum</Text>
<Text variant="caption" tone="muted">12-1 total return</Text>
</Stack>
</li>
</Stack>Usage
import { Stack } from "@luc/ui/layout";<Stack gap="md">
<Text variant="label">Lookback</Text>
<Text variant="body">200 weeks</Text>
</Stack>`gap` is the only spacing decision; `as` changes the element when the group has a meaning of its own.
Package setup — peer requirements, the stylesheet and the provider — is on installation.
API
Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.
| Export | Kind | Signature |
|---|---|---|
alignClass | export | Record<Align, string> |
gapClass | export | Record<Gap, string> |
justifyClass | export | Record<Justify, string> |
Stack | 5 documented props | ({ className, gap, align, justify, as, ...props }: StackProps) => import("@types/react").JSX.Element |
Stack props
Type: StackProps
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
align? | Align | undefined | — | |
as? | "div" | "form" | "nav" | "ol" | "ul" | "aside" | "footer" | "header" | "section" | undefi… | — | Render as `<section>`, `<ul>`, `<nav>` … without losing the API. |
children? | ReactNode | — | |
gap? | Gap | undefined | — | |
justify? | Justify | undefined | — |
Guidelines
Do
- Use `Stack` for every vertical rhythm, including the one-child case: the alternative is a margin on the child, and facing margins do not collapse predictably inside flex.
- Nest a `gap="xs"` stack inside a `gap="lg"` one to keep a label with its value while the topics stay apart.
- Reach for `as` when the group is a list, a nav or a form rather than wrapping the stack in another element to get the semantics.
Don't
- Do not use a `Stack` for two elements that are really the columns of one row; that is `Grid` or `Inline`.
- Do not set a gap and margins on the same group: two spacing systems in one place is what this component exists to remove.
- Do not push a footer down with a gap — a minimum height and `mt-auto` on the footer says it once, and says it in the right place.
Accessibility
`Stack` renders a `div` by default and adds no roles, but `as` *is* its accessibility feature: a stack of rows can be a real `ul` with `li` children, a `nav` or a `form`, and the element is what a screen reader announces and counts.
Family
Layout
Space, rhythm and the surfaces a page is made of.
10 documented entries in this family. Open the family index
Related