CSS Grid: The Complete Guide to Layout Mastery (2026)
CSS Grid changed web layout forever. No more floats, no more hacks. Here's everything you need to build any layout with confidence.
The Mental Model
Think of CSS Grid as a 2D layout system:
┌─────────┬─────────┬─────────┐
│ │ │ │
│ col 1 │ col 2 │ col 3 │ ← columns (vertical tracks)
│ │ │ │
├─────────┼─────────┼─────────┤
│ │ │ │
│ row 1 │ row 1 │ row 1 │ ← rows (horizontal tracks)
│ │ │ │
├─────────┼─────────┼─────────┤
│ │ │ │
│ row 2 │ row 2 │ row 2 │
│ │ │ │
└─────────┴─────────┴─────────┘
Key concepts:
- Grid Container: The parent element with display: grid
- Grid Items: The child elements inside the container
- Grid Lines: The lines between rows/columns (numbered from 1)
- Grid Tracks: The space between two adjacent lines (a row or column)
- Grid Cell: The intersection of a row and column track
- Grid Area: A rectangular area spanning multiple cells
Essential Properties
/* === Container Properties === */
.container {
display: grid;
/* Define columns: */
grid-template-columns: 200px 1fr 200px;
/* Three columns: fixed, flexible, fixed */
grid-template-columns: repeat(3, 1fr);
/* Three equal-width columns */
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
/* Responsive! Creates as many 250px+ columns as fit */
/* Define rows: */
grid-template-rows: auto 1fr auto;
/* Header: content size | Main: fill remaining | Footer: content size */
/* Gap between items: */
gap: 16px; /* Row AND column gap */
column-gap: 12px; /* Column gap only */
row-gap: 8px; /* Row gap only */
/* Place content within the grid: */
justify-items: start; /* Horizontal alignment of items in their cells */
/* Values: start | end | center | stretch (default) */
align-items: center; /* Vertical alignment of items in their cells */
/* Values: start | end | center | stretch (default) */
place-items: center; /* Shorthand for align + justify */
/* Distribute content across the entire grid: */
justify-content: center; /* When total item size < container size */
align-content: center;
place-content: center;
/* Auto-flow direction: */
grid-auto-flow: row; /* Fill rows first (default) */
grid-auto-flow: column; /* Fill columns first */
grid-auto-flow: dense; /* Pack items tightly (fill gaps) */
/* Size of implicitly-created rows/columns: */
grid-auto-rows: minmax(100px, auto);
}
/* === Item Properties === */
.item {
/* Position by line number: */
grid-column-start: 1;
grid-column-end: 3;
/* Spans from line 1 to line 3 = 2 columns wide */
/* Shorthand: */
grid-column: 1 / 3; /* Same as above */
grid-column: 1 / -1; /* Span to last line (-1 = last line) */
grid-column: span 2; /* Span 2 columns from current position */
grid-row: 2 / 4; /* Row 2 through 4 */
/* Full shorthand: */
grid-area: 2 / 1 / 4 / 3;
/* grid-row-start / grid-column-start / grid-row-end / grid-column-end */
/* Self-alignment (overrides container's align/justify-items): */
justify-self: end;
align-self: start;
place-self: end start;
/* Order (like flexbox order): */
order: -1; /* Appears before other items */
}
Named Grid Areas (The Game Changer)
/* Instead of remembering line numbers, NAME your areas! */
.layout {
display: grid;
grid-template-columns: 250px 1fr 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
gap: 20px;
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }
/* Result:
┌──────────┬──────────────┬──────────┐
│ header │ │
├──────────┼──────────────┼──────────┤
│ sidebar │ main │ aside │
│ │ │ │
├──────────┴──────────────┴──────────┤
│ footer │
└─────────────────────────────────────┘
Responsive tweak with media query:
@media (max-width: 768px) {
.layout {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"aside"
"footer";
}
}
/* Mobile layout — same HTML, just change the template! */
Real-World Layout Patterns
/* === Pattern 1: Holy Grail Layout === */
.holy-grail {
display: grid;
grid-template: auto 1fr auto / 220px 1fr 180px;
grid-template-areas:
"header header header"
"nav content ads"
"footer footer footer";
min-height: 100vh;
}
/* === Pattern 2: Dashboard Grid === */
.dashboard {
display: grid;
grid-template-columns: repeat(12, 1fr); /* 12-column system! */
grid-template-rows: auto 1fr auto;
gap: 24px;
padding: 24px;
}
.dash-header { grid-area: 1 / 1 / 2 / 13; }
.stat-card:nth-child(2) { grid-column: span 3; }
.stat-card:nth-child(3) { grid-column: span 3; }
.stat-card:nth-child(4) { grid-column: span 3; }
.stat-card:nth-child(5) { grid-column: span 3; }
.main-chart { grid-area: 3 / 1 / 6 / 9; }
.side-panel { grid-area: 3 / 9 / 6 / 13; }
/* Responsive dashboard: */
@media (max-width: 1024px) {
.dashboard { grid-template-columns: repeat(6, 1fr); }
.stat-card:nth-child(n) { grid-column: span 3; }
.main-chart { grid-area: 3 / 1 / 6 / 7; }
.side-panel { grid-area: 6 / 1 / 9 / 7; }
}
@media (max-width: 640px) {
.dashboard { grid-template-columns: 1fr; }
.stat-card:nth-child(n) { grid-column: 1 / -1; }
}
/* === Pattern 3: Card Gallery === */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
padding: 24px;
}
/* Automatically adjusts number of columns based on available width!
1400px viewport → 4 cards per row
900px viewport → 3 cards per row
600px viewport → 2 cards per row
*/
/* === Pattern 4: Overlapping Elements === */
.hero {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
}
.hero-text {
grid-column: 1;
grid-row: 1 / 3;
z-index: 2;
}
.hero-image {
grid-column: 2;
grid-row: 1 / 3;
clip-path: polygon(10% 0, 100% 0, 100% 100%, 0 100%);
}
/* Text overlaps image slightly with angled edge on the image */
/* === Pattern 5: Masonry-like Layout === */
.masonry {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 10px; /* Base unit for sizing */
gap: 16px;
}
.masonry-item:nth-child(1) { grid-row: span 20; } /* Tall card */
.masonry-item:nth-child(2) { grid-row: span 15; }
.masonry-item:nth-child(3) { grid-row: span 25; } /* Tallest */
.masonry-item:nth-child(4) { grid-row: span 18; }
.masonry-item:nth-child(5) { grid-row: span 22; }
/* Note: True masonry needs JS or CSS masonry spec (upcoming).
This approach works when you know approximate heights. */
Grid + Flexbox: Better Together
/*
When to use which:
GRID → 2D layouts (rows AND columns)
✅ Page layout (header, nav, main, sidebar, footer)
✅ Card grids and galleries
✅ Complex arrangements where items need precise placement
✅ Items that need to overlap or span multiple tracks
FLEXBOX → 1D layouts (row OR column)
✅ Component-level alignment (navbar, form inputs, button groups)
✅ Content that flows naturally in one direction
✅ Centering (vertical + horizontal)
✅ Evenly spacing items with unknown sizes
They work TOGETHER:
*/
.page-layout {
display: grid;
grid-template: auto 1fr auto / 240px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
.navbar {
/* Inside the header area, use Flexbox for navbar items */
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
}
.card-list {
/* Inside the main area, use Flexbox for list items */
display: flex;
flex-direction: column;
gap: 16px;
}
.form-row {
/* Form inside a grid cell, use Flexbox for input alignment */
display: flex;
gap: 12px;
align-items: center;
}
Pro Tips & Common Pitfalls
/* ❌ Forgetting gap support in older browsers */
/* Use margin fallback if supporting very old browsers: */
.grid-old-support {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px; /* Modern browsers */
/* Fallback: */
/* @supports not (gap: 20px) { margin: -10px; } */
}
/* ❌ Using grid for single-dimension layouts */
/* If items just flow in one direction → use Flexbox instead */
.wrong-use-of-grid {
display: grid;
grid-auto-flow: column;
gap: 16px;
/* This is basically flexbox. Just use display: flex! */
}
/* ✅ Use fr units intelligently */
.good-fr {
grid-template-columns: 250px 1fr 150px;
/* Fixed sidebar | Flexible main | Fixed side panel */
grid-template-columns: 2fr 1fr 1fr;
/* Main takes 2x space of each sidebar */
}
/* ✅ minmax() prevents overflow */
.responsive-safe {
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
/* Columns are at least 300px but shrink on small screens.
Never causes horizontal scroll! */
}
/* ✅ subgrid for nested grids (modern browsers!) */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.card {
display: grid;
grid-template-rows: subgrid; /* Inherits parent's row definition! */
grid-row: span 3; /* Spans 3 rows of the PARENT grid */
}
/* Card items align perfectly with neighboring cards' items */
/* ⚠️ Grid items are blockified */
/* <span> becomes display: block when it's a grid item */
/* Inline elements behave as blocks inside grid containers */
/* ⚠️ Percentage in grid works differently */
/* % refers to the container's inline-size for columns,
block-size for rows (not always what you expect!) */
What's your favorite Grid layout trick? What layout challenge are you still struggling with?
Follow @armorbreak for more practical developer guides.
Top comments (0)