Skip to main content
Light Dark System

Grid

<cw-grid> | CwGrid
Since 1.6 experimental

Lays its children out in columns, one child per cell, dropping to fewer columns as it narrows.

<cw-grid> arranges its children in columns, one child per cell, filling rows from the start. There are no wrappers to add: each direct child is a cell.

Columns respond to the grid’s own width, not the viewport’s, so the same grid works in a full-width page band and in a narrow sidebar without any breakpoints to tune.

One
Two
Three
Four
Five
Six
<cw-grid columns="3" min-column-width="10rem">
  <div class="grid-box">One</div>
  <div class="grid-box">Two</div>
  <div class="grid-box">Three</div>
  <div class="grid-box">Four</div>
  <div class="grid-box">Five</div>
  <div class="grid-box">Six</div>
</cw-grid>

<style>
  .grid-box {
    padding: var(--cw-spacing-large);
    border-radius: var(--cw-border-radius-medium);
    background: var(--cw-color-primary-100);
    color: var(--cw-color-primary-800);
    font-weight: var(--cw-font-weight-semibold);
  }
</style>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const css = `
  .grid-box {
    padding: var(--cw-spacing-large);
    border-radius: var(--cw-border-radius-medium);
    background: var(--cw-color-primary-100);
    color: var(--cw-color-primary-800);
    font-weight: var(--cw-font-weight-semibold);
  }
`;

const App = () => (
  <>
    <CwGrid columns="3" minColumnWidth="10rem">
      <div className="grid-box">One</div>
      <div className="grid-box">Two</div>
      <div className="grid-box">Three</div>
      <div className="grid-box">Four</div>
      <div className="grid-box">Five</div>
      <div className="grid-box">Six</div>
    </CwGrid>

    <style>{css}</style>
  </>
);

See Page Layout for how sections, grids and stacks combine into a full page.

Examples

Equal Columns

Set columns to a count from 1 to 6. The default is 2.

The count is a maximum, not a fixed number. Whenever a column would be narrower than min-column-width (15rem by default), the grid drops a column, so a four-column grid becomes three, then two, then one as it narrows. A partly filled last row keeps the same item width as the rows above it.

Drag the bottom-right corner of the box below to resize it. This example sets min-column-width="7rem" so all four columns fit in this page’s content column; most other examples here lower it too.

One
Two
Three
Four
Five
Six
<div class="grid-resizer">
  <cw-grid columns="4" min-column-width="7rem">
    <div class="grid-box">One</div>
    <div class="grid-box">Two</div>
    <div class="grid-box">Three</div>
    <div class="grid-box">Four</div>
    <div class="grid-box">Five</div>
    <div class="grid-box">Six</div>
  </cw-grid>
</div>

<style>
  .grid-resizer {
    resize: horizontal;
    overflow: auto;
    max-width: 100%;
    min-width: 8rem;
    padding: var(--cw-spacing-small);
    border: dashed 1px var(--cw-color-neutral-400);
  }
</style>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const css = `
  .grid-resizer {
    resize: horizontal;
    overflow: auto;
    max-width: 100%;
    min-width: 8rem;
    padding: var(--cw-spacing-small);
    border: dashed 1px var(--cw-color-neutral-400);
  }
`;

const App = () => (
  <>
    <div className="grid-resizer">
      <CwGrid columns="4" minColumnWidth="7rem">
        <div className="grid-box">One</div>
        <div className="grid-box">Two</div>
        <div className="grid-box">Three</div>
        <div className="grid-box">Four</div>
        <div className="grid-box">Five</div>
        <div className="grid-box">Six</div>
      </CwGrid>
    </div>

    <style>{css}</style>
  </>
);

Grid of Cards

Items in a row stretch to the height of the tallest one by default, so cards with different amounts of content still line up.

Components
Accessible, themeable components built on web standards.
Tokens
Color, spacing and typography tokens you can override per theme.
Frameworks
Works anywhere HTML does, with generated wrappers for React and a manifest for page builders that read one.
<cw-grid columns="3" min-column-width="10rem" class="grid-cards">
  <cw-card>
    <strong>Components</strong><br />
    Accessible, themeable components built on web standards.
  </cw-card>

  <cw-card>
    <strong>Tokens</strong><br />
    Color, spacing and typography tokens you can override per theme.
  </cw-card>

  <cw-card>
    <strong>Frameworks</strong><br />
    Works anywhere HTML does, with generated wrappers for React and a manifest for page builders that read one.
  </cw-card>
</cw-grid>

<style>
  .grid-cards cw-card::part(base) {
    height: 100%;
  }
</style>
import CwCard from '@cordwainer/cw-elements/dist/react/card';
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const css = `
  .grid-cards cw-card::part(base) {
    height: 100%;
  }
`;

const App = () => (
  <>
    <CwGrid columns="3" minColumnWidth="10rem" className="grid-cards">
      <CwCard>
        <strong>Components</strong>
        <br />
        Accessible, themeable components built on web standards.
      </CwCard>

      <CwCard>
        <strong>Tokens</strong>
        <br />
        Color, spacing and typography tokens you can override per theme.
      </CwCard>

      <CwCard>
        <strong>Frameworks</strong>
        <br />
        Works anywhere HTML does, with generated wrappers for React and a manifest for page builders that read one.
      </CwCard>
    </CwGrid>

    <style>{css}</style>
  </>
);

