Grid placement & spanning
Grid items can be placed explicitly in specific cells or allowed to auto-place. The grid-column and grid-row properties specify which columns and rows an item spans. Items can span multiple cells, creating complex layouts. Understanding placement is key to harnessing grid's power.
Auto-placement
By default, grid items automatically fill cells in row order. You can control this with grid-auto-flow.
<!-- Items auto-place in reading order -->
<div class="grid">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
/* 3 columns, rows auto-create -->
}
<!-- Auto-placement flow: row (default) -->
[1] [2] [3]
[4] [5] [6]
.grid {
grid-auto-flow: column; /* fill columns first -->
}
<!-- Auto-placement flow: column -->
[1] [4]
[2] [5]
[3] [6]
.grid {
grid-auto-flow: dense; /* fill gaps with smaller items -->
}
<!-- Tries to prevent gaps for better space usage -->grid-column and grid-row
Property | Syntax | Example | Result |
|---|---|---|---|
| start / end | 1 / 3 | Span columns 1-2 |
| start / end | 1 / 3 | Span rows 1-2 |
| start / span | 2 / span 2 | Span 2 columns from 2 |
| start / -1 | 1 / -1 | Span to grid end |
<!-- Three-column grid -->
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
<!-- Position item in specific cell -->
.item {
grid-column: 2; /* place in column 2 -->
grid-row: 1; /* place in row 1 -->
}
<!-- Span multiple cells -->
.item {
grid-column: 1 / 3; /* span columns 1-2 -->
grid-row: 1 / 3; /* span rows 1-2 -->
}
<!-- Span using span keyword -->
.item {
grid-column: 1 / span 2; /* start column 1, span 2 columns -->
grid-row: 1 / span 3; /* start row 1, span 3 rows -->
}
<!-- Span to the end -->
.item {
grid-column: 1 / -1; /* span from column 1 to grid end -->
grid-row: 1 / -1; /* span from row 1 to grid end -->
}
<!-- Only column (row auto-places) -->
.item {
grid-column: 2;
/* grid-row: auto (automatic) */
}Grid areas with named cells
Named grid areas provide a semantic way to define layout regions and place items.
<!-- Define layout with named areas -->
<div class="app">
<header>Header</header>
<nav>Navigation</nav>
<main>Main Content</main>
<aside>Sidebar</aside>
<footer>Footer</footer>
</div>
.app {
display: grid;
grid-template-columns: 200px 1fr 250px;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
"header header header"
"nav main sidebar"
"footer footer footer";
gap: 20px;
}
header {
grid-area: header;
}
nav {
grid-area: nav;
}
main {
grid-area: main;
}
aside {
grid-area: sidebar;
}
footer {
grid-area: footer;
}
<!-- Result -->
[════════ Header ════════]
[Nav] [Main] [Sidebar]
[════════ Footer ════════]
<!-- Responsive: change areas on mobile -->
@media (max-width: 768px) {
.app {
grid-template-columns: 1fr;
grid-template-rows: auto auto auto auto auto;
grid-template-areas:
"header"
"nav"
"main"
"sidebar"
"footer";
}
}
<!-- Result on mobile -->
[Header]
[Nav]
[Main]
[Sidebar]
[Footer]Practical spanning patterns
<!-- Magazine-style layout with featured article -->
<div class="magazine">
<article class="featured">Featured</article>
<article>Article 1</article>
<article>Article 2</article>
<article>Article 3</article>
<article>Article 4</article>
</div>
.magazine {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto auto auto;
gap: 20px;
}
.featured {
grid-column: 1 / 3; /* span 2 columns -->
grid-row: 1 / 3; /* span 2 rows -->
}
<!-- Result -->
[═════Featured═════] [Article 1]
[═════Featured═════] [Article 2]
[Article 3] [Article 4]
<!-- Dashboard with larger widget -->
<div class="dashboard">
<div class="widget main">Main Widget</div>
<div class="widget">Widget 2</div>
<div class="widget">Widget 3</div>
<div class="widget">Widget 4</div>
</div>
.dashboard {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-auto-rows: 200px;
gap: 15px;
}
.widget {
background: white;
border: 1px solid #eeeeee;
padding: 20px;
border-radius: 8px;
}
.widget.main {
grid-column: span 6; /* take 6 columns -->
}
.widget {
grid-column: span 3; /* take 3 columns -->
}
<!-- Result: main widget is 2×2, others are 1×1 -->Dense packing to avoid gaps
<!-- Without dense: gaps created -->
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: row; /* row by row -->
}
.item:nth-child(1) {
grid-column: span 2;
}
<!-- [═══Item 1═══] [───]
[Item 2] [Item 3] [Item 4]
[Item 5] [Item 6] -->
<!-- With dense: fills gaps -->
.grid {
grid-auto-flow: dense; /* rearrange to fill gaps -->
}
<!-- [═══Item 1═══] [Item 5]
[Item 2] [Item 3] [Item 4]
[Item 6] -->
<!-- Item 5 moves up to fill gap -->Negative positioning with -1
Use -1 to reference the grid end, useful for spanning to the end without knowing total columns.
<!-- Span from column 1 to grid end -->
.full-width {
grid-column: 1 / -1; /* automatically spans entire width -->
}
<!-- Works regardless of how many columns exist -->
<!-- Span from row 2 to grid end -->
.tall {
grid-row: 2 / -1;
}
<!-- Practical: auto-sizing footer -->
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr auto;
}
footer {
grid-column: 1 / -1; /* always spans full width -->
grid-row: 3;
}