Skip to content

Grid

grid makes an element a grid container. Modifier classes on the same element define its columns and rows, how items flow into them and how items align inside their cells; span classes on the items make them cover several tracks.

  • Modifiers sit on the container. cols-3, auto-fit-md, flow-dense and the alignment classes work only next to grid (or a responsive grid@{breakpoint}): class="grid cols-3 gap-4".
  • Spans sit on items. col-span-2 and row-span-2 are standalone classes, so they work in any grid, including one built with mixins.
  • Equal tracks that can shrink. cols-{n} and rows-{n} create tracks of minmax(0, 1fr), so long content does not force a column wider than its share.
  • Gap from the spacing scale. Use gap-*, gap-x-* and gap-y-* for the space between tracks. See Spacing.
Group Classes Property
Container grid, grid inline display: grid, display: inline-grid
Columns and rows cols-1 to cols-12, rows-1 to rows-12 grid-template-columns, grid-template-rows
Automatic columns auto-fit-{size}, auto-fill-{size} grid-template-columns
Flow flow-row, flow-col, flow-dense grid-auto-flow
Item alignment justify-{value}, align-{value}, place-{value} justify-items, align-items, place-items
Spans col-span-1 to col-span-12, row-span-1 to row-span-12 grid-column, grid-row
1
2
3
4
5
6
<div class="grid cols-3 gap-2">
<div class="p-3 bg-brand-subtle rounded">1</div>
<div class="p-3 bg-brand-subtle rounded">2</div>
<div class="p-3 bg-brand-subtle rounded">3</div>
<div class="p-3 bg-brand-subtle rounded">4</div>
<div class="p-3 bg-brand-subtle rounded">5</div>
<div class="p-3 bg-brand-subtle rounded">6</div>
</div>

cols-{n} outputs grid-template-columns: repeat(n, minmax(0, 1fr)); rows-{n} does the same for rows. Rows have an effect when the grid has a height or when items flow into columns.

1
2
3
4
5
6
<div class="grid rows-3 flow-col gap-2">
<div class="p-2 bg-alternate rounded">1</div>
<div class="p-2 bg-alternate rounded">2</div>
<div class="p-2 bg-alternate rounded">3</div>
<div class="p-2 bg-alternate rounded">4</div>
<div class="p-2 bg-alternate rounded">5</div>
<div class="p-2 bg-alternate rounded">6</div>
</div>

For columns of different widths, use the cols-custom() mixin with a quoted track list. There is no class for custom templates.

.layout {
@include apply(grid, cols-custom("250px 1fr"), gap(4));
}
.layout {
display: grid;
grid-template-columns: 250px 1fr;
gap: var(--nu-space-4);
}
.layout > * {
--nu-flex-gap: var(--nu-space-4);
}

Quote the template: an unquoted cols-custom(1fr auto 1fr) reaches the mixin as three arguments. An underscore also works as a space (cols-custom("250px_1fr")).

auto-fit-{size} and auto-fill-{size} create as many columns as fit, each at least the given minimum width. The grid reflows by itself as the container narrows, without breakpoints. The sizes come from $grid-item-sizes; see the class reference for the generated values.

1
2
3
4
<div class="grid auto-fit-xs gap-2">
<div class="p-3 bg-brand-subtle rounded">1</div>
<div class="p-3 bg-brand-subtle rounded">2</div>
<div class="p-3 bg-brand-subtle rounded">3</div>
<div class="p-3 bg-brand-subtle rounded">4</div>
</div>

The difference shows when there are fewer items than columns:

  • auto-fit collapses the empty tracks, so the items stretch to fill the row.
  • auto-fill keeps the empty tracks, so the items keep their column width and the end of the row stays empty.
Two items: auto-fit (top) and auto-fill (bottom)
auto-fit
auto-fit
auto-fill
auto-fill
<div class="space-y-2">
<div class="grid auto-fit-xs gap-2 p-2 bg-alternate rounded">
<div class="p-3 bg-brand-subtle rounded">auto-fit</div>
<div class="p-3 bg-brand-subtle rounded">auto-fit</div>
</div>
<div class="grid auto-fill-xs gap-2 p-2 bg-alternate rounded">
<div class="p-3 bg-brand-subtle rounded">auto-fill</div>
<div class="p-3 bg-brand-subtle rounded">auto-fill</div>
</div>
</div>

The mixins take any minimum width: auto-fit(18rem), auto-fill(min(100%, 250px)). A minimum wrapped in min(100%, ...) keeps a single column from overflowing a container narrower than the minimum.

grid-auto-flow decides where items without an explicit position go.

