Stack
The layout primitive. Lays out children in one direction with a token-based gap, so spacing is never hand-typed.
Basic usage
Install the package:
pnpm add @design-schema/react @design-schema/tokens Import the stylesheet once for the app, then the component:
import '@design-schema/tokens/calm-precise/css';
import '@design-schema/react/index.css';
import { Stack } from '@design-schema/react'; Examples
First item
Second item
Third item
import { Stack, Text } from '@design-schema/react';
const items = [
<Text key="first">First item</Text>,
<Text key="second">Second item</Text>,
<Text key="third">Third item</Text>,
];
<Stack>{items}</Stack>Props
7 rows| Prop | Type | Default | Description |
|---|---|---|---|
| children required | content | — | Any components. Stack does not style its children; it only positions them. Null and boolean children are skipped, as the platform skips them. In examples `children` describes the content in words; stories render it with system components (Input, Button, Text) in the order named, and the Default story renders three Text children reading "First item", "Second item" and "Third item". The examples resolve as: form fields are three Inputs labelled "Full name", "Email" and "Password"; the regions of the page are three Boxes (`surface: subtle`, `inset: md`) each holding a Text naming its region ("Summary", "Details", "History"); a row of filters is eight small secondary Buttons ("All", "Open", "Closed", "Mine", "Unassigned", "Urgent", "This week", "Archived"); the button row's primary submit Button is labelled "Submit". All of it is story scaffolding, not copy. |
| direction | vertical | horizontal | vertical | Main axis. `horizontal` follows writing direction (start→end), not left→right. |
| gap | none | tight | normal | loose | section | normal | Space between children, from the layout rhythm (`layout.gap.*`), not the raw spacing scale: tight for related controls, normal for fields in a form, loose for groups, section between page sections. The only way to set spacing between siblings. |
| align | start | center | end | stretch | stretch | Cross-axis alignment. |
| justify | start | center | end | between | start | Main-axis distribution. It only shows where the main axis is larger than the content — a vertical Stack needs a bounded height for it to mean anything, and Stack has no size of its own, so that is the caller's to give. The four values are the whole set: `around` and `evenly` are deliberately left out, since a rhythm system should not offer four ways to divide leftover space. Its enum stories (and `DirectionHorizontal`) are therefore horizontal: an enum-value story may add the args that make its value visible (`direction: horizontal`, `align: start`), while an example story has exactly its `given`. The `Gap*` stories take only `gap` and stay vertical. Meta args hold only schema defaults, and those merged under an example story count as "exactly its `given`". |
| wrap | boolean | false | Allow horizontal stacks to wrap onto new lines instead of overflowing. It is set whatever the direction — on a column it is inert unless the block size is bounded — rather than being silently ignored on a vertical Stack. As a boolean it gets one story, `Wrap` (horizontal, `align: start`, the eight filter Buttons of `wrapping-filters`, in the same width-bounded decorator). Accessibility: Prefer wrapping over horizontal scrolling so content reflows at 320px and 400% zoom. Native has neither viewport width nor browser zoom: the equivalent is that a wrapped row still fits when the platform's text size is turned up. |
| element | div | section | nav | ul | ol | div | Landmark or list semantics when the group has meaning. For `ul`/`ol`, each child is wrapped in an `li` that is `display: contents`, so the children stay the flex items and the gap is unchanged; the wrapper carries `role="listitem"` and the list `role="list"`, because dropping `list-style` removes list semantics in some browsers. One `li` per child as the platform counts children: a fragment holding two elements is one child, so pass an array; null and boolean children get no `li`. The list role wins over a consumer `role` on `ul`/`ol`; on the other elements a consumer `role` passes through. React Native has no counterpart for either value — a native list has no accessibility role to claim — so a navigation region there is Landmark and a list is a plain View whose rows carry their own semantics. |
Accessibility
No implicit ARIA role — the component is styling and structure, and adds nothing to the accessibility tree.
Anatomy
What generation guarantees
This component declares no contrast pair of its own — it inherits the colour of whatever it is placed in — and it does not animate.