Module 5: Layout & Positioning
CSS Grid: Two-Dimensional Layout with Tracks, Lines and Areas
How grid defines rows and columns, how fr and minmax size tracks, how to place items by line or by name, and how auto-fit builds responsive grids without media queries.
Grid lays out items in rows and columns at the same time. Flexbox lets content decide the layout. Grid lets you define the structure first and then place content into it. For page layouts, galleries, dashboards and anything where things should line up in both directions, it is the right tool.
The structure of a grid#
.grid {
display: grid;
grid-template-columns: 200px 1fr 1fr;
grid-template-rows: auto auto;
gap: 1rem;
}
A grid is made of lines and tracks. Lines are the dividers, numbered starting at 1. Tracks are the spaces between lines: columns and rows. A cell is one row and one column track crossing.
line 1 line 2 line 3 line 4
│ col 1 │ col 2 │ col 3 │
line 1 ────┼─────────┼─────────┼─────────┤
row 1 │ │ │ │
line 2 ────┼─────────┼─────────┼─────────┤
row 2 │ │ │ │
line 3 ────┴─────────┴─────────┴─────────┘
Three columns means four column lines. People new to grid miscount constantly because of this.
Direct children become grid items, exactly as with flexbox, and they fill cells automatically in source order.
Sizing tracks#
grid-template-columns: 200px 1fr 2fr;
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(12, 1fr);
grid-template-columns: 240px minmax(0, 1fr);
grid-template-columns: auto 1fr auto;
- Fixed lengths (
200px,15rem) make rigid tracks. fris a fraction of the leftover space after fixed tracks and gaps have been subtracted.1fr 2frgives the second column twice as much as the first.autosizes a track to fit its content.repeat(n, size)avoids typing the same value over and over.minmax(min, max)gives a track a range: it can shrink tominbut never beyond, and grow up tomax.
The 1fr blowout#
1fr is shorthand for minmax(auto, 1fr). The auto minimum means the track will not shrink below its content's minimum size, so a long word, a wide table or a code block forces the column wider than the grid, and the layout overflows. It is the same issue as min-width: auto in flexbox.
.layout { grid-template-columns: 240px 1fr; } /* can blow out */
.layout { grid-template-columns: 240px minmax(0, 1fr); } /* safe */
Use minmax(0, 1fr) whenever the content of a track can be wide or unpredictable.
Placing items#
By default, items flow into cells one by one. You can place them explicitly using line numbers:
.hero { grid-column: 1 / 4; } /* from line 1 to line 4: spans 3 columns */
.sidebar { grid-column: 1 / 2; grid-row: 2 / 4; }
.wide { grid-column: span 2; } /* span 2 tracks from wherever it lands */
.last { grid-column: 2 / -1; } /* to the final line, whatever the count */
-1 refers to the last line of the explicit grid, which is why 1 / -1 means "full width".
Named areas#
For page-level layouts, naming areas reads like a picture of the page:
.page {
display: grid;
grid-template-columns: 220px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100dvh;
}
.page > header { grid-area: header; }
.page > aside { grid-area: sidebar; }
.page > main { grid-area: main; }
.page > footer { grid-area: footer; }
┌──────────────────────────────┐
│ header │
├────────┬─────────────────────┤
│sidebar │ main │
│ │ │
├────────┴─────────────────────┤
│ footer │
└──────────────────────────────┘
To make it responsive, redefine the areas inside a media query and the entire page restructures with no changes to the HTML:
@media (max-width: 700px) {
.page {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
}
Every row of the template must have the same number of cells, and each named area must be a single rectangle. A "L-shaped" area is invalid and the whole property is dropped.
Explicit and implicit grids#
The tracks you define with grid-template-* are the explicit grid. If more items arrive than cells exist, or you place an item outside it, the browser creates extra tracks, the implicit grid, sized by:
.grid {
grid-auto-rows: minmax(100px, auto); /* size of rows the browser invents */
grid-auto-flow: row; /* fill rows first (default); or column, or dense */
}
A grid with only grid-template-columns set and no rows defined relies on the implicit grid for every row. That is the normal case for a card gallery.
The responsive gallery with no media queries#
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
gap: 1rem;
}
Reading it from the inside out:
minmax(220px, 1fr): each column is at least 220px and shares extra space equally.repeat(auto-fit, ...): fit as many such columns as the container allows.min(220px, 100%): on a container narrower than 220px, do not force an overflow.
On a 1000px container you get four columns. On 500px, two. On 300px, one. No breakpoint needed.
The difference between the two keywords:
auto-fillcreates as many tracks as fit and keeps empty ones, so items stay at their size, leaving blank space at the end.auto-fitdoes the same, then collapses empty tracks to zero, so the existing items stretch to fill the row.
With few items, auto-fit makes them grow. auto-fill keeps the column size consistent. Choose by looking at what you want a three-item gallery to do on a wide screen.
Alignment#
Grid has the same alignment vocabulary as flexbox, with two sets:
.grid {
justify-items: stretch; /* each item within its cell, horizontally (default stretch) */
align-items: stretch; /* each item within its cell, vertically */
place-items: center; /* shorthand for align-items + justify-items */
justify-content: center; /* the whole grid inside the container, if it is smaller */
align-content: start;
}
.item { justify-self: end; align-self: center; } /* per item */
*-items and *-self position items inside their cells. *-content positions the entire grid of tracks inside the container.
Subgrid, briefly#
A nested grid normally defines its own tracks, which do not line up with the parent. grid-template-columns: subgrid makes a child grid reuse the parent's tracks. It lets items in separate cards, like a title, text and button in each, line up across cards. It is supported in all current major browsers.
Grid or flexbox#
| Question | Choose |
|---|---|
| Do you need rows AND columns to line up? | Grid |
| Is the layout driven by the content's size? | Flexbox |
| One row or one column of items? | Flexbox |
| A page skeleton or a card gallery? | Grid |
They are not rivals. A page is often a grid of regions, each containing a flexbox for its internal arrangement.
Common mistakes#
- Off-by-one on lines. Four lines for three columns, and
spanversus end-line confusion. 1froverflow from long content, fixed withminmax(0, 1fr).- Setting
display: gridon the wrong element. It must be the parent of the items. - Misaligned template areas: uneven rows or a non-rectangular area silently invalidate the declaration.
- Using fixed
pxcolumns that overflow on small screens. - Using
marginfor spacing instead ofgap. - Reordering with
grid-areaororderin a way that no longer matches the keyboard tab order.
Practice#
- Rebuild the page layout from this lesson with named areas, then collapse it to a single column below 700px.
- Build an image gallery with
repeat(auto-fit, minmax(...)). Test it with 3 and with 9 items. Switch toauto-filland compare. - Make a dashboard where one card spans two columns and another spans two rows. Use line numbers for one and
spanfor the other. - Put a wide
preblock in a1frcolumn and show the blowout, then fix it withminmax(0, 1fr).