Class Value Effect
flow-row row Fill each row, then start a new row (the browser default)
flow-col column Fill each column, then start a new column
flow-dense dense Back-fill earlier gaps with later, smaller items
col-span-2
col-span-2
fills the gap
4
<div class="grid cols-3 flow-dense gap-2">
<div class="col-span-2 p-2 bg-brand-subtle rounded">col-span-2</div>
<div class="col-span-2 p-2 bg-brand-subtle rounded">col-span-2</div>
<div class="p-2 bg-alternate rounded">fills the gap</div>
<div class="p-2 bg-alternate rounded">4</div>
</div>

flow-dense changes the visual order of items. Keep it for content where order does not matter, such as a photo wall.

justify-*, align-* and place-* align items inside their cells. They set justify-items, align-items and place-items, and the values are start, end, center and stretch.

Class Property Axis
justify-{value} justify-items Inline axis (along the row)
align-{value} align-items Block axis (down the column)
place-{value} place-items Both axes
justify-start
align-end
place-center
<div class="grid cols-3 gap-2">
<div class="grid justify-start h-24 p-2 bg-alternate rounded"><span class="p-2 bg-brand-subtle rounded">justify-start</span></div>
<div class="grid align-end h-24 p-2 bg-alternate rounded"><span class="p-2 bg-brand-subtle rounded">align-end</span></div>
<div class="grid place-center h-24 p-2 bg-alternate rounded"><span class="p-2 bg-brand-subtle rounded">place-center</span></div>
</div>

col-span-{n} makes an item cover n columns, row-span-{n} covers n rows.

col-span-4: header
row-span-2
col-span-3
col-span-2
1
<div class="grid cols-4 gap-2">
<div class="col-span-4 p-2 bg-brand text-on-brand rounded">col-span-4: header</div>
<div class="row-span-2 p-2 bg-brand-subtle rounded">row-span-2</div>
<div class="col-span-3 p-2 bg-alternate rounded">col-span-3</div>
<div class="col-span-2 p-2 bg-alternate rounded">col-span-2</div>
<div class="p-2 bg-alternate rounded">1</div>
</div>

col-span-{n} outputs grid-column: span n / span n.

Line placement has mixins only: col-start(), col-end(), row-start() and row-end() take a line number, a line name or a span value.

.feature {
@include apply(col-start(2), col-end(4));
}

The placement mixins set grid-column and grid-row directly, for any value those properties accept.

Mixin Output
grid-place-at($col, $row) grid-column: $col; grid-row: $row
grid-col-at($col) grid-column: $col
grid-row-at($row) grid-row: $row
@use "sass:list";
@use "@nuvoui/core" as *;
.hero {
@include grid-place-at(2, span 2);
}
.full-width {
@include grid-col-at(list.slash(1, -1));
}
.hero {
grid-column: 2;
grid-row: span 2;
}
.full-width {
grid-column: 1 / -1;
}

For a start and end pair, pass list.slash(1, -1). A literal 1 / -1 is Sass division and raises a deprecation warning.

grid, cols-{n}, rows-{n}, the alignment classes and the span classes have responsive variants with an @{breakpoint} suffix. auto-fit-*, auto-fill-* and the flow classes do not. Variants apply from their breakpoint upwards.

One column, two from sm, four from lg
1
2
3
4
<div class="grid cols-1 cols-2@sm cols-4@lg gap-2">
<div class="p-3 bg-brand-subtle rounded">1</div>
<div class="p-3 bg-brand-subtle rounded">2</div>
<div class="p-3 bg-brand-subtle rounded">3</div>
<div class="p-3 bg-brand-subtle rounded">4</div>
</div>
SuffixMedia query
@xs@media (min-width: 480px)
@sm@media (min-width: 640px)
@md@media (min-width: 768px)
@lg@media (min-width: 1024px)
@xl@media (min-width: 1280px)
@2xl@media (min-width: 1536px)

Grid classes have no state variants.

Class Mixin
grid, grid inline grid, inline-grid
cols-3, rows-2 cols(3), rows(2)
(no class) cols-custom("250px 1fr")
auto-fit-md, auto-fill-md auto-fit($min-width), auto-fill($min-width)
flow-row, flow-col, flow-dense flow-row, flow-col, flow-dense
justify-center, align-end, place-center justify(center), align(end), place(center)
col-span-2, row-span-2 col-span(2), row-span(2)
(no class) col-start(), col-end(), row-start(), row-end(), grid-place-at(), grid-col-at(), grid-row-at()

justify(), align() and place() accept any value of their property, not only the four class values: place(start end).

