Grid
A fixed number of columns, so a wall of cards lines up and wraps on its own.
A grid fixes the number of columns across a region and lets the children fill them. It is the layout a card deck, a feature list, a pricing table or a dashboard is built on: the count is the design decision, and each cell wraps onto the next row without being told where it goes.
Two things separate it from its siblings. Stack arranges one axis, a row or a column in the
order you write it, while a grid is two-dimensional, so the count across is stated once and the
rows follow. And Split is only ever two panes; a grid is for the case where there are many cells
and they are interchangeable.
Usage
import { Grid } from "@asheeui/web";
<Grid columns={1} columnsMd={2} columnsLg={3} gap="lg">
<Card title="Inbox" description="12 unread" />
<Card title="Sent" description="348 this month" />
<Card title="Drafts" description="3 unfinished" />
</Grid>
gap is a token from the shared spacing scale rather than a number of pixels, so a grid and the
stack inside one of its cells keep the same rhythm.
The column scale
columns is one of 1, 2, 3, 4, 6 or 12. There is no arbitrary count, because every
column class has to be a complete literal the stylesheet can see; the larger counts are for a
layout that needs the finer division, not for spanning a cell across several tracks, with
columns={12} each child still takes exactly one of the twelve.
The direction is mobile-first. columns is the count at the narrowest viewport, columnsMd takes
over from the md breakpoint and columnsLg from lg, and a breakpoint you leave unstated
inherits the count below it rather than forcing one. A grid that never changes shape states
columns alone; a grid that changes twice states all three.
Because the breakpoints are min-width, columnsLg has the last word on a wide screen even when
columnsMd is also stated. Read the three props as one sentence from narrow to wide rather than
as three independent overrides.
A row that does not fill
Cells are placed in the order they are written, left to right and then onto the next row. When the children do not divide evenly by the column count the last row is simply short: each cell keeps the width of the track it sits in, and the remaining tracks stay empty instead of stretching a cell to fill them.
That is usually what a deck of cards wants, and it is worth checking at each breakpoint: five
cards are a tidy 3 + 2 at lg and a wasteful 2 + 2 + 1 at md, so the count is the thing to
tune, not the children.
align decides the cross axis for every cell. The default, stretch, makes the cells in a row
share its height, which is what a row of cards with different amounts of text needs; start,
center and end let each cell keep its own height and align it within the row instead.
Props
columns, gap and align are registered by the component as 1, md and stretch, so each
resolves from the instance, then components.grid, then that value. columnsMd and columnsLg
are deliberately left out of both the registered defaults and the fallback: an unstated breakpoint
has to inherit the count below it, and a registered value would replace that inheritance with a
number the application never asked for. A page with no configuration therefore gets a
single-column grid with a md gap, and components.grid states the application's count once for
every deck in the product.
Accessibility
- A grid adds no role and no ARIA attributes. It changes presentation only, so the cells are read by a screen reader and reached with Tab in the order they are written, the visual arrangement cannot reorder them.
asrenders the element you name, so a grid whose cells are list items statesas="ul"and receiveslichildren. That gives the deck list semantics, how many items, and the position within them, rather than a run of unrelated boxes.gapis spacing on the container, not a wrapper element between the cells, so nothing extra sits in the reading order and notabindexis introduced.- Alignment is layout:
stretchmakes the cards in a row match heights and says nothing about their headings, so keep each card's owntitleAsin step with the page's outline.
Gaps, width tokens and the axis words are the layout family's shared vocabulary rather than a grid-only set, see Layout and spacing.
Native
@asheeui/native exports the same Grid with the same columns, columnsMd, columnsLg and
gap, and the same inheritance rule. What differs is the mechanism: a device has no media query,
so the grid asks useBreakpoint what the window has reached and places the cells as a wrapped row
of fixed-basis children. That mechanism states its cell widths as four counts, so 6 and 12 are
expressed on the web only and fall back to a single column on a device; keep to 1 to 4 in a
grid that has to render on both. See React Native.