1. When Flexbox Isn't Enough
You just learned Flexbox. It's brilliant for one-dimensional layouts — a navbar, a row of cards, a vertically-centered hero. But consider this: you want a photo gallery where items sit in neat rows and columns, and the layout snaps to a grid. Or a page layout where a sidebar is exactly 300px wide and the main content fills the rest, across multiple rows.
Flexbox can approximate these, but it's fighting its nature. Flexbox distributes items along one axis. CSS Grid was designed from scratch for two-axis control — columns and rows, simultaneously. (MDN: Grid vs. Flexbox)
The Decision Rule
Flexbox: Lay items out in a line (row or column). Let the content drive the size.Grid: Define a grid structure first, then place items into it. Let the layout drive the size.
In practice, you'll use both together. Grid for the overall page layout. Flexbox inside individual components. They're partners, not competitors.
2. Turning On Grid
Just like Flexbox, one line switches the layout engine:
.layout {
display: grid;
}
By itself, display: grid doesn't change much visually — children still stack vertically. The magic comes when you define the grid structure with columns and rows.
3. Defining Columns
The most important Grid property. You declare how many columns you want and how wide each should be:
.layout {
display: grid;
grid-template-columns: 200px 1fr 1fr;
}
This creates three columns: the first is fixed at 200px, the other two share the remaining space equally. Items flow into these columns automatically, left to right, then wrap to the next row. (MDN: grid-template-columns)
Vocabulary
display: grid4. The fr Unit
You already know px, rem, %. Grid introduces a new one: fr — a fraction of the remaining space. It's like flex: 1 but for Grid tracks.
/* Three equal columns */
grid-template-columns: 1fr 1fr 1fr;
/* Middle column is twice as wide */
grid-template-columns: 1fr 2fr 1fr;
/* Fixed sidebar + flexible main */
grid-template-columns: 300px 1fr;
fr calculates after fixed sizes and gaps are subtracted. So 300px 1fr means: "give 300px to the first column, then give all remaining space to the second." This is the key mental model. (MDN: the fr unit)
fr is gap-aware. If you write grid-template-columns: 50% 50% and add a gap, the total exceeds 100% and causes overflow. 1fr 1fr never overflows — it divides the space after gaps are removed.
5. Grid Playground — Try It Live
Change the properties below and watch the grid update instantly. Pay attention to how grid-template-columns reshapes the entire layout:
6. Defining Rows
Rows work just like columns:
.layout {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 100px 200px;
}
This creates two explicitly-defined rows: the first 100px tall, the second 200px tall. If you have more items than cells (3 columns × 2 rows = 6 cells), extra items create implicit rows — rows the browser generates automatically. (MDN: grid-template-rows)
grid-template-rows when you need precise row heights (like a dashboard header + body + footer).
7. The gap Property
You know gap from Flexbox — it's exactly the same here:
/* Equal gap on rows and columns */
gap: 16px;
/* Different row and column gaps */
gap: 24px 16px; /* row-gap column-gap */
/* Or use the longhand properties */
row-gap: 24px;
column-gap: 16px;
Unlike margins, gap only creates space between tracks — never at the edges. This is why it's better than padding hacks. (MDN: gap)
8. The repeat() Function
Writing 1fr 1fr 1fr 1fr gets tedious. repeat() is the shorthand:
/* These are identical */
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-columns: repeat(4, 1fr);
/* Mix with fixed sizes */
grid-template-columns: 200px repeat(3, 1fr);
The first argument is the number of repetitions, the second is the track size to repeat. You can mix repeat() with other values freely. (MDN: repeat())
9. Responsive Grids: minmax() + auto-fill
This is the most powerful line of CSS you'll learn today. It creates a fully responsive grid without media queries:
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
}
Let's break this down:
minmax(250px, 1fr)— each column is at least 250px wide, but can grow to fill available spaceauto-fill— create as many columns as will fit in the container width- On a wide screen? 4 columns. Narrow tablet? 2 columns. Phone? 1 column. No media queries needed.
Try resizing your browser window with this playground to see it in action:
auto-fill creates empty columns if there's leftover space. auto-fit collapses empty columns, stretching items to fill the container. For most use cases (card grids, galleries), auto-fill is what you want. (CSS-Tricks: auto-fill vs auto-fit)
10. Placing Items on the Grid
So far, items flow into cells automatically, left to right, top to bottom. But Grid's real power is explicit placement — telling an item exactly where to sit and how many cells to span.
Grid Lines
Every grid has numbered lines. A 3-column grid has 4 column lines (1 through 4) and rows work the same way:
col 1 col 2 col 3 col 4
| | | |
----+--------+--------+--------+---- row 1
| Cell 1 | Cell 2 | Cell 3 |
----+--------+--------+--------+---- row 2
| Cell 4 | Cell 5 | Cell 6 |
----+--------+--------+--------+---- row 3
Lines are numbered starting from 1, not 0. A 3-column grid has column lines 1, 2, 3, 4.
Spanning Multiple Cells
Use grid-column and grid-row on a grid item (not the container) to control its placement:
/* Span columns 1 through 3 (takes up full width of 3-col grid) */
.header {
grid-column: 1 / 4;
}
/* Span 2 columns from wherever the item naturally lands */
.wide-card {
grid-column: span 2;
}
/* Span 2 rows */
.sidebar {
grid-row: 1 / 3;
}
The syntax is start-line / end-line. So 1 / 4 means "start at line 1, end at line 4" (spanning 3 tracks). The span keyword is a shortcut: span 2 means "take up 2 tracks from wherever I am." (MDN: grid-column)
grid-column: 1 / -1 means "start at the first line, end at the last line." This spans the entire row regardless of how many columns exist. Negative numbers count from the end.
11. Alignment in Grid
Grid gives you four alignment properties. They work in pairs:
| Property | Applies To | Axis | What It Does |
|---|---|---|---|
justify-items |
All items (on container) | Inline (horizontal) | Aligns items within their cells, horizontally |
align-items |
All items (on container) | Block (vertical) | Aligns items within their cells, vertically |
justify-content |
The grid itself (on container) | Inline (horizontal) | Aligns the entire grid within the container |
align-content |
The grid itself (on container) | Block (vertical) | Aligns the entire grid within the container |
You'll use justify-items and align-items most often. Both default to stretch, which is why grid items fill their cells by default. Change them to start, end, or center for different effects. (MDN: Box alignment in Grid)
12. Three Patterns You'll Use Constantly
Pattern 1: The Holy Grail Layout
Header, sidebar, main content, footer — the classic page layout, done in 6 lines:
.page {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
.page-header { grid-column: 1 / -1; }
.page-footer { grid-column: 1 / -1; }
The header and footer span all columns with 1 / -1. The sidebar and main content sit in the middle row, each in its own column — automatically.
Pattern 2: Responsive Card Grid
No media queries needed — the single most useful Grid pattern:
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
Pattern 3: Dashboard Grid with Spanning
A dashboard where some widgets are wider or taller than others:
.dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.widget-wide { grid-column: span 2; }
.widget-tall { grid-row: span 2; }
.widget-big { grid-column: span 2; grid-row: span 2; }
13. The Grid + Flexbox Decision Cheatsheet
| Scenario | Use | Why |
|---|---|---|
| Navigation bar | Flexbox | One row of items with flexible spacing |
| Card grid / gallery | Grid | Two-dimensional layout, neat rows + columns |
| Sidebar + content page layout | Grid | Column structure across multiple rows |
| Centering a single element | Either | Grid: place-items: center. Flexbox: justify-content + align-items |
| A button group | Flexbox | Items in a line, content-sized |
| Dashboard with spanning widgets | Grid | Explicit placement and spanning across rows/columns |
| Overall page structure | Grid | Header/sidebar/main/footer is a 2D layout problem |
14. Test Your Knowledge
Quiz: CSS Grid Fundamentals
fr unit represent in CSS Grid?fr divides the remaining space after fixed sizes and gaps are subtracted.fr represents a fraction of the space left after fixed-size tracks and gaps are accounted for.grid-column: 1 / -1 do to a grid item?1 / -1 means "start at line 1, end at the last line" — spanning everything.-1 refers to the last grid line, so 1 / -1 spans from the first to the last line — the entire row.repeat(auto-fill, minmax(250px, 1fr)) achieve?auto-fill tells the browser "fit as many columns as possible," and minmax(250px, 1fr) sets each column's size range.space-between is the right tool.fr preferred over % for Grid column sizing?fr divides space after gaps are subtracted, preventing overflow. 50% + 50% + gap exceeds 100%.50% + 50% already fills the container, but then gap adds extra space, causing overflow. fr is gap-aware.15. Retrieval Practice: Fill In The Blanks
Close your eyes, think, then type. Effortful recall is what moves knowledge from short-term to long-term memory.
16. Build Challenge: Dashboard Layout with Grid
Build a mini analytics dashboard from scratch. This is a real-world layout — the kind you'd see in any SaaS product. Use Grid for the overall page structure, and Flexbox for components inside each widget.
- Create
practices/lesson7-dashboard.htmlandpractices/lesson7-style.css - Build the page layout using Grid:
- Full-width header spanning all columns:
grid-column: 1 / -1 - A 4-column grid for the main content area
- Full-width footer spanning all columns
- Full-width header spanning all columns:
- Build a stats row — 4 cards in a row:
- Each card shows a metric name and number (e.g., "Total Users: 12,847")
- Use
repeat(4, 1fr)orrepeat(auto-fill, minmax(200px, 1fr))
- Build a widget area below the stats:
- One "wide" widget spanning 2 columns (a chart placeholder)
- Two regular widgets in the remaining columns
- Use
grid-column: span 2for the wide one
- Make it responsive:
- Use
repeat(auto-fill, minmax(280px, 1fr))so cards reflow automatically - Or add one
@mediaquery to switch to 1 column on mobile
- Use
Primary Sources: CSS-Tricks: A Complete Guide to CSS Grid is the canonical Grid reference — bookmark it alongside the Flexbox guide. Also: MDN: CSS Grid Layout for the official spec, and Kevin Powell for visual demonstrations of Grid in action.
Finished the build? Type /teach and show me your dashboard. I'll review the layout, check your Grid + Flexbox decisions, and then we'll move on to Responsive Design.
minmax(), or placement syntax is unclear, ask me. I'll explain it differently until it clicks. Don't move on with uncertainty.