1.x 2.0
flow-in-row, flow-in-col flow-row, flow-col
flow-dense-items flow-dense
grid-position() grid-place-at()
position-col(), position-row() grid-col-at(), grid-row-at()
grid-inline inline-grid

A removed name raises Undefined mixin. when included directly and a Mixin not found error inside apply().

$column-count (default 12) sets how many cols-*, rows-*, col-span-* and row-span-* classes are generated. $grid-item-sizes sets the auto-fit-* and auto-fill-* sizes; the map replaces the default map.

@use "@nuvoui/core" with (
$generate-utility-classes: true,
$grid-item-sizes: ("sm": 12rem, "md": 16rem, "lg": 20rem)
);

The grid feature flag owns every grid class: .grid, .grid.inline, the responsive .grid@{breakpoint}, all modifiers, the span classes and .container.grid. Turning off the displays flag does not remove them.

Generated from the compiled core build. Container modifiers are listed with their plain .grid selector; every rule also applies to the responsive .grid@{breakpoint} containers. Responsive variants are not listed.

Container (1 rules)
SelectorDeclarationsDefault value
.griddisplay: gridgrid
Columns and rows (24 rules)
SelectorDeclarationsDefault value
.grid.cols-1grid-template-columns: repeat(1, minmax(0, 1fr))repeat(1, minmax(0, 1fr))
.grid.rows-1grid-template-rows: repeat(1, minmax(0, 1fr))repeat(1, minmax(0, 1fr))
.grid.cols-2grid-template-columns: repeat(2, minmax(0, 1fr))repeat(2, minmax(0, 1fr))
.grid.rows-2grid-template-rows: repeat(2, minmax(0, 1fr))repeat(2, minmax(0, 1fr))
.grid.cols-3grid-template-columns: repeat(3, minmax(0, 1fr))repeat(3, minmax(0, 1fr))
.grid.rows-3grid-template-rows: repeat(3, minmax(0, 1fr))repeat(3, minmax(0, 1fr))
.grid.cols-4grid-template-columns: repeat(4, minmax(0, 1fr))repeat(4, minmax(0, 1fr))
.grid.rows-4grid-template-rows: repeat(4, minmax(0, 1fr))repeat(4, minmax(0, 1fr))
.grid.cols-5grid-template-columns: repeat(5, minmax(0, 1fr))repeat(5, minmax(0, 1fr))
.grid.rows-5grid-template-rows: repeat(5, minmax(0, 1fr))repeat(5, minmax(0, 1fr))
.grid.cols-6grid-template-columns: repeat(6, minmax(0, 1fr))repeat(6, minmax(0, 1fr))
.grid.rows-6grid-template-rows: repeat(6, minmax(0, 1fr))repeat(6, minmax(0, 1fr))
.grid.cols-7grid-template-columns: repeat(7, minmax(0, 1fr))repeat(7, minmax(0, 1fr))
.grid.rows-7grid-template-rows: repeat(7, minmax(0, 1fr))repeat(7, minmax(0, 1fr))
.grid.cols-8grid-template-columns: repeat(8, minmax(0, 1fr))repeat(8, minmax(0, 1fr))
.grid.rows-8grid-template-rows: repeat(8, minmax(0, 1fr))repeat(8, minmax(0, 1fr))
.grid.cols-9grid-template-columns: repeat(9, minmax(0, 1fr))repeat(9, minmax(0, 1fr))
.grid.rows-9grid-template-rows: repeat(9, minmax(0, 1fr))repeat(9, minmax(0, 1fr))
.grid.cols-10grid-template-columns: repeat(10, minmax(0, 1fr))repeat(10, minmax(0, 1fr))
.grid.rows-10grid-template-rows: repeat(10, minmax(0, 1fr))repeat(10, minmax(0, 1fr))
.grid.cols-11grid-template-columns: repeat(11, minmax(0, 1fr))repeat(11, minmax(0, 1fr))
.grid.rows-11grid-template-rows: repeat(11, minmax(0, 1fr))repeat(11, minmax(0, 1fr))
.grid.cols-12grid-template-columns: repeat(12, minmax(0, 1fr))repeat(12, minmax(0, 1fr))
.grid.rows-12grid-template-rows: repeat(12, minmax(0, 1fr))repeat(12, minmax(0, 1fr))
Automatic columns (10 rules)
SelectorDeclarationsDefault value
.grid.auto-fit-xsgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr))repeat(auto-fit, minmax(200px, 1fr))
.grid.auto-fill-xsgrid-template-columns: repeat(auto-fill, minmax(200px, 1fr))repeat(auto-fill, minmax(200px, 1fr))
.grid.auto-fit-smgrid-template-columns: repeat(auto-fit, minmax(250px, 1fr))repeat(auto-fit, minmax(250px, 1fr))
.grid.auto-fill-smgrid-template-columns: repeat(auto-fill, minmax(250px, 1fr))repeat(auto-fill, minmax(250px, 1fr))
.grid.auto-fit-mdgrid-template-columns: repeat(auto-fit, minmax(300px, 1fr))repeat(auto-fit, minmax(300px, 1fr))
.grid.auto-fill-mdgrid-template-columns: repeat(auto-fill, minmax(300px, 1fr))repeat(auto-fill, minmax(300px, 1fr))
.grid.auto-fit-lggrid-template-columns: repeat(auto-fit, minmax(350px, 1fr))repeat(auto-fit, minmax(350px, 1fr))
.grid.auto-fill-lggrid-template-columns: repeat(auto-fill, minmax(350px, 1fr))repeat(auto-fill, minmax(350px, 1fr))
.grid.auto-fit-xlgrid-template-columns: repeat(auto-fit, minmax(400px, 1fr))repeat(auto-fit, minmax(400px, 1fr))
.grid.auto-fill-xlgrid-template-columns: repeat(auto-fill, minmax(400px, 1fr))repeat(auto-fill, minmax(400px, 1fr))
Flow (3 rules)
SelectorDeclarationsDefault value
.grid.flow-rowgrid-auto-flow: rowrow
.grid.flow-colgrid-auto-flow: columncolumn
.grid.flow-densegrid-auto-flow: densedense
Item alignment (12 rules)
SelectorDeclarationsDefault value
.grid.justify-startjustify-items: startstart
.grid.align-startalign-items: startstart
.grid.place-startplace-items: startstart
.grid.justify-endjustify-items: endend
.grid.align-endalign-items: endend
.grid.place-endplace-items: endend
.grid.justify-centerjustify-items: centercenter
.grid.align-centeralign-items: centercenter
.grid.place-centerplace-items: centercenter
.grid.justify-stretchjustify-items: stretchstretch
.grid.align-stretchalign-items: stretchstretch
.grid.place-stretchplace-items: stretchstretch
Spans (24 rules)
SelectorDeclarationsDefault value
.col-span-1grid-column: span 1/span 1span 1/span 1
.row-span-1grid-row: span 1/span 1span 1/span 1
.col-span-2grid-column: span 2/span 2span 2/span 2
.row-span-2grid-row: span 2/span 2span 2/span 2
.col-span-3grid-column: span 3/span 3span 3/span 3
.row-span-3grid-row: span 3/span 3span 3/span 3
.col-span-4grid-column: span 4/span 4span 4/span 4
.row-span-4grid-row: span 4/span 4span 4/span 4
.col-span-5grid-column: span 5/span 5span 5/span 5
.row-span-5grid-row: span 5/span 5span 5/span 5
.col-span-6grid-column: span 6/span 6span 6/span 6
.row-span-6grid-row: span 6/span 6span 6/span 6
.col-span-7grid-column: span 7/span 7span 7/span 7
.row-span-7grid-row: span 7/span 7span 7/span 7
.col-span-8grid-column: span 8/span 8span 8/span 8
.row-span-8grid-row: span 8/span 8span 8/span 8
.col-span-9grid-column: span 9/span 9span 9/span 9
.row-span-9grid-row: span 9/span 9span 9/span 9
.col-span-10grid-column: span 10/span 10span 10/span 10
.row-span-10grid-row: span 10/span 10span 10/span 10
.col-span-11grid-column: span 11/span 11span 11/span 11
.row-span-11grid-row: span 11/span 11span 11/span 11
.col-span-12grid-column: span 12/span 12span 12/span 12
.row-span-12grid-row: span 12/span 12span 12/span 12
  • Logical tracks. Grid columns follow the text direction: the first column is on the right in right-to-left text, and justify-start aligns to the start side.
  • Visual order. flow-dense, spans and placement mixins can show items in a different order from the source. Keyboard focus and screen readers follow the source order (WCAG 1.3.2).
  • Reflow. Prefer auto-fit-* or responsive cols-* so the grid becomes one column on narrow screens and at 400% zoom (WCAG 1.4.10).
LTR
1
2
3
RTL
1
2
3
<div class="grid cols-3 gap-2">
<div class="p-2 bg-brand text-on-brand rounded">1</div>
<div class="p-2 bg-brand-subtle rounded">2</div>
<div class="p-2 bg-brand-subtle rounded">3</div>
</div>