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.
How grid works
Section titled “How grid works”- Modifiers sit on the container.
cols-3,auto-fit-md,flow-denseand the alignment classes work only next togrid(or a responsivegrid@{breakpoint}):class="grid cols-3 gap-4". - Spans sit on items.
col-span-2androw-span-2are standalone classes, so they work in any grid, including one built with mixins. - Equal tracks that can shrink.
cols-{n}androws-{n}create tracks ofminmax(0, 1fr), so long content does not force a column wider than its share. - Gap from the spacing scale. Use
gap-*,gap-x-*andgap-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 |
Columns and rows
Section titled “Columns and rows”<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>.gallery { @include apply(grid, cols(3), gap(2));}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.
<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>Custom templates
Section titled “Custom templates”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")).
Automatic columns: auto-fit and auto-fill
Section titled “Automatic columns: auto-fit and auto-fill”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.
<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>.cards { @include apply(grid, auto-fit(250px), gap(4));}The difference shows when there are fewer items than columns:
auto-fitcollapses the empty tracks, so the items stretch to fill the row.auto-fillkeeps the empty tracks, so the items keep their column width and the end of the row stays empty.
<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 |
<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>.masonry { @include apply(grid, cols(3), flow-dense);}flow-dense changes the visual order of items. Keep it for content where order does not matter, such as a photo wall.
Item alignment (grid only)
Section titled “Item alignment (grid only)”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 |
<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>.empty-state { @include apply(grid, place(center), min-height(48));}Spanning tracks
Section titled “Spanning tracks”col-span-{n} makes an item cover n columns, row-span-{n} covers n rows.
<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>.dashboard__chart { @include apply(col-span(2), row-span(2));}col-span-{n} outputs grid-column: span n / span n.
Start and end lines
Section titled “Start and end lines”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));}grid-place-at, grid-col-at, grid-row-at
Section titled “grid-place-at, grid-col-at, grid-row-at”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.
Responsive variants
Section titled “Responsive variants”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.
<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>.gallery { @include apply(grid, cols(1), cols(2) sm, cols(4) lg, gap(2));}| Suffix | Media 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).
Renamed and removed in 2.0
Section titled “Renamed and removed in 2.0”| 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().
Configuration
Section titled “Configuration”$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.
Class reference
Section titled “Class reference”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)
| Selector | Declarations | Default value |
|---|---|---|
.grid | display: grid | grid |
Columns and rows (24 rules)
| Selector | Declarations | Default value |
|---|---|---|
.grid.cols-1 | grid-template-columns: repeat(1, minmax(0, 1fr)) | repeat(1, minmax(0, 1fr)) |
.grid.rows-1 | grid-template-rows: repeat(1, minmax(0, 1fr)) | repeat(1, minmax(0, 1fr)) |
.grid.cols-2 | grid-template-columns: repeat(2, minmax(0, 1fr)) | repeat(2, minmax(0, 1fr)) |
.grid.rows-2 | grid-template-rows: repeat(2, minmax(0, 1fr)) | repeat(2, minmax(0, 1fr)) |
.grid.cols-3 | grid-template-columns: repeat(3, minmax(0, 1fr)) | repeat(3, minmax(0, 1fr)) |
.grid.rows-3 | grid-template-rows: repeat(3, minmax(0, 1fr)) | repeat(3, minmax(0, 1fr)) |
.grid.cols-4 | grid-template-columns: repeat(4, minmax(0, 1fr)) | repeat(4, minmax(0, 1fr)) |
.grid.rows-4 | grid-template-rows: repeat(4, minmax(0, 1fr)) | repeat(4, minmax(0, 1fr)) |
.grid.cols-5 | grid-template-columns: repeat(5, minmax(0, 1fr)) | repeat(5, minmax(0, 1fr)) |
.grid.rows-5 | grid-template-rows: repeat(5, minmax(0, 1fr)) | repeat(5, minmax(0, 1fr)) |
.grid.cols-6 | grid-template-columns: repeat(6, minmax(0, 1fr)) | repeat(6, minmax(0, 1fr)) |
.grid.rows-6 | grid-template-rows: repeat(6, minmax(0, 1fr)) | repeat(6, minmax(0, 1fr)) |
.grid.cols-7 | grid-template-columns: repeat(7, minmax(0, 1fr)) | repeat(7, minmax(0, 1fr)) |
.grid.rows-7 | grid-template-rows: repeat(7, minmax(0, 1fr)) | repeat(7, minmax(0, 1fr)) |
.grid.cols-8 | grid-template-columns: repeat(8, minmax(0, 1fr)) | repeat(8, minmax(0, 1fr)) |
.grid.rows-8 | grid-template-rows: repeat(8, minmax(0, 1fr)) | repeat(8, minmax(0, 1fr)) |
.grid.cols-9 | grid-template-columns: repeat(9, minmax(0, 1fr)) | repeat(9, minmax(0, 1fr)) |
.grid.rows-9 | grid-template-rows: repeat(9, minmax(0, 1fr)) | repeat(9, minmax(0, 1fr)) |
.grid.cols-10 | grid-template-columns: repeat(10, minmax(0, 1fr)) | repeat(10, minmax(0, 1fr)) |
.grid.rows-10 | grid-template-rows: repeat(10, minmax(0, 1fr)) | repeat(10, minmax(0, 1fr)) |
.grid.cols-11 | grid-template-columns: repeat(11, minmax(0, 1fr)) | repeat(11, minmax(0, 1fr)) |
.grid.rows-11 | grid-template-rows: repeat(11, minmax(0, 1fr)) | repeat(11, minmax(0, 1fr)) |
.grid.cols-12 | grid-template-columns: repeat(12, minmax(0, 1fr)) | repeat(12, minmax(0, 1fr)) |
.grid.rows-12 | grid-template-rows: repeat(12, minmax(0, 1fr)) | repeat(12, minmax(0, 1fr)) |
Automatic columns (10 rules)
| Selector | Declarations | Default value |
|---|---|---|
.grid.auto-fit-xs | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) | repeat(auto-fit, minmax(200px, 1fr)) |
.grid.auto-fill-xs | grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)) | repeat(auto-fill, minmax(200px, 1fr)) |
.grid.auto-fit-sm | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) | repeat(auto-fit, minmax(250px, 1fr)) |
.grid.auto-fill-sm | grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)) | repeat(auto-fill, minmax(250px, 1fr)) |
.grid.auto-fit-md | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) | repeat(auto-fit, minmax(300px, 1fr)) |
.grid.auto-fill-md | grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)) | repeat(auto-fill, minmax(300px, 1fr)) |
.grid.auto-fit-lg | grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)) | repeat(auto-fit, minmax(350px, 1fr)) |
.grid.auto-fill-lg | grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)) | repeat(auto-fill, minmax(350px, 1fr)) |
.grid.auto-fit-xl | grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)) | repeat(auto-fit, minmax(400px, 1fr)) |
.grid.auto-fill-xl | grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)) | repeat(auto-fill, minmax(400px, 1fr)) |
Flow (3 rules)
| Selector | Declarations | Default value |
|---|---|---|
.grid.flow-row | grid-auto-flow: row | row |
.grid.flow-col | grid-auto-flow: column | column |
.grid.flow-dense | grid-auto-flow: dense | dense |
Item alignment (12 rules)
| Selector | Declarations | Default value |
|---|---|---|
.grid.justify-start | justify-items: start | start |
.grid.align-start | align-items: start | start |
.grid.place-start | place-items: start | start |
.grid.justify-end | justify-items: end | end |
.grid.align-end | align-items: end | end |
.grid.place-end | place-items: end | end |
.grid.justify-center | justify-items: center | center |
.grid.align-center | align-items: center | center |
.grid.place-center | place-items: center | center |
.grid.justify-stretch | justify-items: stretch | stretch |
.grid.align-stretch | align-items: stretch | stretch |
.grid.place-stretch | place-items: stretch | stretch |
Spans (24 rules)
| Selector | Declarations | Default value |
|---|---|---|
.col-span-1 | grid-column: span 1/span 1 | span 1/span 1 |
.row-span-1 | grid-row: span 1/span 1 | span 1/span 1 |
.col-span-2 | grid-column: span 2/span 2 | span 2/span 2 |
.row-span-2 | grid-row: span 2/span 2 | span 2/span 2 |
.col-span-3 | grid-column: span 3/span 3 | span 3/span 3 |
.row-span-3 | grid-row: span 3/span 3 | span 3/span 3 |
.col-span-4 | grid-column: span 4/span 4 | span 4/span 4 |
.row-span-4 | grid-row: span 4/span 4 | span 4/span 4 |
.col-span-5 | grid-column: span 5/span 5 | span 5/span 5 |
.row-span-5 | grid-row: span 5/span 5 | span 5/span 5 |
.col-span-6 | grid-column: span 6/span 6 | span 6/span 6 |
.row-span-6 | grid-row: span 6/span 6 | span 6/span 6 |
.col-span-7 | grid-column: span 7/span 7 | span 7/span 7 |
.row-span-7 | grid-row: span 7/span 7 | span 7/span 7 |
.col-span-8 | grid-column: span 8/span 8 | span 8/span 8 |
.row-span-8 | grid-row: span 8/span 8 | span 8/span 8 |
.col-span-9 | grid-column: span 9/span 9 | span 9/span 9 |
.row-span-9 | grid-row: span 9/span 9 | span 9/span 9 |
.col-span-10 | grid-column: span 10/span 10 | span 10/span 10 |
.row-span-10 | grid-row: span 10/span 10 | span 10/span 10 |
.col-span-11 | grid-column: span 11/span 11 | span 11/span 11 |
.row-span-11 | grid-row: span 11/span 11 | span 11/span 11 |
.col-span-12 | grid-column: span 12/span 12 | span 12/span 12 |
.row-span-12 | grid-row: span 12/span 12 | span 12/span 12 |
Accessibility and RTL
Section titled “Accessibility and RTL”- Logical tracks. Grid columns follow the text direction: the first column is on the right in right-to-left text, and
justify-startaligns 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 responsivecols-*so the grid becomes one column on narrow screens and at 400% zoom (WCAG 1.4.10).
<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>