Chapter 1 of ?
css 8 min read

CSS3 Mastery — Chapter 16: CSS Grid Layout — Complete Guide

Chapter 16: CSS Grid Layout — Complete Guide

CSS Grid Layout is the most powerful 2D layout engine in web design. Unlike 1D Flexbox, Grid manages both rows and columns simultaneously, enabling precise page architecture, named template areas, subgrid inheritance, and responsive 12-column grid systems.

16.1 Understanding 2D CSS Grid & The Grid Blueprint

CSS Grid creates an explicit or implicit 2D coordinate grid defined by grid lines, grid tracks (columns & rows), and grid cells. Every grid item lives in one or more cells:

display: grid | grid-template-columns: repeat(3, 1fr) | grid-template-rows: auto auto Cell (1,1) col 1 / row 1 Cell (1,2) col 2 / row 1 Cell (1,3) col 3 / row 1 Spanning Item grid-column: 1 / 3 (spans 2 cols) Cell (2,3) grid-row: 2
Grid Concept Definition Example
Grid Container The parent element with display: grid or display: inline-grid .wrapper { display: grid; }
Grid Lines Numbered lines (1-based) that divide the grid into tracks. Negative indices count from the end. Column line 1, row line -1 (last)
Grid Track A single row or column — the space between two adjacent grid lines. The area between column lines 1 and 2
Grid Cell The intersection of one row track and one column track — the smallest unit. Cell at column 2, row 3
Grid Area Any rectangular area composed of one or more grid cells. An item spanning columns 1–3, row 1

16.2 Grid Container Properties — Complete Reference

Container-level properties define the grid structure. These are applied to the parent element:

Property Key Values / Syntax Description
display grid, inline-grid Activates grid context. inline-grid keeps the container inline.
grid-template-columns 1fr 2fr 1fr, repeat(3, 1fr), 200px auto 1fr Defines column track sizes. Accepts px, %, fr, auto, minmax(), repeat().
grid-template-rows auto, 100px 1fr, repeat(2, 60px) Defines row track sizes. auto = sized by content.
grid-template-areas "header header" "sidebar main" "footer footer" ASCII-art visual layout map using named areas. Each string = one row.
grid-template Shorthand combining rows, columns, and areas Combines grid-template-rows, -columns, and -areas.
gap (row-gap / column-gap) 16px, 1rem 2rem Sets gutter spacing between tracks. No margin hacks needed.
justify-items start, end, center, stretch Aligns all items horizontally within their grid cells. Default: stretch.
align-items start, end, center, stretch Aligns all items vertically within their grid cells.
justify-content start, end, center, space-between, space-around Distributes the entire grid horizontally within the container.
align-content start, end, center, space-between Distributes the entire grid vertically within the container.
grid-auto-flow row, column, row dense, column dense Controls how implicitly-placed items fill the grid. dense fills holes.
grid-auto-rows / grid-auto-columns auto, 100px, minmax(80px, auto) Sets sizes for implicitly-created tracks.

16.3 Key Grid Functions: fr, repeat(), minmax()

grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
repeat(auto-fit, ...) = create as many columns as fit in the container
minmax(200px, 1fr) = each column is at least 200px, grows to 1fr
→ Result: Zero media queries needed for a fully responsive card grid!
Function / Unit Syntax Description
fr (Fractional Unit) 1fr 2fr 1fr Shares remaining free space proportionally. 2fr = twice as wide as 1fr.
repeat(N, size) repeat(4, 1fr) Repeats a track definition N times. Avoids repetitive code.
minmax(min, max) minmax(150px, 1fr) Track is at least min, at most max. Enables fluid, bounded tracks.
auto-fill repeat(auto-fill, minmax(200px, 1fr)) Fills as many columns as possible. Keeps empty columns as space at the end.
auto-fit repeat(auto-fit, minmax(200px, 1fr)) Fills as many columns as possible. Collapses empty columns to 0 — items stretch to fill. Preferred for card grids!
fit-content(max) fit-content(300px) Track is sized by content, capped at the specified maximum.
<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; display: flex; flex-direction: column; gap: 24px; }
    h4 { color: #38bdf8; margin: 0 0 8px; font-size: 13px; }
    .label { font-size: 11px; color: #64748b; font-family: monospace; margin-bottom: 6px; }
    .card { background: #1e293b; border: 1px solid #334155; border-radius: 10px; padding: 14px; font-size: 12px; color: #94a3b8; }
    .card strong { color: #f8fafc; display: block; margin-bottom: 4px; }

    /* auto-fit: collapses empty columns — items fill full width */
    .grid-autofit {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: 12px;
    }

    /* fr unit: proportional column widths */
    .grid-fr {
      display: grid;
      grid-template-columns: 1fr 2fr 1fr; /* sidebar | main | sidebar */
      gap: 12px;
    }
  </style>
</head>
<body>
  <div>
    <h4>auto-fit + minmax() — Responsive card grid with zero media queries</h4>
    <p class="label">grid-template-columns: repeat(auto-fit, minmax(180px, 1fr))</p>
    <div class="grid-autofit">
      <div class="card"><strong>Card 1</strong>Grows to fill space</div>
      <div class="card"><strong>Card 2</strong>Minimum 180px wide</div>
      <div class="card"><strong>Card 3</strong>Maximum 1fr wide</div>
      <div class="card"><strong>Card 4</strong>Auto-wraps below 180px</div>
    </div>
  </div>

  <div>
    <h4>fr units — 1fr sidebar | 2fr main | 1fr sidebar</h4>
    <p class="label">grid-template-columns: 1fr 2fr 1fr</p>
    <div class="grid-fr">
      <div class="card" style="border-color:#f59e0b;"><strong style="color:#fbbf24;">Left Sidebar</strong>1fr</div>
      <div class="card" style="border-color:#10b981;"><strong style="color:#34d399;">Main Content</strong>2fr — double width</div>
      <div class="card" style="border-color:#f59e0b;"><strong style="color:#fbbf24;">Right Sidebar</strong>1fr</div>
    </div>
  </div>
</body>
</html>

16.4 auto-fill vs auto-fit — The Critical Difference

This is one of the most commonly confused concepts in CSS Grid. Both auto-fill and auto-fit create as many tracks as possible — but they handle empty tracks differently:

Keyword Empty Track Behavior Result With 2 Items in a Wide Container Best Used For
auto-fill Keeps empty tracks (reserves space) Items stay at their minimum width; empty column space remains on the right Grids where you need consistent column positions (e.g., known future items)
auto-fit Collapses empty tracks to 0 Items stretch to fill all available space (grow to 1fr each) Card grids — items always fill the full container width
<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; display: flex; flex-direction: column; gap: 20px; }
    h4 { color: #6366f1; margin: 0 0 6px; }
    .label { font-size: 11px; font-family: monospace; color: #64748b; margin-bottom: 8px; }
    .card { background: #1e293b; border: 2px solid #334155; border-radius: 10px; padding: 16px; font-weight: bold; font-size: 13px; text-align: center; color: #a5b4fc; }

    /* auto-fill: reserves space for potential extra columns */
    .grid-fill { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }

    /* auto-fit: collapses empty columns — items expand to fill container */
    .grid-fit { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
  </style>
</head>
<body>
  <div>
    <h4>auto-fill — Items stay at min-width, empty space preserved</h4>
    <p class="label">repeat(auto-fill, minmax(140px, 1fr)) — only 2 items</p>
    <div class="grid-fill">
      <div class="card" style="border-color:#38bdf8;">Item A</div>
      <div class="card" style="border-color:#38bdf8;">Item B</div>
    </div>
  </div>

  <div>
    <h4>auto-fit — Empty columns collapse; items fill full width</h4>
    <p class="label">repeat(auto-fit, minmax(140px, 1fr)) — same 2 items</p>
    <div class="grid-fit">
      <div class="card" style="border-color:#10b981;color:#34d399;">Item A — fills container</div>
      <div class="card" style="border-color:#10b981;color:#34d399;">Item B — fills container</div>
    </div>
  </div>
</body>
</html>

16.5 Grid Named Template Areas (grid-template-areas)

grid-template-areas provides a visual ASCII-art mapping syntax to define page layout regions. Each quoted string represents one row, with area names mapping to element positions:

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
    h3 { margin-top: 0; color: #818cf8; }

    /* The ASCII map defines the layout visually */
    .app-layout {
      display: grid;
      grid-template-areas:
        "header  header  header"
        "sidebar main    main"
        "sidebar widgets widgets"
        "footer  footer  footer";
      grid-template-columns: 180px 1fr 1fr;
      grid-template-rows: 60px 1fr auto 50px;
      gap: 10px;
      min-height: 300px;
    }

    .area { border-radius: 8px; padding: 14px; font-size: 13px; font-weight: bold; display: flex; align-items: center; justify-content: center; }
    .area-header  { grid-area: header;  background: rgba(99,102,241,0.3);  border: 2px solid #6366f1; color: #a5b4fc; }
    .area-sidebar { grid-area: sidebar; background: rgba(245,158,11,0.2);  border: 2px solid #f59e0b; color: #fbbf24; }
    .area-main    { grid-area: main;    background: rgba(16,185,129,0.2);  border: 2px solid #10b981; color: #34d399; }
    .area-widgets { grid-area: widgets; background: rgba(56,189,248,0.15); border: 2px solid #38bdf8; color: #7dd3fc; }
    .area-footer  { grid-area: footer;  background: rgba(30,41,59,0.8);    border: 1px solid #334155; color: #64748b; font-size: 12px; }
  </style>
</head>
<body>
  <h3>grid-template-areas — Full Page Layout</h3>
  <div class="app-layout">
    <header class="area area-header">Header (grid-area: header — spans 3 cols)</header>
    <aside class="area area-sidebar">Sidebar<br/>grid-area: sidebar</aside>
    <main class="area area-main">Main Content (grid-area: main)</main>
    <section class="area area-widgets">Widgets Panel (grid-area: widgets)</section>
    <footer class="area area-footer">Footer — grid-area: footer — spans all 3 columns</footer>
  </div>
</body>
</html>

16.6 Grid Item Placement (grid-column, grid-row, span)

Place items explicitly using grid line numbers or the span keyword:

Property Syntax Description
grid-column 1 / 3, 1 / -1, span 2 Shorthand for grid-column-start / grid-column-end. Use negative index for last line.
grid-row 1 / 2, 2 / span 3 Shorthand for grid-row-start / grid-row-end.
grid-area header, 1 / 1 / 2 / 4 Maps item to a named area OR shorthand for row-start / col-start / row-end / col-end.
justify-self start, end, center, stretch Overrides justify-items for this individual item (horizontal alignment in cell).
align-self start, end, center, stretch Overrides align-items for this individual item (vertical alignment in cell).
<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
    h4 { color: #f59e0b; margin: 0 0 10px; }

    .grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(3, 60px); gap: 8px; }
    .item { background: #1e293b; border: 1px solid #334155; border-radius: 6px; padding: 8px; font-size: 11px; text-align: center; color: #94a3b8; font-family: monospace; display: flex; align-items: center; justify-content: center; }

    /* Full-width header */
    .full-row { grid-column: 1 / -1; background: rgba(99,102,241,0.3); border-color: #6366f1; color: #a5b4fc; font-weight: bold; }

    /* Double-wide item */
    .span-2 { grid-column: span 2; background: rgba(16,185,129,0.25); border-color: #10b981; color: #34d399; font-weight: bold; }

    /* Item placed at specific row/column */
    .placed { grid-column: 4; grid-row: 2 / 4; background: rgba(245,158,11,0.25); border-color: #f59e0b; color: #fbbf24; font-weight: bold; }
  </style>
</head>
<body>
  <h4>Grid Item Placement Demo (4-column × 3-row grid)</h4>
  <div class="grid">
    <div class="item full-row">grid-column: 1 / -1 (full width header)</div>
    <div class="item span-2">grid-column: span 2</div>
    <div class="item">Item 3</div>
    <div class="item placed">grid-column: 4<br/>grid-row: 2 / 4</div>
    <div class="item">Item 5</div>
    <div class="item">Item 6</div>
    <div class="item">Item 7</div>
  </div>
</body>
</html>

16.7 grid-auto-flow: dense — Filling Grid Holes

When items have varying spans, the default auto placement algorithm can leave holes. grid-auto-flow: dense automatically backfills those gaps with smaller items — perfect for masonry-style layouts:

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; display: flex; gap: 24px; flex-wrap: wrap; }
    h4 { color: #38bdf8; margin: 0 0 8px; font-size: 13px; }
    .wrapper { flex: 1 1 300px; }
    .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
    .dense { grid-auto-flow: dense; } /* Backfills holes! */
    .item { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 12px; font-size: 11px; font-weight: bold; text-align: center; height: 60px; display: flex; align-items: center; justify-content: center; }
    .wide { grid-column: span 2; background: rgba(16,185,129,0.25); border-color: #10b981; color: #34d399; }
    .tall { grid-row: span 2; height: auto; background: rgba(245,158,11,0.25); border-color: #f59e0b; color: #fbbf24; }
    .i1 { color: #a5b4fc; } .i2 { color: #7dd3fc; } .i3 { color: #f9a8d4; }
  </style>
</head>
<body>
  <div class="wrapper">
    <h4>Without dense — holes appear</h4>
    <div class="grid">
      <div class="item wide">Wide (span 2)</div>
      <div class="item i1">Item 2</div>
      <div class="item i2">Item 3</div>
      <div class="item wide">Wide (span 2)</div>
      <div class="item i3">Item 5</div>
    </div>
  </div>

  <div class="wrapper">
    <h4>With dense — holes backfilled automatically!</h4>
    <div class="grid dense">
      <div class="item wide">Wide (span 2)</div>
      <div class="item i1">Item 2</div>
      <div class="item i2">Item 3</div>
      <div class="item wide">Wide (span 2)</div>
      <div class="item i3">Item 5 — fills gap!</div>
    </div>
  </div>
</body>
</html>

16.8 Responsive 12-Column Grid System

Building a lightweight, custom 12-column grid layout system from scratch using CSS Grid — the same pattern used by Bootstrap and Tailwind CSS under the hood:

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; display: flex; flex-direction: column; gap: 16px; }
    h3 { margin-top: 0; color: #10b981; }

    /* 12-Column Grid Container */
    .grid-12 {
      display: grid;
      grid-template-columns: repeat(12, 1fr);
      gap: 10px;
    }

    /* Column span utilities */
    .col-12 { grid-column: span 12; }
    .col-8  { grid-column: span 8; }
    .col-6  { grid-column: span 6; }
    .col-4  { grid-column: span 4; }
    .col-3  { grid-column: span 3; }
    .col-2  { grid-column: span 2; }

    .box {
      background: #1e293b;
      border: 1px solid #334155;
      border-radius: 8px;
      padding: 12px 10px;
      font-size: 11px;
      font-weight: bold;
      text-align: center;
      color: #64748b;
      font-family: monospace;
    }
  </style>
</head>
<body>
  <h3>12-Column CSS Grid System</h3>
  <div class="grid-12">
    <div class="box col-12" style="border-color:#6366f1;color:#a5b4fc;">col-12 (Full Width)</div>

    <div class="box col-8" style="border-color:#10b981;color:#34d399;">col-8 (Main Content)</div>
    <div class="box col-4" style="border-color:#f59e0b;color:#fbbf24;">col-4 (Sidebar)</div>

    <div class="box col-6" style="border-color:#38bdf8;color:#7dd3fc;">col-6</div>
    <div class="box col-6" style="border-color:#38bdf8;color:#7dd3fc;">col-6</div>

    <div class="box col-4">col-4</div>
    <div class="box col-4">col-4</div>
    <div class="box col-4">col-4</div>

    <div class="box col-3" style="border-color:#ec4899;color:#f472b6;">col-3</div>
    <div class="box col-3" style="border-color:#ec4899;color:#f472b6;">col-3</div>
    <div class="box col-3" style="border-color:#ec4899;color:#f472b6;">col-3</div>
    <div class="box col-3" style="border-color:#ec4899;color:#f472b6;">col-3</div>
  </div>
</body>
</html>

16.9 Interactive Sandbox — CSS Grid Playground

Experiment with grid-template-areas, auto-fit vs auto-fill, and the 12-column system live:

Live Playground — Chapter 16 Grid Sandbox
Done with this chapter?
Mark it complete to track your progress and unlock your certificate.
Next Up

Learner Reviews

Write a Review
Share your experience to help other learners.
Your Rating *