Layout and spacing
The spacing scale, the width tokens, and the axis vocabulary every layout component shares.
The layouts are one system rather than twelve components: a spacing scale, a set of width tokens,
and an axis vocabulary that Stack, Grid and Split express differently. Learn the three and
you can read any of them.
The spacing scale
Every spacing, gap and minHeight in the layout family takes the same token, so a page's
rhythm is a decision rather than a number typed in each place.
| Token | Steps of 0.25rem | Use it for |
|---|---|---|
none |
0 | A band that must sit flush against what follows |
xs |
1 (0.25rem) | Icon-to-label, inside a chip |
sm |
2 (0.5rem) | Between related lines |
md |
4 (1rem) | The default gap between siblings |
lg |
6 (1.5rem) | Between groups inside a band |
xl |
8 (2rem) | Between bands |
2xl |
12 (3rem) | A band that is meant to feel like a break |
The token names are the API; the numbers are what the theme maps them to. A product that wants a tighter or looser page changes the scale once, not every call site.
Width
A width is one of five tokens, and it is a maximum rather than a fixed size: content narrower than the token still sits at its natural width, centred or aligned by its parent.
| Token | Behaviour |
|---|---|
sm, md, lg, xl |
A maximum width with side gutters, so text does not run to the edge of a wide window |
full |
No maximum: the band fills the viewport |
Two props pair with it, and the pairing is what keeps a full-bleed band from being a design dead
end: contained says whether the content inside is held to a width at all, and containerSize
says which width. A band with contained={false} is full-bleed but its content may still be
contained, that is what a coloured section with a centred headline is.
Asking for the width
A style is answered by a media query through a class, which is what every component uses. A
branch — render a table or cards, a row of actions or an overflow menu — decides which elements
exist, and a class cannot do that. useBreakpoint() is how a component asks, and both renderers
export it under that name with the same shape:
import { useBreakpoint } from "asheeui";
const { width, breakpoint, isAtLeast } = useBreakpoint();
return isAtLeast("lg") ? <Table rows={rows} /> : <Cards rows={rows} />;
breakpoint is the largest name the width has reached, or base below the smallest, and the
widths are @asheeui/core's BREAKPOINT (sm 640, md 768, lg 1024, xl 1280) on both
platforms. The platform reads its window's own dimensions; the web measures the window after mount,
so server-rendered markup is the narrow arrangement and hydration never mismatches.
The rule that turns a width into a name is exported as resolveBreakpoint, and the state's shape
as BreakpointState, so a component that measures its own container can reuse both without
importing a renderer. useReduceMotion() follows the same pattern: the reader's motion preference,
under one name on either platform.
The as prop
Every layout component takes as, which changes the element it renders without changing what it
does. That is how a page states its structure: as="header", as="nav", as="main",
as="section". The default is always the sensible neutral element, so as is for stating meaning
the component cannot infer, not for styling.
The axis vocabulary
Three components place children along an axis, and they use the same words for the same ideas:
| Idea | Stack |
Grid |
Split |
|---|---|---|---|
| How children flow | direction (row, column) |
columns (1…12, at sm/md/lg breakpoints) |
Two panes, side by side above stackAt |
| Space between them | gap |
gap |
gap |
| Cross-axis alignment | align (start, center, end, stretch, baseline) |
align (start, center, end, stretch) |
align (start, center, stretch) |
| Main-axis distribution | justify (start, center, end, between) |
ratio (equal, start, end) |
|
| When it wraps | wrap |
Always, by column count | At stackAt (sm, md, lg, xl) |
The distinction worth holding on to: Stack is one-dimensional, a row or a column of things,
in the order you write them. Grid is two-dimensional, a fixed number of columns, so the
items that do not fit wrap onto the next line themselves. Split is two things, a pair that
sits side by side when there is room and stacks when there is not, which is the shape of a
settings panel, a comparison or a table of contents.
Which one for which job
| The page needs | Use |
|---|---|
| A row or column of items with a gap | Stack |
| A wall of cards in equal columns | Grid |
| Something beside something else, stacking on narrow screens | Split |
| One thing in the middle of the viewport | Centered |
| A band of a page with a heading and a width | Section |
| A maximum width around arbitrary content | Container |
| A whole page shell | Page, SidebarLayout, DocsLayout, MarketingLayout |
For how the shells assemble a real page, including which one a product should start from, see Page compositions.