Placing Items on the Grid

โ–ถ Try It Yourself

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
Note: Grid line numbers start at 1, not 0. A 4-column grid has 5 column lines: 1, 2, 3, 4, 5. Negative line numbers count from the end: -1 is the last line, -2 is the second-to-last. Using 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.
Tip: Use 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.
Warning: When you place items explicitly with 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.

/* 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; }

▶ Try It Yourself

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

🧠 Test Yourself

In a 4-column grid, which declaration makes an item span all columns regardless of how many columns exist?





โ–ถ Try It Yourself