Grid
Responsive CSS grid that adapts its column count to its own width via container queries.
Import
import { Grid } from "@hyzer-labs/ui"Demo
Column counts are container queries against the Grid's own width. A Grid in a
sidebar and one in a hero each pick their own layout, whatever the window is doing. Each
key names the band ending at its width token: sm below 640px, md to 968px, lg to 1200px, xl beyond. Use the slider to change the
grid's width.
md band → 2 columns
<Grid columns={{ sm: 1, md: 2, lg: 3 }}>
<Card>…</Card>
<!-- … -->
</Grid>columns={{ min: … }} fits as many equal tracks as have at least min room, with no breakpoints at all. The band thresholds are fixed
constants, but min takes any length, var() included, so you can drive this mode from tokens and overrides. Reach for it
when the real requirement is "cards at least this wide".
3 columns
<Grid columns={{ min: '12rem' }}>
<Card>…</Card>
<!-- … -->
</Grid>A number gives the same column count at every width, with no responsive behavior.
<Grid columns={2}>
<div>Cell</div>
<!-- … -->
</Grid><Grid columns={3}>
<div>Cell</div>
<!-- … -->
</Grid><Grid columns={4}>
<div>Cell</div>
<!-- … -->
</Grid><Grid columns={6}>
<div>Cell</div>
<!-- … -->
</Grid>near and away are the density distances: context-aware values that
tighten inside data-density-shift regions.
<Grid columns={3} gap="none">
<div>Cell</div>
<!-- … -->
</Grid><Grid columns={3} gap="sm">
<div>Cell</div>
<!-- … -->
</Grid><Grid columns={3} gap="md">
<div>Cell</div>
<!-- … -->
</Grid><Grid columns={3} gap="lg">
<div>Cell</div>
<!-- … -->
</Grid><Grid columns={3} gap="near">
<div>Cell</div>
<!-- … -->
</Grid><Grid columns={3} gap="away">
<div>Cell</div>
<!-- … -->
</Grid>align maps to align-items. You see it when cells in the same
row have different heights; the default stretch makes them equal.
<Grid columns={3} align="start">
<div>Short</div>
<div>Taller cell…</div>
<div>Tallest cell…</div>
</Grid><Grid columns={3} align="center">
<div>Short</div>
<div>Taller cell…</div>
<div>Tallest cell…</div>
</Grid><Grid columns={3} align="end">
<div>Short</div>
<div>Taller cell…</div>
<div>Tallest cell…</div>
</Grid><Grid columns={3} align="stretch">
<div>Short</div>
<div>Taller cell…</div>
<div>Tallest cell…</div>
</Grid><Grid columns={3} align="baseline">
<div>Short</div>
<div>Taller cell…</div>
<div>Tallest cell…</div>
</Grid>The tinted zone is the Grid; the space between its edge and the cells is the padding. It
sits on the grid root, so the column breakpoints measure the padded-down width. padding applies on both axes; paddingInline / paddingBlock override one axis and win where set. The axis names are the
CSS logical properties, so they stay correct in RTL and vertical writing modes. See Spacing & Sizing.
<Grid columns={3} padding="none">…</Grid>
<Grid columns={3} paddingInline="none">…</Grid>
<Grid columns={3} paddingBlock="none">…</Grid><Grid columns={3} padding="sm">…</Grid>
<Grid columns={3} paddingInline="sm">…</Grid>
<Grid columns={3} paddingBlock="sm">…</Grid><Grid columns={3} padding="md">…</Grid>
<Grid columns={3} paddingInline="md">…</Grid>
<Grid columns={3} paddingBlock="md">…</Grid><Grid columns={3} padding="lg">…</Grid>
<Grid columns={3} paddingInline="lg">…</Grid>
<Grid columns={3} paddingBlock="lg">…</Grid><Grid columns={3} padding="near">…</Grid>
<Grid columns={3} paddingInline="near">…</Grid>
<Grid columns={3} paddingBlock="near">…</Grid><Grid columns={3} padding="away">…</Grid>
<Grid columns={3} paddingInline="away">…</Grid>
<Grid columns={3} paddingBlock="away">…</Grid>Props
| Name | Type | Default | Note |
|---|---|---|---|
columns | number | { sm?: number; md?: number; lg?: number; xl?: number } | { min: string } | { sm: 1, md: 2, lg: 3 } | Band keys name the band ending at their width token: sm below 640px, md to 968px, lg to 1200px, xl beyond — fixed system constants, because queries cannot read tokens. { min } is the fluid, fully var-driven mode. |
gap | 'none' | 'sm' | 'md' | 'lg' | 'near' | 'away' | 'md' | near/away are the density distances — they tighten inside data-density-shift regions. |
align | 'start' | 'center' | 'end' | 'stretch' | 'baseline' | 'stretch' | Shared LayoutAlign scale (Stack/Cluster/Grid). |
padding | 'none' | 'sm' | 'md' | 'lg' | 'near' | 'away' | 'none' | Both axes, on the grid root — the column breakpoints measure the padded-down width. Shared LayoutPadding scale. |
paddingInline | 'none' | 'sm' | 'md' | 'lg' | 'near' | 'away' | — | Per-axis override — wins over padding on the inline axis. Same LayoutPadding scale. |
paddingBlock | 'none' | 'sm' | 'md' | 'lg' | 'near' | 'away' | — | Per-axis override — wins over padding on the block axis. Same LayoutPadding scale. |
as | string | 'div' | |
class | string | — | Merged after the hz-grid class. |
children | Snippet | — |
Theme hooks
What this component promises your CSS. The reference theme styles exactly these — from @layer hz-theme, so your unlayered rules win. See Styling Components for the how.
Root class: .hz-grid
Data attributes
| Hook | Values | Styles |
|---|---|---|
data-gap | 'none' | 'sm' | 'md' | 'lg' | 'near' | 'away' | Track gap. Default md. |
data-align | 'start' | 'center' | 'end' | 'stretch' | 'baseline' | align-items. Default stretch. |
data-padding | 'none' | 'sm' | 'md' | 'lg' | 'near' | 'away' | Padding, applied to the root so container queries measure the space the tracks actually get. Default none. |
data-fluid | present in fluid mode | Read-only: present when columns is {min}. Target it to style fluid grids apart from banded ones — the mode is set by the prop, not the attribute. |
Part classes
| Hook | Values | Styles |
|---|---|---|
.hz-grid-layout | child element | The real grid. The root is a size container only (an element cannot container-query itself), so track and gap rules live here. |
Accessibility
Grid is a layout primitive with no ARIA semantics. Reading and focus order follow DOM order.