#The grid model: lines, tracks, cells and areas
AddedCSS Grid lays items out in rows and columns at the same time. You declare display: grid on a parent (the grid container), describe its columns and rows, and its direct children (the grid items) are placed into the result. Flexbox, by contrast, lays items out along one line at a time (see The flex model: main axis and cross axis); grid is the tool when you want things to line up in both directions.
The mental model is a sheet of graph paper with numbered lines. The lines are the real coordinates: a grid with three columns has four column lines, numbered 1 to 4 from the start edge, and also -1 to -4 from the end edge. The space between two neighbouring lines is a track (a column or a row). Where a column track crosses a row track you get a cell, the smallest unit. A rectangle of one or more cells is a grid area, and every item occupies exactly one area. A gap is a gutter between tracks: it isn't a track, and nothing can be placed in it.
| Term | What it is | In the example below |
|---|---|---|
| Grid line | A dividing line, numbered from 1 (or from -1 at the end) | 4 column lines, 3 row lines |
| Track | The space between two adjacent lines: a column or a row | 3 columns of 100px, 2 rows of 50px |
| Cell | One column crossed with one row | 6 cells |
| Grid area | A rectangle of cells, bounded by four lines | B covers 2 cells, column lines 2 to 4 |
| Gap (gutter) | Space between tracks, not a track | 10px between every track |
.grid {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 50px 50px;
gap: 10px;
}
.grid > div { background: #dbeafe; color: #1e3a8a; padding: 4px 8px; }
.grid > .b { grid-column: 2 / 4; background: #fde68a; color: #78350f; }<div class="grid">
<div class="a">A</div>
<div class="b">B: lines 2 to 4</div>
<div class="c">C</div>
<div class="d">D</div>
<div class="e">E</div>
</div>display: gridmakes.grida grid container, and its five childdivs become grid items. Only direct children are items: anything nested inside them is laid out normally inside its item.grid-template-columns: 100px 100px 100pxcreates three column tracks, and therefore column lines1,2,3and4.grid-template-rows: 50px 50pxcreates two row tracks and row lines1to3.gap: 10pxputs a 10px gutter between neighbouring tracks in both directions, so the column lines sit at 0, 110, 220 and 320px from the container's left edge (line 2 and line 3 are "thick": a line plus its gutter).Ahas no placement, so auto-placement puts it in the first free cell: column 1, row 1, measured at0, 0with size100 × 50.Bsaysgrid-column: 2 / 4: start at column line 2, end at column line 4. Its area covers two cells and the gap between them, measured210pxwide (100 + 10 + 100) starting at110px. Its row isn't set, so auto-placement keeps it in row 1, where columns 2 and 3 are still free.- Row 1 is now full, so
C,DandEflow into row 2 at0,110and220px, starting60pxdown (50px row plus 10px gap). That's the whole model: lines are coordinates, tracks are the spaces between them, and every item gets a rectangle of cells.
A grid container's grid only applies to its children. A grandchild with grid-column set does nothing, because its parent isn't a grid. That surprises people who wrap items in an extra div (a link, a <li>, a component's root) and then wonder why placement stopped working. display: contents on the wrapper removes the wrapper's own box, so its children are promoted to items of the outer grid. Subgrid is the other answer, when the wrapper must keep its box.
.grid {
display: grid;
grid-template-columns: 80px 80px 80px;
gap: 10px;
margin-bottom: 12px;
}
.grid div { height: 30px; background: #dcfce7; color: #166534; font-size: 13px; }
.grid .wrap { height: auto; background: #fef3c7; }
.grid .late { grid-column: 3; background: #fbcfe8; color: #831843; }
.contents .wrap { display: contents; }<div class="grid plain">
<div class="wrap"><div>1</div><div class="late">2: col 3?</div></div>
</div>
<div class="grid contents">
<div class="wrap"><div>1</div><div class="late">2: col 3</div></div>
</div>- In the first grid, the only grid item is
.wrap. It gets column 1 (80pxwide, measured). Its two children are ordinary blocks stacked inside it. .latehasgrid-column: 3, but its parent.wrapisn't a grid container, so the property doesn't apply. It sits at0px, inside the wrapper, in column 1.- In the second grid,
.wraphasdisplay: contents. Its box disappears and its children become the grid's items:1goes to column 1 and.lategoes to column 3, measured180pxfrom the left (80 + 10 + 80 + 10). display: contentshas a cost: the wrapper has no box, so its own background, border and padding are gone. Use it on pure wrappers (a<ul>around<li>s is the classic case) and check that the semantics you need survive in the browsers you support.
A grid has grid-template-columns: repeat(4, 1fr) and grid-template-rows: 100px 100px 100px. How many column lines and row lines are there, and what is line -1 in each direction?
Show answer
Five column lines and four row lines. Column line -1 is line 5, row line -1 is line 4.
- Lines bound tracks, so there's always one more line than tracks: 4 columns → lines
1to5, 3 rows → lines1to4. - Negative numbers count backwards from the end of the explicit grid:
-1is the last line (5for columns,4for rows),-2is the one before it, and so on. - So
grid-column: 1 / -1means1 / 5, which spans all four columns. Gaps never add lines; they only widen the existing ones.