🔲 CSS Grid Basics
Flexbox arranges things in a line. CSS Grid gives you the whole sheet of graph paper — rows and columns at once. With Grid you draw the skeleton of a page directly in your CSS, place items into named regions, and let the browser handle the responsive reflow. It is the most powerful layout tool CSS has ever had.
Week 1 · Thursday: CSS Layout · Lecture 2
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain how Grid differs from Flexbox and choose the right tool for a layout
- Define columns and rows with
grid-template-columns/rowsand thefrunit - Use
repeat(),minmax(), andauto-fitto build responsive grids with no media queries - Space tracks with the
gapproperty - Place items across the grid using line numbers,
span, and negative lines - Lay out a whole page visually with
grid-template-areas
Estimated Time: 65 minutes
Practice: Build an auto-fitting card grid and a named-area page layout.
In This Lesson
Why CSS Grid?
CSS Grid is a two-dimensional layout system. Where Flexbox controls items along a single axis, Grid lets you position content into a matrix of rows and columns simultaneously. Picture a sheet of graph paper: you decide how many columns wide and rows tall your layout is, then drop content into specific cells — or let it flow automatically.
The mental shift is from "how do these items line up next to each other?" (Flexbox) to "what is the overall structure of this page?" (Grid). That's why Grid shines for page-level scaffolding: a header spanning the full width, a sidebar and main content side by side, a footer pinned below — all described in a few readable lines.
Grid and Flexbox are not rivals — they're partners. A common professional pattern is Grid for the page skeleton and Flexbox inside individual grid cells for their internal alignment.
Anatomy of a Grid
A handful of terms unlock every Grid property. Learn these and the syntax reads like plain English.
| Term | Meaning |
|---|---|
| Grid container | The parent set to display: grid |
| Grid item | Each direct child of the container |
| Grid line | The numbered dividing lines between tracks (they start at 1) |
| Grid track | The space between two adjacent lines — a full row or column |
| Grid cell | A single row × column intersection |
| Grid area | A rectangular block of one or more cells |
Defining Rows & Columns
You create a grid by declaring display: grid and then describing its tracks. The number of values you list is the number of tracks you get.
/* Three fixed 200px columns and two 100px rows */
.grid {
display: grid;
grid-template-columns: 200px 200px 200px;
grid-template-rows: 100px 100px;
}
Listing the same value repeatedly is tedious. The repeat() function says it once:
.grid {
display: grid;
grid-template-columns: repeat(3, 200px); /* three 200px columns */
grid-template-rows: repeat(2, 100px); /* two 100px rows */
}
You rarely need to define rows explicitly. As items flow in, Grid creates implicit rows automatically. Control their size with grid-auto-rows:
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(100px, auto); /* each new row is at least 100px */
}
The fr Unit, repeat() & minmax()
Fixed pixel columns don't adapt. Grid introduces the fr unit — a "fraction of the available space" — which makes flexible, self-sizing columns trivial.
/* A 1 : 2 : 1 layout — the middle column is twice as wide */
.grid {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
/* Three equal, fully flexible columns */
.equal {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
/* Mix fixed and flexible: 200px sidebar, the rest fluid */
.app {
display: grid;
grid-template-columns: 200px 1fr;
}
💡 fr vs percentages
Why not 33.33%? Because percentages don't account for the gap between tracks — three 33.33% columns plus gaps overflow the container. The fr unit distributes only the leftover space after gaps are subtracted, so it always fits.
minmax(min, max) gives a track a floor and a ceiling — indispensable for responsive design. Combined with repeat() and the auto-fit keyword, you get a grid that adds and removes columns on its own:
/* Fit as many ~250px columns as will fit; each stretches to fill.
Fewer columns on narrow screens, more on wide ones — no media queries. */
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
This one declaration is the workhorse of modern card layouts. Read it as: "repeat a column pattern automatically; each column is at least 250px and at most one fraction of the free space."
📖 auto-fit vs auto-fill
Both create as many columns as fit. The difference shows when items are few: auto-fit collapses the empty trailing tracks so existing items stretch to fill the row. auto-fill keeps those empty tracks reserved, leaving blank space on the right. For most card grids, auto-fit is what you want.
Gaps
Space between tracks is set with gap — the same property you met in Flexbox. It replaces the old grid-gap family.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px; /* same gap for rows and columns */
gap: 20px 30px; /* row-gap 20px, column-gap 30px */
/* Or set each explicitly */
row-gap: 20px;
column-gap: 30px;
}
Because gaps are handled by the grid itself, you never touch item margins for spacing — cleaner CSS and no double-margin bugs at the edges.
Placing Items
By default items flow into cells one after another. To position an item deliberately, refer to the numbered grid lines it should start and end on.
/* Make an item start at column line 1 and end at line 3 (spanning 2 cols) */
.item {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 2;
}
/* The readable shorthand — start / end */
.item {
grid-column: 1 / 3;
grid-row: 1 / 2;
}
/* Say how many tracks to span instead of the end line */
.item {
grid-column: 1 / span 2; /* start at line 1, cover 2 columns */
}
/* Negative line numbers count from the end. -1 is the last line. */
.full-width {
grid-column: 1 / -1; /* stretch across every column */
}
The 1 / -1 trick is a favorite: a hero banner or footer that always spans the full grid, no matter how many columns you defined.
Auto-placement flow
.grid {
grid-auto-flow: row; /* default — fill left-to-right, top-to-bottom */
grid-auto-flow: column; /* fill top-to-bottom, then next column */
grid-auto-flow: row dense; /* backfill earlier gaps left by spanning items */
}
Named Template Areas
This is Grid's most beautiful feature. Instead of counting line numbers, you draw your layout as ASCII art and name each region. The CSS becomes a picture of the page.
.layout {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
gap: 20px;
min-height: 100vh;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
}
/* Assign each element to a named area — that's the whole placement */
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }
grid-template-areas strings above render exactly this layout — the CSS mirrors the picture.Use a dot (.) to leave a cell empty:
.grid {
grid-template-areas:
"header header header"
"sidebar . aside" /* the middle cell stays blank */
"footer footer footer";
}
The best part: rearranging the whole page for mobile is just rewriting these strings inside a media query — the HTML never changes.
Responsive Grids
There are two complementary approaches to responsive Grid: let it adapt automatically, or redraw the areas at breakpoints.
1. Automatic — no media queries
/* Columns appear and disappear as the container resizes */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
}
2. Mobile-first with redrawn areas
/* Base: single column, stacked (mobile) */
.layout {
display: grid;
gap: 20px;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
/* Tablet: sidebar joins main */
@media (min-width: 768px) {
.layout {
grid-template-columns: 200px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
}
/* Desktop: three columns */
@media (min-width: 1024px) {
.layout {
grid-template-columns: 200px 1fr 200px;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
}
}
Notice how the same five elements simply move to new positions at each breakpoint — the markup is written once, the layout is described three ways.
Grid vs Flexbox
The question isn't "which is better" — it's "which dimension am I working in?"
| Aspect | Flexbox | Grid |
|---|---|---|
| Dimensions | One (row or column) | Two (rows and columns) |
| Best for | Content-driven, aligning items in a line | Layout-driven, overall page structure |
| Sizing model | Items decide, container aligns | Container defines the grid, items fill it |
| Overlapping items | Not really | Yes — items can share cells |
Rule of thumb: reach for Grid to define the page, and Flexbox to arrange the components inside each grid area. Using both together is the norm, not the exception.
Practice & Quiz
🏋️ Exercise 1: An auto-fitting card grid
Goal: Display a set of cards that show as many columns as fit (each at least 240px), stretch to fill the row, and reflow with no media queries.
<div class="cards">
<article class="card">Card 1</article>
<article class="card">Card 2</article>
<article class="card">Card 3</article>
<article class="card">Card 4</article>
</div>
💡 Hint
One line does it: grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); plus a gap.
✅ Solution
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1rem;
}
.card {
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
🏋️ Exercise 2: A named-area page layout
Goal: Build a header, sidebar, main, and footer where the header and footer span the full width and the sidebar sits left of the main content — using grid-template-areas.
💡 Hint
Define two columns (200px 1fr), then draw three area rows. Give each element a matching grid-area name.
✅ Solution
.page {
display: grid;
min-height: 100vh;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
gap: 1rem;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
.page-header { grid-area: header; }
.page-sidebar { grid-area: sidebar; }
.page-main { grid-area: main; }
.page-footer { grid-area: footer; }
🎯 Quick Quiz
Question 1: What does the fr unit represent?
Question 2: Which declaration builds a responsive card grid with no media queries?
Question 3: What does grid-column: 1 / -1; do?
Best Practices & Pitfalls
✅ Do
- Use Grid for two-dimensional page structure and Flexbox for the pieces inside
- Prefer
frunits andminmax()over fixed pixels for flexible tracks - Reach for
repeat(auto-fit, minmax(...))before writing media queries - Use
grid-template-areasfor readable, self-documenting layouts - Let implicit rows and
grid-auto-rowshandle content of unknown length
❌ Don't
- Use percentage columns when there's a
gap— they overflow; usefr - Hand-place every item with line numbers when a template area would be clearer
- Reach for Grid on a simple one-line row — Flexbox is lighter for that
- Forget that grid lines start at 1, not 0
✅ Debug with your browser's Grid inspector
Firefox and Chrome DevTools both draw an overlay of your grid lines, track numbers, and area names right on the page. When a layout misbehaves, toggle the grid overlay — you'll usually spot the off-by-one line or a mis-sized track in seconds.
Summary
🎉 Key Takeaways
- Grid is two-dimensional:
display: gridplusgrid-template-columns/rowsdefines the structure - The
frunit shares free space;repeat()andminmax()keep tracks flexible repeat(auto-fit, minmax(250px, 1fr))builds a responsive card grid with no media queries- Place items by grid lines (
grid-column: 1 / -1) or by named areas - Use Grid for the page and Flexbox for the components inside it
📚 Additional Resources
🚀 What's Next?
You can now build layouts in one dimension (Flexbox) and two (Grid). The final piece is making them adapt to every screen. Next: Responsive design principles — the viewport meta tag, media queries, fluid typography with clamp(), and a mobile-first workflow.
🎉 Well done!
Grid is the most capable layout tool CSS has ever shipped — and you've just learned its core. Every dashboard and landing page you build from here gets easier.