Stack
One axis, a token gap, and the two alignment answers, cross-axis placement and main-axis distribution.
A stack lays its children out in a row or a column with one gap between them, and then answers the two questions every row has: where each child sits across the axis, and how the children share the axis. It is the tool for a group of things in the order you wrote them, a toolbar, a form, a card body, a row that pairs a label with an action.
It is one-dimensional on purpose. Grid is the wall of equal columns when the items should wrap
themselves, and Split is the pair that stacks below a breakpoint; a stack states one axis and
stays on it.
Usage
import { Button, HStack } from "@asheeui/web";
<HStack gap="sm">
<Button>Save invoice</Button>
<Button variant="ghost">Cancel</Button>
</HStack>;
gap takes the shared spacing token rather than a number, so a stack's rhythm is the same
decision as a section's and a grid's. direction is the axis: column is the default, because
most groups read top to bottom.
Direction
A row keeps the children beside each other and a column stacks them; either way the order is the
order you wrote, and gap is the only space added between them. Nothing wraps by default, so a
row that must break onto a second line states wrap.
The tiles in the demo are align="stretch", so in a row they share the height of the tallest and
in a column they share the width of the widest. That is the column default, and it is the reason
a column of buttons makes every button as wide as the column, rarely what a row of actions
wants, which is why HStack states align="center" and lets each action keep its own width.
Alignment and distribution
align places each child across the axis, at the start, in the center, at the end,
stretched to fill it, or on the baseline of their first line of text. justify distributes
the free space along the axis: start, center, end, between, around and evenly.
The two are independent, which is why they are two options rather than one arrangement: a header
row of a title and an action is align="center" with justify="between", and a column of cards
is align="stretch" with justify="start". between is the one to reach for when the first item
belongs at one edge and the last at the other; baseline is the one to reach for when a large
label sits beside a small one and their text should line up rather than their boxes.
The presets
HStack is a stack with direction="row" and align="center", and VStack is one with
direction="column" and align="stretch". Both accept every other stack option, so a preset is
a starting point rather than a restriction, and align passed to either one wins over the preset
it implies.
The presets state their own direction and alignment one tier below the component configuration,
so components.stack.direction and components.stack.align reach an HStack and a VStack as
well as a plain Stack: a product whose rows are really columns states it once and both presets
follow. Only gap, justify and wrap are registered, which is exactly what leaves configuration
able to outrank a preset.
Props
gap md, justify start and wrap false are registered by the component, so
components.stack restates the framework's default rhythm once for the whole application.
direction and align are deliberately not registered: the hard fallbacks state the plain
Stack's column and stretch, HStack and VStack contribute their own pair below the
component tier, and a components.stack.direction or .align overrides all three. Registering
either would outrank the presets and make them ignore that configuration forever. Nothing resolves
from defaultVariant or defaultRadius: a stack has no surface and no corners, it is a
relationship between its children, not a box around them.
HStack and VStack take the same props with direction removed, because the preset states it.
Accessibility
- The element is a plain
divwith no role, and layout is not semantics: a stack of cards is not a list because it is a stack of cards. When the children are a list, renderas="ul"and give itlichildren, so a screen reader announces how many items there are. gapis a flex gap rather than a margin, so no space is attached to any child: a child that is hidden or absent does not leave a gap behind it.justifydistributes space andalignplaces items; neither reorders the children.wrapmoves overflow onto a new line without changing the sequence, which is what makes a wrapping toolbar safe to use.align="baseline"lines up the first lines of text rather than the boxes, which is how a heading, a badge and a caption share a row without their different heights looking like a mistake.- Nothing here announces anything, so a row whose meaning depends on position, the active tab, the current step, needs that state on the child, not on the stack.
The spacing scale, the width tokens and as are shared by every layout; see
Layout and spacing.