Ratio Splits

Set columns to a ratio to size columns in proportion: 2-1, 1-2, 3-1, 1-3, 2-1-1, 1-2-1 or 1-1-2. A 2-1 grid suits a main column beside a sidebar.

Ratio splits stack below 40rem by default (see below), which is wider than this page’s content column, so the ratio examples here lower that threshold to 25rem with --cw-grid-stack-below.

Main content
Sidebar

Aside
Main content
Aside
<cw-grid columns="2-1" style="--cw-grid-stack-below: 25rem;">
  <div class="grid-box">Main content</div>
  <div class="grid-box">Sidebar</div>
</cw-grid>
<br />
<cw-grid columns="1-2-1" style="--cw-grid-stack-below: 25rem;">
  <div class="grid-box">Aside</div>
  <div class="grid-box">Main content</div>
  <div class="grid-box">Aside</div>
</cw-grid>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const App = () => (
  <>
    <CwGrid columns="2-1" style={{ '--cw-grid-stack-below': '25rem' }}>
      <div className="grid-box">Main content</div>
      <div className="grid-box">Sidebar</div>
    </CwGrid>
    <br />
    <CwGrid columns="1-2-1" style={{ '--cw-grid-stack-below': '25rem' }}>
      <div className="grid-box">Aside</div>
      <div className="grid-box">Main content</div>
      <div className="grid-box">Aside</div>
    </CwGrid>
  </>
);

A ratio split doesn’t step down through narrower column counts, since a 2-1 layout squeezed into equal columns would read as a different layout. It stays split until the grid is narrower than --cw-grid-stack-below (40rem by default), then stacks every child into one column, in source order. Resize the box to see it stack below 25rem.

Main content
Sidebar
<div class="grid-resizer">
  <cw-grid columns="2-1" style="--cw-grid-stack-below: 25rem;">
    <div class="grid-box">Main content</div>
    <div class="grid-box">Sidebar</div>
  </cw-grid>
</div>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const App = () => (
  <div className="grid-resizer">
    <CwGrid columns="2-1" style={{ '--cw-grid-stack-below': '25rem' }}>
      <div className="grid-box">Main content</div>
      <div className="grid-box">Sidebar</div>
    </CwGrid>
  </div>
);

With more children than the split has columns, the ratio repeats on each row.

Inside a Narrow Container

Because a grid responds to its own width, the same columns="3" grid that fills a wide page drops to a single column inside a narrow sidebar, even on a wide screen.

One
Two
Three
<div class="grid-sidebar">
  <cw-grid columns="3">
    <div class="grid-box">One</div>
    <div class="grid-box">Two</div>
    <div class="grid-box">Three</div>
  </cw-grid>
</div>

<style>
  .grid-sidebar {
    max-width: 18rem;
    padding: var(--cw-spacing-medium);
    border: solid 1px var(--cw-color-neutral-200);
    border-radius: var(--cw-border-radius-medium);
  }
</style>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const css = `
  .grid-sidebar {
    max-width: 18rem;
    padding: var(--cw-spacing-medium);
    border: solid 1px var(--cw-color-neutral-200);
    border-radius: var(--cw-border-radius-medium);
  }
`;

const App = () => (
  <>
    <div className="grid-sidebar">
      <CwGrid columns="3">
        <div className="grid-box">One</div>
        <div className="grid-box">Two</div>
        <div className="grid-box">Three</div>
      </CwGrid>
    </div>

    <style>{css}</style>
  </>
);

Minimum Column Width

Use min-column-width to set how narrow an equal column can get before the grid drops one. It takes any CSS length. Smaller values keep more columns on narrow screens; larger ones drop columns sooner.

1
2
3
4
5
6
<cw-grid columns="6" min-column-width="4rem">
  <div class="grid-box">1</div>
  <div class="grid-box">2</div>
  <div class="grid-box">3</div>
  <div class="grid-box">4</div>
  <div class="grid-box">5</div>
  <div class="grid-box">6</div>
</cw-grid>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const App = () => (
  <CwGrid columns="6" minColumnWidth="4rem">
    <div className="grid-box">1</div>
    <div className="grid-box">2</div>
    <div className="grid-box">3</div>
    <div className="grid-box">4</div>
    <div className="grid-box">5</div>
    <div className="grid-box">6</div>
  </CwGrid>
);

Gap

Use gap to set the space between rows and columns. It takes a spacing token name, from 3x-small to 4x-large. The default is large.

One
Two
Three

One
Two
Three
<cw-grid columns="3" gap="x-small" min-column-width="8rem">
  <div class="grid-box">One</div>
  <div class="grid-box">Two</div>
  <div class="grid-box">Three</div>
</cw-grid>
<br />
<cw-grid columns="3" gap="3x-large" min-column-width="8rem">
  <div class="grid-box">One</div>
  <div class="grid-box">Two</div>
  <div class="grid-box">Three</div>
