Skip to main content

Design system components

Grid

CSS grid layout primitive with scalar and token-breakpoint responsive columns and gaps.

Status: stable. Group: layout. Markdown version: /design-system/components/grid.md

When to use

Grid is the two-dimensional layout primitive: pass columns as a number for equal tracks or a template string for custom ones, then add tabletColumns/desktopColumns/wideColumns/ultraColumns and matching gap props when the layout changes at the 768/1024/1440/1920 token breakpoints. Sparse responsive values inherit through the previous rung, and numeric responsive tracks use minmax(0, 1fr). Give spanning children span/rowSpan, usually through Grid.Item; Grid reads and strips those two props from every direct element child, so a child component's own span or rowSpan prop never reaches it. Use Stack or FlexBox when only one axis matters.

When not to use

  • Do not use Grid for single-axis flows; Stack (token gaps) or FlexBox (free-form) read clearer and cost less.
  • Do not size cells by styling children with widths; spans and templates belong on the grid, not in the card.
  • Do not use Grid for single-axis flows; Stack (token gaps) or FlexBox (free-form) read clearer and cost less.

Look-alikes

The contract names no look-alike with a deciding condition.

Props

Grid props
PropTypeDefaultDescriptionRequired
childrenReactNodenoneGrid cells (supports span on child props).Yes
columnsnumber | string1Column count or grid-template-columns string. Defaults to 1.No
tabletColumnsnumber | stringnoneColumns from the tablet breakpoint (768px) up. Numeric counts render as repeat(n, minmax(0, 1fr)) so cells can shrink below content width.No
desktopColumnsnumber | stringnoneColumns from the desktop breakpoint (1024px) up. Falls back to tabletColumns, then columns.No
wideColumnsnumber | stringnoneColumns from the wide breakpoint (1440px) up. Falls back through desktopColumns, tabletColumns, columns.No
ultraColumnsnumber | stringnoneColumns from the ultra breakpoint (1920px) up. Falls back through wideColumns, desktopColumns, tabletColumns, columns.No
rowsnumber | stringnoneRow count or grid-template-rows string.No
gapstring1remGrid gap. Defaults to the raw value 1rem; pass a space token such as var(--space-layout-24) instead.No
tabletGapstringnoneGap from the tablet breakpoint (768px) up.No
desktopGapstringnoneGap from the desktop breakpoint (1024px) up. Falls back to tabletGap, then gap.No
wideGapstringnoneGap from the wide breakpoint (1440px) up. Falls back through desktopGap, tabletGap, gap.No
ultraGapstringnoneGap from the ultra breakpoint (1920px) up. Falls back through wideGap, desktopGap, tabletGap, gap.No
rowGapstringnoneRow gap override.No
colGapstringnoneColumn gap override.No
align"inherit" | "center" | "flex-start" | "flex-end" | "stretch" | "baseline" | "-moz-initial" | "initial" | "revert" | "revert-layer" | "unset" | "end" | "self-end" | "self-start" | "start" | "anchor-center" | "normal"normalAlign-items. No default: when omitted Grid sets no align-items, so the CSS initial value (normal) applies.No
justify"inherit" | "center" | "flex-start" | "flex-end" | "stretch" | "baseline" | "-moz-initial" | "initial" | "revert" | "revert-layer" | "unset" | "end" | "self-end" | "self-start" | "start" | "anchor-center" | "normal" | "left" | "legacy" | "right"noneJustify-items. No default: when omitted Grid sets no justify-items.No
styleCSSProperties{}No description in the contract.No
classNamestring""Grid class names.No

Variants

  • Allowed align values: inherit, center, flex-start, flex-end, stretch, baseline, -moz-initial, initial, revert, revert-layer, unset, end, self-end, self-start, start, anchor-center, normal (default normal).
  • Allowed justify values: inherit, center, flex-start, flex-end, stretch, baseline, -moz-initial, initial, revert, revert-layer, unset, end, self-end, self-start, start, anchor-center, normal, left, legacy, right.

Invalid combinations

The contract records no invalid combinations.

Keyboard

The contract records no keyboard interaction.

Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

Grid rules
LevelRuleHow it is checked
MUSTRequired stories produce no axe violations at the configured severity.Automated by npm run test:stories:matrix:ci. WCAG 2.5.8.
MUSTThe accessibility tree (role, name, state) matches the committed snapshot for every required story.Automated by npm run test:stories:matrix:ci. WCAG 1.3.1, 4.1.2.
MUSTThe component remains legible and operable under a real browser forced-colors: active pass.Automated by npm run test:stories:hc.
MUSTA human completed an end-to-end accessibility review of this component.Manual review, last on 2026-11-04.
SHOULDUse numeric columns for equal tracks and a template string (e.g. "2fr 1fr") only when tracks genuinely differ.Not stated in the contract.
SHOULDWrap only the children that span in Grid.Item; plain children flow one per cell without a wrapper.Not stated in the contract.
SHOULDSet gaps with space tokens ("var(--space-layout-24)") so grid gutters follow the page rhythm.Not stated in the contract.
SHOULDSet only the responsive rungs that change; omitted tablet, desktop, wide, or ultra values inherit through the previous rung.Not stated in the contract.
SHOULDGive featured cards span={2} instead of a wider one-off column template.Not stated in the contract.
SHOULD NOTDo not size cells by styling children with widths; spans and templates belong on the grid, not in the card.Not stated in the contract.
SHOULD NOTDo not place a component that has its own span or rowSpan prop directly inside Grid; Grid strips those props and turns them into grid spans.Not stated in the contract.

Install and import

  • Install: npm install @digitaltableteur/react (version 0.9.1, restricted access on npm).
  • Import: import { Grid } from "@digitaltableteur/react";
  • Entry point: @digitaltableteur/react/layout exports it too.
  • Styles: import @digitaltableteur/react/style.css once at the app boundary.
  • Also available as the custom element <dt-grid>; see the custom elements manifest.