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
PropTypeDefaultDescriptionPlatforms
children requiredcontent—

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.

web, lit, rn, swiftui
directionvertical | horizontalvertical

Main axis. `horizontal` follows writing direction (start→end), not left→right.

web, lit, rn, swiftui
gapnone | tight | normal | loose | sectionnormal

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.

web, lit, rn, swiftui
alignstart | center | end | stretchstretch

Cross-axis alignment.

web, lit, rn, swiftui
justifystart | center | end | betweenstart

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`".

web, lit, rn, swiftui
wrapbooleanfalse

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.

web, lit, rn, swiftui
elementdiv | section | nav | ul | oldiv

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.

web, lit

Accessibility

No implicit ARIA role — the component is styling and structure, and adds nothing to the accessibility tree.

Anatomy

containeritem

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.