Grid
The layout grid, on native CSS Grid — twelve columns, a gutter per breakpoint, spans and column counts per breakpoint or container width, auto-fill, and subgrid.
Overview
<Grid> renders the .grid of @chassis-ui/css: a display: grid container with twelve columns and, between its items, the gutter of the current breakpoint. <GridItem> places a child on the columns and rows. <Grid> replaces <Row> and <Col>, which version 0.4 removes with the flexbox grid they rendered — see Migrating from Row/Col. The Responsive Grid guide builds page layouts from the pieces documented here. For a single row or column of items, <Stack> and <Flex> are a lighter fit.
A Tailwind build of @chassis-ui/css has the classes of the <Grid> and <GridItem> props only
with its safelist loaded: see Tailwind.
Basic usage
Three equal-width columns in a twelve-column grid each span four tracks:
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid>
<GridItem span={4}>span=4</GridItem>
<GridItem span={4}>span=4</GridItem>
<GridItem span={4}>span=4</GridItem>
</Grid>
)
}Item placement
<GridItem> places a child within a <Grid>. An item with no placement props is one track wide and takes the next free cell.
Span
Set span to a number from 1 to 12 to size an item to that many column tracks, rendered as the .col-span-{n} class. An object in place of the number sets the span per breakpoint: base is the span from the narrowest viewport, and a key named after a breakpoint (sm/md/lg/xl/2xl) is the span from that breakpoint up. Every placement prop of <GridItem> takes such an object, as do columns, rows, gap and flow of <Grid>. Three span={{ base: 6, md: 4 }} items span six tracks, and four from the md breakpoint:
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid>
<GridItem span={{ base: 6, md: 4 }}>span=6 md:span=4</GridItem>
<GridItem span={{ base: 6, md: 4 }}>span=6 md:span=4</GridItem>
<GridItem span={{ base: 6, md: 4 }}>span=6 md:span=4</GridItem>
</Grid>
)
}span="full" spans every track of the row (.col-span-full), whatever the column count. An item that stacks below a breakpoint needs it as the base: with a span at md only, the item is one track wide below md, not full width.
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid>
<GridItem span="full">span=full</GridItem>
<GridItem span={{ base: 'full', md: 6 }}>span=full md:span=6</GridItem>
<GridItem span={{ base: 'full', md: 6 }}>span=full md:span=6</GridItem>
</Grid>
)
}Start
Set start to a column line from 1 to 12 to position an item, independent of the items before it (.col-start-{n}). An offset in the flexbox grid was relative to the previous column; a start line is absolute.
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid>
<GridItem span={4} start={3}>
span=4 start=3
</GridItem>
<GridItem span={4}>span=4</GridItem>
</Grid>
)
}start="auto" returns an item to the flow, to undo a start line from a breakpoint up:
<GridItem span={6} start={{ base: 4, md: 'auto' }} />End
Set end to the column line an item ends at, from 1 to 13 (.col-end-{n}): line 13 is the end edge of twelve columns. With span, it places an item against that edge, whatever comes before it.
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid>
<GridItem span={3}>span=3</GridItem>
<GridItem span={3} end={13}>
span=3 end=13
</GridItem>
</Grid>
)
}end="auto" undoes an end line from a breakpoint up, as start="auto" does a start line.
Rows
Set rows on <Grid> for the number of equal rows in its template: a count from 1 to 6 renders the .grid-rows-{n} class. A grid with no rows has no template rows, and each row is as tall as its content. Place an item on the rows with rowSpan (.row-span-{n}, 1 to 6), rowStart (.row-start-{n}, 1 to 6, or 'auto') and rowEnd (.row-end-{n}, 1 to 7, or 'auto'). All three take a value per breakpoint on an item, as rows does on the grid.
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid rows={2}>
<GridItem span={4} rowSpan={2}>
span=4 rowSpan=2
</GridItem>
<GridItem span={8}>span=8</GridItem>
<GridItem span={8} start={5} rowStart={2}>
span=8 start=5 rowStart=2
</GridItem>
</Grid>
)
}Columns and gap
columns sets the number of columns and gap the space between items. A column count from 1 to 12 renders the .grid-cols-{n} class, and a Spacing token in gap (the scale <Flex> and <Stack> use) renders the .gap-{token} class. Any other gap is a raw CSS value — a length, or a "{row} {column}" pair — set through the --cx-grid-gap custom property. With no gap, the grid has the gutter of the breakpoint: 1rem below md and 1.5rem from it.
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<>
<Grid columns={4} gap="1rem">
<GridItem span={2}>span=2</GridItem>
<GridItem span={2}>span=2</GridItem>
</Grid>
<Grid gap=".25rem 1rem">
<GridItem span={6}>span=6</GridItem>
<GridItem span={6}>span=6</GridItem>
<GridItem span={6}>span=6</GridItem>
<GridItem span={6}>span=6</GridItem>
</Grid>
<Grid gap="lg">
<GridItem span={4}>span=4</GridItem>
<GridItem span={4}>span=4</GridItem>
<GridItem span={4}>span=4</GridItem>
</Grid>
</>
)
}An object changes columns and gap from a breakpoint up, with the same classes. Only a token fits a breakpoint's gap, since a raw value has no class; base takes either. A breakpoint's columns is a count from 1 to 12 for the same reason.
import { Grid } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid columns={{ base: 1, sm: 2, lg: 4 }} gap={{ base: 'sm', lg: 'md' }}>
<div>Column</div>
<div>Column</div>
<div>Column</div>
<div>Column</div>
</Grid>
)
}The .row-gap-{token} and .column-gap-{token} utilities set one axis, passed through className.
Flow
flow sets the direction a grid places its items in, rendered as the .grid-flow-* classes. A grid fills a row before it starts the next. flow="column" fills the rows of a column before it moves to the next column, so it goes with rows. An object changes the flow from a breakpoint up.
import { Grid } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid columns={2} rows={3} flow="column" gap="md">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</Grid>
)
}flow="dense" moves a later item into a gap that an earlier, wider one left; 'row-dense' and 'column-dense' name the direction too. Below, the third item fits beside the first, where the second did not.
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid columns={3} flow="dense" gap="md">
<GridItem span={2}>1</GridItem>
<GridItem span={2}>2</GridItem>
<GridItem>3</GridItem>
<GridItem>4</GridItem>
</Grid>
)
}Dense placement changes where items appear, not their order in the document, which the keyboard and screen readers follow. Use it for items that read in any order, such as a gallery.
Auto-fill layout
Set fill to render .grid-fill: equal columns, as many as there are children, with no columns to count.
import { Grid } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid fill gap="md">
<div>Column</div>
<div>Column</div>
<div>Column</div>
<div>Column</div>
</Grid>
)
}min sets the narrowest a column of a fill grid can get. Children that would be narrower wrap to a new row, so the column count follows the available width with no breakpoint.
import { Grid } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid fill min="10rem" gap="md">
<div>Column</div>
<div>Column</div>
<div>Column</div>
<div>Column</div>
<div>Column</div>
<div>Column</div>
</Grid>
)
}Container queries
A breakpoint key of a prop, such as md in span={{ base: 'full', md: 6 }}, is a width of the viewport. A grid in a sidebar, a panel or a narrow column has less room than the viewport, and a container key follows the room it has: '@sm' to '@2xl' are the widths of sm to 2xl, measured on the nearest query container. <Grid> and <GridItem> take them beside the breakpoint keys and render the container classes of @chassis-ui/css: columns={{ '@md': 3 }} is .@md:grid-cols-3, and span={{ '@md': 4 }} is .@md:col-span-4. Where a breakpoint key and a container key both apply, the container key wins.
A query container is an ancestor with the .contains-inline class. Set contained on the grid for its default gutter and column count to follow that container too. Both boxes below hold the same grid, at any viewport width: the first is 18rem wide, under the @sm width, so its items stack, and the second is 40rem wide, so they sit side by side.
import { Grid, GridItem } from '@chassis-ui/react'
const Items = () => (
<Grid contained>
<GridItem span={{ base: 'full', '@sm': 4 }}>Item</GridItem>
<GridItem span={{ base: 'full', '@sm': 4 }}>Item</GridItem>
<GridItem span={{ base: 'full', '@sm': 4 }}>Item</GridItem>
</Grid>
)
export const Example = () => {
return (
<>
<div className="contains-inline mb-md" style={{ width: '18rem' }}>
<Items />
</div>
<div className="contains-inline" style={{ width: '40rem' }}>
<Items />
</div>
</>
)
}Without a query container above the grid, a container key never applies and contained leaves the gutter of the viewport.
Nesting
A <Grid> inside a <GridItem> is a grid of its own, with twelve tracks across the width of the item. It is narrower than the viewport that the gutter follows, so give it a gap, and columns in place of twelve tracks, or lay it out by its container.
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid gap="md">
<GridItem span={8}>
<Grid columns={2} gap="sm">
<div>Nested column</div>
<div>Nested column</div>
</Grid>
</GridItem>
<GridItem span={4}>span=4</GridItem>
</Grid>
)
}A nested grid inherits what the outer grid sets through a custom property: a raw gap, min, and a columns above 12. It does not inherit the rest: columns from 1 to 12, rows, flow and a token gap.
Subgrid
Set subgrid on a <GridItem> to give it the column tracks it spans in the parent <Grid> instead of tracks of its own, so its children align to the outer columns. subgrid is a prop of <GridItem> because CSS subgrid only takes effect on an element that is itself a grid item of the parent. A subgrid has a gap and rows of its own: pass it the gap of the parent, or its children are narrower than the parent's tracks.
import { Grid, GridItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Grid gap="md">
<GridItem span={8} subgrid gap="md">
<GridItem span={4}>Subgrid span=4</GridItem>
<GridItem span={4}>Subgrid span=4</GridItem>
</GridItem>
<GridItem span={4}>span=4</GridItem>
</Grid>
)
}Migrating from Row/Col
<Row> and <Col> rendered the flexbox grid, which @chassis-ui/css removed in 0.7. @chassis-ui/react deprecated them in 0.3 and removes them in 0.4. Each of their props has a counterpart on <Grid> and <GridItem>:
| Row and Col | Grid and GridItem |
|---|---|
<Row> | <Grid>, with no <Container> needed around it |
<Col span={4}> | <GridItem span={4}> |
<Col span={12}> | <GridItem span="full"> |
<Col span={12} responsive={{ md: { span: 6 } }}> | <GridItem span={{ base: 'full', md: 6 }}> |
<Col span={4} offset={2}> | <GridItem span={4} start={3}> |
<Col order="first"> | <GridItem className="order-first"> |
<Row> with bare <Col>s | <Grid fill> with plain children |
<Row cols={3}> with bare <Col>s | <Grid columns={3}> with plain children |
<Row cols={1} responsive={{ md: { cols: 3 } }}> | <Grid columns={{ base: 1, md: 3 }}> |
<Row gutter="md"> | <Grid gap="md"> |
gutterX, gutterY | className="column-gap-{token}", className="row-gap-{token}" |
<Col span="auto"> | <Flex gap="md"> with plain children |
Three differences change how a layout behaves:
- An item with no
spanis one track wide. A bare<Col>shared the row with its siblings, and a<Col>with only a responsive span was as wide as the row below that breakpoint. Give such an itemspan="full", as the value or thebase, or use afillgrid. - Each prop holds its own breakpoints.
<Row>and<Col>took oneresponsiveobject, keyed by breakpoint, with the props inside.<Grid>and<GridItem>have noresponsiveprop: each prop takes an object keyed by breakpoint, with the value below the first one asbase.<Grid>and<GridItem>of version 0.3 took theresponsiveprop too, and their props move the same way. - The gap applies to both axes. A
<Row>with no gutter prop had 1.5rem between columns and nothing between rows. A<Grid>has the gutter of the breakpoint between both;className="row-gap-zero"removes it between rows. - A start line is absolute.
offsetpushed a column away from the one before it, sostartis the offset plus one only for the first item of a row.
Scope
No content-width tracks. A grid sizes its tracks from the container, not from the content, so <Col span="auto"> has no grid counterpart: a row of items as wide as their content is a <Flex>.
Placement classes stop at twelve columns and six rows. span, start and end render classes that @chassis-ui/css generates up to 12 columns, and rowSpan, rowStart and rowEnd up to 6 rows. In a grid with more, an item past those needs an inline style={{ gridColumn: '…' }}.
No query container of its own. <Grid> makes neither itself nor its parent a query container. The container keys and contained read one from an ancestor, which the layout around the grid provides.
API
Grid
| Prop | Type | Default |
|---|---|---|
asChild? | boolean | — |
Render the single child element in place of this component's own element, with this
component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log
in</Link></Button>. The alternative to component for React Server Components — an element
can be passed from the server to this package's client components, a component reference
(component={Link}) can't. | ||
className? | string | — |
| A string of all className you want applied to the base component. | ||
columns? | Responsive<number, Breakpoint | ContainerBreakpoint> | — |
Number of columns in the grid template (the column count of @chassis-ui/css when omitted,
12 by default). A count from 1 to 12 is mapped to the grid-cols-{n} class, which applies
to this grid alone. Any other count has no class and is set via the --cx-grid-columns
custom property, which a grid nested in this one inherits: give that grid columns of its
own. Has no effect when fill is set.
An object sets the count from a width up, as in { base: 1, md: 3 }, here and on rows,
gap and flow. A breakpoint key (md) is a width of the viewport. A container key
('@md') is the same width of the nearest query container (an ancestor with the
contains-inline class), and wins over a breakpoint key where both apply. Without a query
container above the grid, a container key never applies. The count of a key is a class, so
a count from 1 to 12. | ||
component? | ElementType | — |
| Component used for the root node. Either a string to use a HTML element or a component. | ||
contained? | boolean | — |
Adds the contained class: the default gutter and column count follow the width of the
nearest query container instead of the viewport, so a grid in a narrow column of a wide page
has the gutter of a narrow page. Without a query container above it the grid keeps the
values of the viewport. columns and gap still override them. | ||
fill? | boolean | — |
Renders .grid-fill instead of .grid — columns expand equally to fill the available
width, with the column count determined by the number of children rather than columns. | ||
flow? | Responsive<GridFlow, Breakpoint | ContainerBreakpoint> | — |
The direction items are placed in, mapped to the grid-flow-* classes: by 'row' (the CSS
default) or by 'column', which fills the rows of one column before it starts the next.
'dense', 'row-dense' and 'column-dense' also move later items into gaps that earlier,
wider ones left. An object sets the flow from a width up. | ||
gap? | { Spacing | string | { base?: Spacing | string } & Partial<Record<Breakpoint | ContainerBreakpoint, Spacing>> } | — |
Gap between grid items: a Spacing token (mapped to the gap-{token} class) or any raw CSS
gap value, including a "{row} {column}" pair (set via the --cx-grid-gap custom
property). The gutter of the current breakpoint when omitted, or of the query container when
contained is set. An object sets the gap from a width up: only a token fits a key other
than base, since a raw value has no class. | ||
min? | string | — |
Minimum column width of a fill grid, set via the --cx-grid-min custom property: any CSS
length, e.g. "16rem". Children wrap to a new row when they would get narrower. 12rem in
@chassis-ui/css when omitted. Only relevant when fill is set. | ||
ref? | any | — |
rows? | Responsive<number, Breakpoint | ContainerBreakpoint> | — |
Number of equal rows in the grid template. A grid has none when omitted: its rows are as
tall as their content. A count from 1 to 6 is mapped to the grid-rows-{n} class; any other
count has no class and is set inline, as the grid-template-rows the class declares. Has no
effect when fill is set. An object sets the count from a width up, where it is a class, so
a count from 1 to 6. | ||
GridItem
| Prop | Type | Default |
|---|---|---|
asChild? | boolean | — |
Render the single child element in place of this component's own element, with this
component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log
in</Link></Button>. The alternative to component for React Server Components — an element
can be passed from the server to this package's client components, a component reference
(component={Link}) can't. | ||
className? | string | — |
| A string of all className you want applied to the base component. | ||
component? | ElementType | — |
| Component used for the root node. Either a string to use a HTML element or a component. | ||
end? | { Responsive<number | 'auto', Breakpoint | ContainerBreakpoint> } | — |
Grid column line to end this item at, mapped to the col-end-{n} class (1 to 13, the line
after the last of 12 columns). With span, it places the item against the end edge of the
grid: span={3} end={13}. 'auto' (col-end-auto) undoes an end line at a wider
breakpoint: { base: 13, md: 'auto' }. | ||
gap? | { Spacing | string } | — |
Gap between the subgrid's children: a Spacing token (mapped to the gap-{token} class) or
any raw CSS gap value (set via the --cx-grid-gap custom property). Only relevant when
subgrid is set. A subgrid has the gap of its own: when the parent <Grid> takes a gap
token, give the subgrid the same one, or its children are narrower than the parent's tracks. | ||
ref? | any | — |
rowEnd? | { Responsive<number | 'auto', Breakpoint | ContainerBreakpoint> } | — |
Grid row line to end this item at, mapped to the row-end-{n} class (1 to 7, the line after
the last of 6 rows), or 'auto' (row-end-auto) to undo an end line at a wider breakpoint.
Takes an object per width, as span does. | ||
rows? | number | — |
Number of equal rows in the subgrid's own row template: the grid-rows-{n} class for a
count from 1 to 6, the same grid-template-rows set inline for any other. Only relevant
when subgrid is set — subgrid only inherits the parent's column tracks, not its rows. | ||
rowSpan? | Responsive<number, Breakpoint | ContainerBreakpoint> | — |
Number of grid row tracks this item spans, mapped to the row-span-{n} class. Takes an
object per width, as span does. | ||
rowStart? | { Responsive<number | 'auto', Breakpoint | ContainerBreakpoint> } | — |
Grid row line to start this item at, mapped to the row-start-{n} class (1 to 6), or
'auto' (row-start-auto) to return it to the flow. Takes an object per width, as span
does. | ||
span? | { Responsive<number | 'full', Breakpoint | ContainerBreakpoint> } | — |
Number of grid column tracks (of the parent <Grid>'s columns) this item spans, mapped to
the col-span-{n} class, or 'full' for every track of the row (col-span-full). An item
with no span is one track wide.
An object sets the span from a width up, as in { base: 'full', md: 6 }, here and on
start, end, rowSpan, rowStart and rowEnd. A breakpoint key (md) is a width of
the viewport. A container key ('@md') is the same width of the nearest query container
(an ancestor with the contains-inline class), and wins over a breakpoint key where both
apply. Without a query container above the grid, a container key never applies. | ||
start? | { Responsive<number | 'auto', Breakpoint | ContainerBreakpoint> } | — |
Grid column line to start this item at, mapped to the col-start-{n} class (1 to 12).
'auto' (col-start-auto) returns the item to the flow, to undo a start line at a wider
breakpoint: { base: 4, md: 'auto' }. | ||
subgrid? | boolean | — |
Turns this item into a nested subgrid: adds .grid/.grid-cols-subgrid alongside its
col-span-{n}/col-start-{n} placement classes, so its own children inherit the parent
<Grid>'s column tracks instead of defining new ones. Combine with span — a subgrid
item must itself be a grid item of the parent for grid-template-columns: subgrid to take
effect, which is why subgrid lives on <GridItem> (the element actually placed as a grid
item) rather than on a <Grid> nested inside it. | ||