By default, CSS Grid auto-places items into cells left to right, top to bottom. But Grid’s real superpower is the ability to place items at precise coordinates โ spanning multiple columns or rows โ without changing the HTML structure. In this lesson you will master grid-column, grid-row, and the span keyword to build complex layouts where individual items occupy exactly the space they need.
Grid Placement Properties
| Property | Syntax | Meaning |
|---|---|---|
grid-column |
grid-column: 1 / 3 |
Start at line 1, end at line 3 (spans 2 columns) |
grid-row |
grid-row: 2 / 4 |
Start at row line 2, end at row line 4 (spans 2 rows) |
grid-column with span |
grid-column: 2 / span 3 |
Start at line 2, span 3 columns forward |
grid-column-start |
grid-column-start: 2 |
Longhand โ start column line only |
grid-column-end |
grid-column-end: -1 |
End at the last column line (regardless of column count) |
grid-area |
grid-area: 1 / 1 / 3 / 4 |
Shorthand: row-start / col-start / row-end / col-end |
Line Numbering
| Reference | Meaning | Example |
|---|---|---|
| Positive number | Count from the start (left/top) | grid-column: 1 โ first column line |
| Negative number | Count from the end (right/bottom) | grid-column-end: -1 โ last column line |
span N |
Span N tracks from the start position | grid-column: 1 / span 2 โ two columns wide |
auto |
Let the grid auto-place this dimension | grid-column: auto / span 2 โ auto-placed, 2 wide |
Auto Placement Algorithm
| Property | Value | Effect |
|---|---|---|
grid-auto-flow |
row (default) |
Items fill left to right, wrapping to next row |
grid-auto-flow |
column |
Items fill top to bottom, then move to next column |
grid-auto-flow |
row dense |
Fills gaps left by spanning items โ order may change |
grid-auto-rows |
200px or minmax(100px, auto) |
Size of implicitly created row tracks |
grid-column: 1 / -1 means “start at the very first line, end at the very last line” โ a reliable way to make an item span the full grid width regardless of how many columns exist.span instead of explicit end lines when you know how many tracks to span but don’t want to hardcode which line the item ends on. grid-column: span 2 means “take two columns, starting wherever auto-placement puts me” โ perfect for items that need extra width without a fixed position.grid-column and grid-row, auto-placed items fill in around them. Items placed at the same cell overlap by default โ the later item in the DOM paints on top. Use z-index on grid items (they create stacking contexts) to control overlap order.Basic Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Grid Item Placement</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; padding: 32px; background: #f8fafc; }
/* โโ Magazine-style asymmetric layout โโ */
.magazine-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, 160px);
gap: 12px;
margin-bottom: 32px;
}
.item {
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
font-weight: 700;
color: white;
text-align: center;
padding: 12px;
}
/* Hero: spans 2 columns, 2 rows */
.hero {
grid-column: 1 / span 2;
grid-row: 1 / span 2;
background: #4f46e5;
font-size: 1rem;
}
/* Wide feature: spans all 4 columns */
.wide {
grid-column: 1 / -1; /* -1 = last line regardless of column count */
background: #0891b2;
}
/* Tall sidebar: spans 2 rows */
.tall {
grid-row: 1 / span 2;
background: #7c3aed;
}
.item-a { background: #10b981; }
.item-b { background: #f59e0b; }
.item-c { background: #ef4444; }
/* โโ dense fill demo โโ */
.dense-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 80px;
grid-auto-flow: row dense; /* fills gaps left by spanning items */
gap: 12px;
}
.span2 { grid-column: span 2; background: #4f46e5; }
.span3 { grid-column: span 3; background: #7c3aed; }
.reg { background: #0891b2; }
</style>
</head>
<body>
<h3 style="font-size:0.8rem;text-transform:uppercase;letter-spacing:.06em;color:#64748b;margin-bottom:8px;">Magazine grid โ explicit placement</h3>
<div class="magazine-grid">
<div class="item hero">Hero<br>col 1-2 / row 1-2</div>
<div class="item tall">Tall<br>col 3 / row 1-2</div>
<div class="item item-a">col 4 / row 1</div>
<div class="item item-b">col 4 / row 2</div>
<div class="item wide">Full-width โ grid-column: 1 / -1</div>
</div>
<h3 style="font-size:0.8rem;text-transform:uppercase;letter-spacing:.06em;color:#64748b;margin-bottom:8px;">dense fill โ gaps filled automatically</h3>
<div class="dense-grid">
<div class="item span3">span 3</div>
<div class="item reg">1</div>
<div class="item span2">span 2</div>
<div class="item reg">2</div>
<div class="item reg">3</div>
<div class="item reg">4</div>
<div class="item span2">span 2</div>
<div class="item reg">5</div>
</div>
</body>
</html>
How It Works
Step 1 โ Hero Spans 2 Columns and 2 Rows
grid-column: 1 / span 2 places the hero starting at column line 1 and spanning 2 column tracks (ending at line 3). grid-row: 1 / span 2 does the same vertically โ the hero occupies a 2ร2 block of grid cells while other items auto-place around it.
Step 2 โ grid-column: 1 / -1 Spans the Full Width
Negative line numbers count from the end. In a 4-column grid, line -1 is line 5 โ the last column line. grid-column: 1 / -1 always spans the full grid width regardless of column count, making it robust when the number of columns changes.
Step 3 โ Tall Item Spans Two Rows
grid-row: 1 / span 2 without an explicit column placement lets the grid auto-place it into column 3 (since columns 1-2 are taken by the hero). The browser’s auto-placement algorithm finds the next available cell โ column 3, row 1 โ and the span extends it down through row 2.
Step 4 โ grid-auto-flow: dense Fills Gaps
With dense packing, the browser backtracks to fill gaps left by spanning items. A single-cell item that comes after a 3-wide item can be placed in the gap on the same row. This reorders items visually โ the DOM order is not preserved, which can be an accessibility concern.
Step 5 โ Overlapping Items Use z-index
When two items are placed at the same grid coordinates, they overlap. The later item in the DOM paints on top by default. Adding z-index to grid items explicitly controls which one appears on top โ grid items automatically participate in stacking.
Real-World Example: Editorial Photo Gallery
/* gallery.css */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #0f172a; padding: 24px; }
.gallery {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-auto-rows: 200px;
gap: 8px;
max-width: 1100px;
margin: 0 auto;
}
.photo {
border-radius: 8px;
overflow: hidden;
position: relative;
background: #1e293b;
cursor: pointer;
}
/* Variation: wide feature */
.photo-wide { grid-column: span 3; }
/* Variation: tall portrait */
.photo-tall { grid-row: span 2; }
/* Variation: large feature */
.photo-large { grid-column: span 3; grid-row: span 2; }
/* Variation: small square */
.photo-small { grid-column: span 2; }
.photo img {
width: 100%; height: 100%;
object-fit: cover;
display: block;
transition: transform 0.3s ease;
}
.photo:hover img { transform: scale(1.04); }
/* Caption overlay */
.photo-caption {
position: absolute;
bottom: 0; left: 0; right: 0;
padding: 12px 16px;
background: linear-gradient(transparent, rgba(0,0,0,0.7));
color: white;
font-size: 0.8rem;
opacity: 0;
transition: opacity 0.2s;
}
.photo:hover .photo-caption { opacity: 1; }
Common Mistakes
Mistake 1 โ Off-by-one on grid line numbers
โ Wrong โ expecting to span columns 2 and 3, but getting only column 2:
.item { grid-column: 2 / 3; } /* line 2 to line 3 = only 1 column track */
โ Correct โ end line must be one beyond the last column to include:
.item { grid-column: 2 / 4; } /* line 2 to line 4 = columns 2 AND 3 */
.item { grid-column: 2 / span 2; } /* clearer alternative */
Mistake 2 โ Placing items outside the defined template
โ Wrong โ placing an item beyond defined rows creates an implicit row with no defined height:
.grid { grid-template-rows: 200px; } /* only 1 explicit row */
.item { grid-row: 2 / 4; } /* row 2-4 are implicit โ height: auto */
โ Correct โ define implicit row sizing with grid-auto-rows:
.grid { grid-template-rows: 200px; grid-auto-rows: 200px; }
Mistake 3 โ Using grid-auto-flow: dense with accessibility-sensitive content
โ Wrong โ dense reordering breaks keyboard/screen-reader logical order:
.grid { grid-auto-flow: row dense; }
/* Visual order differs from DOM order โ links/buttons navigate out of sequence */
โ Correct โ reorder the HTML if visual order must match tab order, or use dense only for decorative galleries:
/* For interactive content: reorder HTML instead of using dense */
.grid { grid-auto-flow: row; }
Quick Reference
| Property | Example | Meaning |
|---|---|---|
grid-column |
1 / 3 |
From line 1 to line 3 (spans 2 columns) |
grid-column |
1 / -1 |
Full width โ first to last line |
grid-column |
span 2 |
2 columns wide, auto-placed |
grid-row |
1 / span 3 |
Starts at row 1, spans 3 rows tall |
grid-area |
1 / 2 / 3 / 4 |
Shorthand: row-s / col-s / row-e / col-e |
grid-auto-flow |
row dense |
Fill gaps left by spanning items |
grid-auto-rows |
minmax(100px, auto) |
Size of implicit rows |