Grid
A CSS grid layout with responsive column support. Accepts a compact string syntax for breakpoint-based column counts.
Basic Usage
Responsive Columns
Pass a space-separated string with breakpoint prefixes. The syntax mirrors Tailwind's responsive convention.
Gap Sizes
Alignment
Semantic Elements
Responsive Syntax
The cols prop accepts a space-separated string where each token is either a plain number (base columns) or a breakpoint:number pair:
cols="1" → grid-cols-1
cols="1 sm:2 lg:3" → grid-cols-1 sm:grid-cols-2 lg:grid-cols-3
cols="2 xl:4" → grid-cols-2 xl:grid-cols-4
Supported breakpoints follow Tailwind's defaults: sm, md, lg, xl, 2xl.
Columns That Follow the Content
cols counts columns against the viewport. min counts them against the container: you give the narrowest a column may be, and as many fit as fit — one on a narrow screen.
The difference is invisible until the container is narrower than the window. Open a sidebar, use a split view, drop the grid inside a card, and a cols="1 md:2 lg:3" grid keeps rendering three columns because the window is still wide. A min grid reflows, because it is measuring the box the cards are actually in.
This is the ordinary responsive card grid, so reach for it first and keep cols for the cases where the column count is genuinely a design decision per breakpoint.
Sessions
1,284 this week
Signups
96 this week
Revenue
€ 18,400
Churn
1.2 %
min takes any CSS length — 14rem, 20ch, 280px. A column never overflows its container: below its own minimum the grid drops to a single column instead.
min and template ride in an inline style, so a strict Content-Security-Policy can remove them. Both take arbitrary values, and an arbitrary value cannot become a class: Tailwind extracts class names from source text, and a track built at runtime leaves its scanner nothing to find. Under a style-src policy without 'unsafe-inline' — Level 3 spells it style-src-attr — the browser drops the attribute.
Elsewhere a dropped inline style costs a shade or a width. Here it costs the whole statement: with no track and no columns, the grid stacks into a single column.
Pass cols alongside the track prop and that is what you fall back to:
{{-- The track when the policy allows it; three equal columns when it does not. --}}
<x-wirekit::grid cols="3" min="14rem"> … </x-wirekit::grid>
The inline style wins on specificity whenever it is allowed, so the cols classes change nothing about what you normally see — they decide what is left when the style never arrives. Approximate by construction, and far better than one column.
Columns of Different Widths
cols only knows equal columns. The two commonest application layouts are neither: the three-pane workspace (navigation, content, detail) and the week grid (a time axis plus seven days). template takes an explicit track list and hands it to CSS as written.
Navigation
Content — this pane takes whatever is left
Detail
Anything grid-template-columns accepts works: lengths, fr, auto, min-content, max-content, minmax(), repeat(), fit-content().
template is not responsive, and that is what asking for exact tracks means. Unlike min, it is handed to CSS as written, so a fixed track is that wide at every viewport. The example above reserves 10rem + 8rem before the content pane gets anything: on a 390 px phone that leaves the middle column a few characters wide and pushes the grid past the screen edge. Nothing warns you — it renders, it is simply unreadable.
Do not reach for template as a general-purpose column prop. It is for layouts whose track widths are the design: the three-pane workspace, the week grid. Below the width those layouts need, switch to something that folds:
{{-- One column on a phone, the workspace from `lg` up. --}}
<div class="lg:hidden"><x-wirekit::stack gap="sm">…</x-wirekit::stack></div>
<div class="hidden lg:block"><x-wirekit::grid template="10rem 1fr 8rem" gap="sm">…</x-wirekit::grid></div>
If every track can flex, prefer min — it collapses to one column instead of overflowing.
template="14rem 1fr 18rem" → navigation | content | detail
template="4.5rem repeat(7, minmax(0, 1fr))" → time axis + seven days
template="1fr 1fr 1fr auto" → three equal columns and a trailing action
Which of the three to use
| You want | Prop |
|---|---|
| A specific column count per breakpoint | cols |
| As many columns as fit, at least this wide | min |
| Columns of different, named widths | template |
Only one column track can exist, so the props do not combine: template wins over min, and min wins over cols.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
cols |
string|int | 1 |
Column count. Integer or responsive string: "1 sm:2 lg:3" |
min |
string|null | null |
Minimum column width for a content-driven grid: "14rem". Wins over cols |
template |
string|null | null |
Explicit column track list: "14rem 1fr 18rem". Wins over min and cols |
gap |
string | 'md' |
Spacing: none, xs, sm, md, lg, xl, 2xl |
align |
string|null | null |
Vertical alignment: start, center, end, stretch |
as |
string | 'div' |
HTML element to render |
scope |
string|null | null |
Scoped personalization name |
The rung names are shared with a second ladder, and the values are not. gap reads --space-wk-*; the tighter --gap-wk-* ladder uses the same xs…2xl labels with different values from md up, and WireKit's own components read it for spacing inside a component. Swapping one for the other by name silently re-spaces the container — see Two spacing ladders, same rung names.
Keyboard Interaction
This component is a layout wrapper. Keyboard interaction is delegated to its children.