</cw-grid>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const App = () => (
  <>
    <CwGrid columns="3" gap="x-small" minColumnWidth="8rem">
      <div className="grid-box">One</div>
      <div className="grid-box">Two</div>
      <div className="grid-box">Three</div>
    </CwGrid>
    <br />
    <CwGrid columns="3" gap="3x-large" minColumnWidth="8rem">
      <div className="grid-box">One</div>
      <div className="grid-box">Two</div>
      <div className="grid-box">Three</div>
    </CwGrid>
  </>
);

Alignment

Use align to position items vertically within their row: stretch (the default), start, center or end.

Short
A taller item
that sets
the row’s height
Short
<cw-grid columns="3" align="center" min-column-width="8rem">
  <div class="grid-box">Short</div>
  <div class="grid-box">A taller item<br />that sets<br />the row's height</div>
  <div class="grid-box">Short</div>
</cw-grid>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const App = () => (
  <CwGrid columns="3" align="center" minColumnWidth="8rem">
    <div className="grid-box">Short</div>
    <div className="grid-box">
      A taller item
      <br />
      that sets
      <br />
      the row's height
    </div>
    <div className="grid-box">Short</div>
  </CwGrid>
);

Spanning Columns

To make one item span more than one equal column, set grid-column on it directly. Spans work against the automatic column drop: an item spanning two columns still asks for two once the grid is down to one, so keep spans for grids that stay wide. Ratio splits don’t support spans.

Spans two columns
One
One
One
One
<cw-grid columns="3" min-column-width="8rem">
  <div class="grid-box" style="grid-column: span 2;">Spans two columns</div>
  <div class="grid-box">One</div>
  <div class="grid-box">One</div>
  <div class="grid-box">One</div>
  <div class="grid-box">One</div>
</cw-grid>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const App = () => (
  <CwGrid columns="3" minColumnWidth="8rem">
    <div className="grid-box" style={{ gridColumn: 'span 2' }}>
      Spans two columns
    </div>
    <div className="grid-box">One</div>
    <div className="grid-box">One</div>
    <div className="grid-box">One</div>
    <div className="grid-box">One</div>
  </CwGrid>
);

Changing the Stack Threshold

Set --cw-grid-stack-below to change where ratio splits stack. Set it on :root to change it for every grid, or on one grid as below, which stays split down to 20rem.

Aside
Main content
<div class="grid-resizer">
  <cw-grid columns="1-2" style="--cw-grid-stack-below: 20rem;">
    <div class="grid-box">Aside</div>
    <div class="grid-box">Main content</div>
  </cw-grid>
</div>
import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

const App = () => (
  <div className="grid-resizer">
    <CwGrid columns="1-2" style={{ '--cw-grid-stack-below': '20rem' }}>
      <div className="grid-box">Aside</div>
      <div className="grid-box">Main content</div>
    </CwGrid>
  </div>
);

Importing

If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.

Script Import Bundler React

To import this component from the CDN using a script tag:

<script type="module" src="https://cdn.jsdelivr.net/npm/@cordwainer/cw-elements@1.6.0/cdn/components/grid/grid.js"></script>

To import this component from the CDN using a JavaScript import:

import 'https://cdn.jsdelivr.net/npm/@cordwainer/cw-elements@1.6.0/cdn/components/grid/grid.js';

To import this component using a bundler:

import '@cordwainer/cw-elements/dist/components/grid/grid.js';

To import this component as a React component:

import CwGrid from '@cordwainer/cw-elements/dist/react/grid';

Slots

Name Description
(default) The grid’s items. Each direct child is one cell, filled in rows from the start.

Learn more about using slots.

Properties

Name Description Reflects Type Default
columns The number of columns. An equal count (1–6) is a maximum: the grid drops to fewer columns whenever a column would be narrower than min-column-width. A ratio split (2-1, 1-2, 3-1, 1-3, 2-1-1, 1-2-1, 1-1-2) sizes its columns in that proportion and stacks them into one column below --cw-grid-stack-below. '1' | '2' | '3' | '4' | '5' | '6' | '2-1' | '1-2' | '3-1' | '1-3' | '2-1-1' | '1-2-1' | '1-1-2' '2'
minColumnWidth
min-column-width
The narrowest an equal column can get before the grid drops a column, as a CSS length. Has no effect on ratio splits. string '15rem'
gap The space between rows and columns, as a spacing token name. '3x-small' | '2x-small' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | '2x-large' | '3x-large' | '4x-large' 'large'
align How items are aligned vertically within their row. stretch makes every item in a row as tall as the tallest, so cards in a row are equal height. 'stretch' | 'start' | 'center' | 'end' 'stretch'
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about attributes and properties.

Custom Properties

Name Description Default
--cw-grid-stack-below Ratio splits stack into a single column when the grid is narrower than this. Set it on :root to change it everywhere, or on one grid. 40rem

Learn more about customizing CSS custom properties.

Parts

Name Description
base The component’s base wrapper, which lays out the slotted items.

Learn more about customizing CSS parts.