🔲 Lesson 14: CSS Grid Basics
Flexbox handles one direction brilliantly — a row or a column. But what about a full page layout with a header, sidebar, main content, and footer? Or a photo gallery with precise rows and columns? CSS Grid is the two-dimensional layout system built for exactly this. If Flexbox is a clothesline, Grid is a spreadsheet.
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain how CSS Grid differs from Flexbox and when to use each
- Create a grid container with
display: grid - Define columns and rows with
grid-template-columnsandgrid-template-rows - Use the
frunit to create flexible, proportional columns - Place items into specific grid cells using line numbers
- Name grid areas with
grid-template-areasfor readable page layouts - Control spacing with
gap - Build a full page layout with header, sidebar, content, and footer
Estimated Time: 60 minutes
Hands-on: You'll build a complete page layout and a responsive image gallery.
📑 In This Lesson
Grid vs. Flexbox — When to Use Which
Flexbox and Grid aren't competitors — they're partners. Each excels at a different type of layout problem:
| Aspect | Flexbox | CSS Grid |
|---|---|---|
| Dimensions | One-dimensional (row or column) | Two-dimensional (rows and columns) |
| Best for | Components: navbars, card rows, button groups | Page layouts: headers, sidebars, content areas, galleries |
| Content vs. Layout | Content-driven — items determine how space is used | Layout-driven — you define the grid, items fill it |
| Item placement | Items flow in order along the main axis | Items can be placed in any cell, in any order |
Rule of thumb: Use Flexbox for components (things inside a page section). Use Grid for page-level structure (the sections themselves). And use them together — a Grid layout whose cells contain Flexbox components is completely normal.
(Flexbox inside)"] A --> C["Sidebar"] A --> D["Main Content"] A --> E["Footer
(Flexbox inside)"] D --> F["Card Row
(Flexbox)"] D --> G["Image Gallery
(Grid)"] style A fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#1e293b style B fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style C fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style D fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style E fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style F fill:#fffbeb,stroke:#f59e0b,stroke-width:2px,color:#1e293b style G fill:#fffbeb,stroke:#f59e0b,stroke-width:2px,color:#1e293b
Creating a Grid
Just like Flexbox, Grid starts with the parent. Apply display: grid to the container, then define columns and rows:
.grid-container {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
}
That one rule creates a 3-column, 3-row grid. The children automatically fill the cells in order — left to right, top to bottom — like reading a book.
Defining Columns
grid-template-columns defines how many columns you want and how wide each one is:
/* Three equal columns, each 200px */
.grid { grid-template-columns: 200px 200px 200px; }
/* Two columns: one fixed, one flexible */
.grid { grid-template-columns: 250px 1fr; }
/* Four columns: 25% each */
.grid { grid-template-columns: 25% 25% 25% 25%; }
/* Three columns using the fr unit (more on this next) */
.grid { grid-template-columns: 1fr 2fr 1fr; }
Defining Rows
grid-template-rows works the same way, but for rows:
/* Explicit row heights */
.grid { grid-template-rows: 80px 1fr 60px; }
/* Auto-sized rows (height fits content) */
.grid { grid-template-rows: auto auto auto; }
If you don't define rows explicitly, Grid creates them automatically as items are added. These implicit rows default to auto height (fitting their content). You can control their size with grid-auto-rows:
/* All auto-created rows will be at least 150px tall */
.grid {
grid-template-columns: 1fr 1fr 1fr;
grid-auto-rows: minmax(150px, auto);
}
The fr Unit — Fractional Space
The fr (fraction) unit is Grid's killer feature. It represents a share of the available space — after fixed-size tracks and gaps are accounted for.
/* Three equal columns */
.grid { grid-template-columns: 1fr 1fr 1fr; }
/* Sidebar (1 share) + Main content (3 shares) */
.grid { grid-template-columns: 1fr 3fr; }
/* Fixed sidebar + flexible main */
.grid { grid-template-columns: 250px 1fr; }
In the 1fr 3fr example, the available space is divided into 4 shares. The first column gets 1/4, the second gets 3/4. If the container is 800px wide, that's 200px and 600px.
fr is more predictable than percentages because it accounts for gaps automatically. With percentages, you'd need to subtract the gap from 100% yourself. With fr, the browser handles it.
💡 Mixing Units
You can mix fr with fixed units freely. The fixed tracks get their exact size first, then the remaining space is split among the fr tracks. This is incredibly powerful for layouts like "fixed sidebar + flexible main content":
.layout { grid-template-columns: 250px 1fr; }
/* Sidebar is always 250px. Main content gets everything else. */
repeat() and minmax()
Typing 1fr 1fr 1fr 1fr for twelve equal columns gets tedious. The repeat() function saves you:
/* 3 equal columns */
.grid { grid-template-columns: repeat(3, 1fr); }
/* 12-column grid (like Bootstrap) */
.grid { grid-template-columns: repeat(12, 1fr); }
/* Pattern: 200px then 1fr, repeated 3 times */
.grid { grid-template-columns: repeat(3, 200px 1fr); }
/* Result: 200px 1fr 200px 1fr 200px 1fr */
minmax() — Flexible Sizing with Limits
minmax(min, max) sets a size range. The track will be at least min and at most max:
/* Rows at least 100px tall, but grow to fit content */
.grid { grid-auto-rows: minmax(100px, auto); }
/* Columns at least 200px, at most 1fr */
.grid { grid-template-columns: repeat(3, minmax(200px, 1fr)); }
Auto-fit and Auto-fill — Responsive Without Media Queries
The real magic happens when you combine repeat(), auto-fit, and minmax():
/* Responsive grid: as many 250px+ columns as will fit */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
This single line creates a fully responsive grid. On a wide screen, you get four or five columns. On a tablet, three. On a phone, one or two. No media queries. The browser figures out how many 250px-minimum columns fit and distributes the remaining space equally.
| Keyword | Behavior | When to Use |
|---|---|---|
auto-fit |
Creates columns, then collapses empty ones so items stretch to fill | Most cases — items expand to use all available space |
auto-fill |
Creates columns but keeps empty ones as visible gaps | When you want consistent column widths even with few items |
In practice, auto-fit is what you want 90% of the time.
gap — Spacing Between Cells
Just like in Flexbox, gap adds space between grid cells without affecting the outer edges:
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px; /* 20px between all rows and columns */
}
/* Different row and column gaps */
.grid {
gap: 20px 16px; /* 20px between rows, 16px between columns */
}
/* Individual properties */
.grid {
row-gap: 24px;
column-gap: 16px;
}
An important advantage over using margins: gap only creates space between cells. There's no extra space on the outer edges, so you don't need to fight with :first-child or :last-child overrides.
Placing Items on the Grid
By default, grid items fill cells in order. But you can place any item in any cell — or make it span multiple cells — using grid line numbers.
Understanding Grid Lines
Grid lines are the invisible lines between and around tracks. A 3-column grid has 4 column lines (numbered 1 through 4):
Placing Items by Line Number
/* Place an item from column line 1 to column line 3 (spans 2 columns) */
.header {
grid-column: 1 / 3;
}
/* Place an item from row line 1 to row line 3 (spans 2 rows) */
.sidebar {
grid-row: 1 / 3;
}
/* Span all columns (use -1 for the last line) */
.footer {
grid-column: 1 / -1;
}
The span Keyword
Instead of counting line numbers, you can say "span N tracks":
/* Span 2 columns from wherever the item naturally lands */
.feature-card {
grid-column: span 2;
}
/* Span 2 rows */
.tall-card {
grid-row: span 2;
}
/* Span 2 columns AND 2 rows */
.big-card {
grid-column: span 2;
grid-row: span 2;
}
Shorthand: grid-column and grid-row
The shorthand grid-column combines grid-column-start and grid-column-end with a slash:
/* These are equivalent: */
.item { grid-column-start: 2; grid-column-end: 4; }
.item { grid-column: 2 / 4; }
.item { grid-column: 2 / span 2; }
grid-template-areas — Named Layouts
This is many developers' favorite Grid feature. Instead of juggling line numbers, you draw your layout as a text map:
.page {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
gap: 0;
}
Then assign each child to its named area:
.page-header { grid-area: header; }
.page-sidebar { grid-area: sidebar; }
.page-main { grid-area: main; }
.page-footer { grid-area: footer; }
<div class="page">
<header class="page-header">Header</header>
<aside class="page-sidebar">Sidebar</aside>
<main class="page-main">Main Content</main>
<footer class="page-footer">Footer</footer>
</div>
The beauty of this approach: the layout is readable as plain text. You can see the structure at a glance — "header" spans both columns, "sidebar" and "main" sit side by side, "footer" spans both columns again.
💡 Area Rules
- Each row in the template is a quoted string
- Area names must form rectangles — no L-shapes or T-shapes
- Use a
.(dot) for an empty cell:"sidebar . main" - The number of names in each row must match the number of columns
Alignment in Grid
Grid offers alignment along both axes simultaneously — something Flexbox can only do in one direction at a time.
Container-Level Alignment
| Property | Axis | What It Does |
|---|---|---|
justify-items |
Inline (horizontal) | Aligns all items horizontally within their cells |
align-items |
Block (vertical) | Aligns all items vertically within their cells |
place-items |
Both | Shorthand: align-items / justify-items |
justify-content |
Inline (horizontal) | Aligns the entire grid within the container |
align-content |
Block (vertical) | Aligns the entire grid within the container |
place-content |
Both | Shorthand: align-content / justify-content |
Item-Level Alignment
Override alignment for individual items with justify-self and align-self:
/* Center everything in the grid by default */
.grid {
display: grid;
place-items: center;
}
/* But this one item aligns to the end */
.special {
justify-self: end;
align-self: start;
}
Centering in Grid — Even Simpler
Grid offers arguably the simplest centering syntax in CSS:
.centered {
display: grid;
place-items: center;
min-height: 100vh;
}
One property — place-items: center — centers all children both horizontally and vertically. Even simpler than the Flexbox version.
Common Grid Patterns
Pattern 1: Classic Page Layout (Header, Sidebar, Main, Footer)
.page-layout {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
Pattern 2: Responsive Image Gallery
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.gallery img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
}
This creates a gallery that automatically adjusts from one column on small screens to as many as five or six on large screens — all without media queries.
Pattern 3: Dashboard Cards
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 24px;
}
/* Feature card spans two columns */
.dashboard .featured {
grid-column: span 2;
}
Pattern 4: Holy Grail Layout (No Sidebar on Mobile)
.page {
display: grid;
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
/* On larger screens, add the sidebar column */
@media (min-width: 768px) {
.page {
grid-template-columns: 250px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
}
This is a sneak peek at responsive design (covered fully in the next lesson). The key idea: redefine grid-template-areas at different screen sizes to completely rearrange the page layout.
Pattern 5: Equal-Height Cards
/* Grid makes all cards in a row the same height automatically */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
/* Each card stretches to fill its cell (default behavior) */
.card-grid .card {
display: flex;
flex-direction: column;
}
/* Push the button to the bottom of the card */
.card-grid .card .btn {
margin-top: auto;
}
Notice the Grid + Flexbox combo: Grid handles the outer layout (equal-height columns), while Flexbox inside each card pushes the button to the bottom. This is the power of using both systems together.
Hands-on Exercise
🏋️ Exercise: Grid Page Layout & Gallery
Objective: Build a complete page layout using grid-template-areas and create a responsive image gallery.
Part A — Page Layout:
- In your
css/style.css, create a.page-gridclass withdisplay: grid - Define two columns:
220px 1fr - Define three rows:
auto 1fr auto - Use
grid-template-areasto create a header (full width), sidebar + main (middle row), and footer (full width) - Set
min-height: 100vhso the layout fills the viewport - Assign each section its grid area name
- Add some padding and background colors to each section so you can see the layout
Part B — Responsive Gallery:
- Inside your main content area, create a
<div class="gallery"> - Add 6–8 placeholder images (use
https://picsum.photos/400/300for placeholder images) - Style the gallery with
display: grid - Use
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - Add
gap: 16px; - Style images with
width: 100%; height: 200px; object-fit: cover; border-radius: 8px; - Resize your browser and watch columns appear and disappear automatically
💡 Hint
For the page layout, remember that grid-template-areas uses quoted strings — one string per row. The "header" name should appear twice in the first row (once for each column) so it spans the full width. Same for the footer. Use min-height: 100vh so the middle row stretches even if there's not much content.
✅ Example Solution
/* ===========================
Page Layout (Grid)
=========================== */
.page-grid {
display: grid;
grid-template-columns: 220px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
}
.page-header {
grid-area: header;
background-color: var(--color-primary, #1e293b);
color: white;
padding: 16px 24px;
}
.page-sidebar {
grid-area: sidebar;
background-color: var(--color-bg-alt, #f8fafc);
padding: 20px;
border-right: 1px solid var(--color-border, #e2e8f0);
}
.page-main {
grid-area: main;
padding: 24px;
}
.page-footer {
grid-area: footer;
background-color: var(--color-bg-alt, #f8fafc);
padding: 16px 24px;
border-top: 1px solid var(--color-border, #e2e8f0);
text-align: center;
font-size: 0.875rem;
color: var(--color-text-light, #64748b);
}
/* ===========================
Responsive Gallery (Grid)
=========================== */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin-top: 24px;
}
.gallery img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
transition: transform 0.2s ease;
}
.gallery img:hover {
transform: scale(1.03);
}
<div class="page-grid">
<header class="page-header">
<h1>My Website</h1>
<!-- Flex nav inside grid header -->
</header>
<aside class="page-sidebar">
<nav>
<h3>Navigation</h3>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="about.html">About</a></li>
<li><a href="recipe.html">Recipes</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</nav>
</aside>
<main class="page-main">
<h2>Photo Gallery</h2>
<div class="gallery">
<img src="https://picsum.photos/400/300?random=1" alt="Gallery photo 1">
<img src="https://picsum.photos/400/300?random=2" alt="Gallery photo 2">
<img src="https://picsum.photos/400/300?random=3" alt="Gallery photo 3">
<img src="https://picsum.photos/400/300?random=4" alt="Gallery photo 4">
<img src="https://picsum.photos/400/300?random=5" alt="Gallery photo 5">
<img src="https://picsum.photos/400/300?random=6" alt="Gallery photo 6">
</div>
</main>
<footer class="page-footer">
<p>© 2026 Alex Johnson. Built with HTML & CSS.</p>
</footer>
</div>
🎯 Quick Quiz
Question 1: What is the main difference between Flexbox and CSS Grid?
Question 2: What does 1fr mean in CSS Grid?
Question 3: What does grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); do?
Question 4: How do you make a grid item span two columns?
Question 5: What is grid-template-areas used for?
Summary
🎉 Key Takeaways
- CSS Grid is two-dimensional — it controls rows and columns at the same time
- Use Flexbox for components (navbars, card rows) and Grid for page layouts (header/sidebar/main/footer)
grid-template-columnsandgrid-template-rowsdefine the grid structure- The
frunit distributes remaining space proportionally — better than percentages because it accounts for gaps repeat(auto-fit, minmax(250px, 1fr))creates a fully responsive grid with no media queries- Place items with line numbers (
grid-column: 1 / 3) or thespankeyword (grid-column: span 2) grid-template-areaslets you draw your layout as a readable text map — the most intuitive way to define page structureplace-items: centeris the simplest way to center content in CSS- Grid + Flexbox together is the standard approach: Grid for the outer page, Flexbox for components inside each grid cell
📁 Your Project So Far
my-website/
├── css/
│ └── style.css ← now with Grid page layout + gallery
├── images/
│ └── sunset.jpg
├── index.html ← grid-based page structure
├── about.html
├── recipe.html
└── contact.html
🚀 What's Next?
You can now build sophisticated layouts with Flexbox and Grid. But what happens when someone visits your site on a phone? Or a giant desktop monitor? A layout that works at one screen size might be cramped or wasted at another. In the next lesson, you'll learn Responsive Design — using media queries, fluid sizing, and mobile-first thinking to make your site look great on every device.