CSS Grid
Why you need this
Flexbox lays elements out in one line (a row or a column). What if you need a grid — rows and columns at the same time? A photo gallery, a news tile wall, a "header — menu — text — footer" layout. That's what CSS Grid is for. If flex is beads on a string, grid is a chessboard.
Turning on a grid
<style>
.grid {
display: grid;
grid-template-columns: 100px 200px 100px;
gap: 10px;
}
.grid div { background: skyblue; padding: 20px; }
</style>
<div class="grid">
<div>1</div><div>2</div><div>3</div>
<div>4</div><div>5</div><div>6</div>
</div>
Result: two rows of three blocks; the middle column is twice as wide as the side ones; 10px between all blocks.
We didn't set any rows — they are created by themselves when the items don't fit in the first row.
grid-template-columns lists the column widths separated by spaces.
The fr unit
fr (fraction) is a share of the free space:
.grid { display: grid; grid-template-columns: 1fr 2fr 1fr; }
Result: three columns, the middle one twice as wide; together they always take the full width of the container and adapt when the window gets narrower.
You can mix them: 200px 1fr — the left column is 200px, the right one gets the rest.
repeat and minmax
To avoid writing 1fr 1fr 1fr 1fr:
.grid { grid-template-columns: repeat(4, 1fr); }
minmax(min, max) sets a size "from … to". The famous "as many columns as fit" trick:
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
Result: cards no narrower than 200px; 5 in a row on a wide screen, 1 on a phone. Without a single media query.
Rows
.grid { grid-template-rows: 80px 1fr 60px; }
auto is a height based on the content. Rows and columns together form cells, and the lines between
them are numbered from 1: three columns have lines 1, 2, 3, 4.
Spanning several cells
You can tell an item how many cells it takes:
.big {
grid-column: span 2; /* 2 columns */
grid-row: span 2; /* 2 rows */
}
.wide {
grid-column: 1 / 4; /* from line 1 to line 4 = 3 columns */
}
.full {
grid-column: 1 / -1; /* from the first to the last line — full width */
}
Result: .big is a 2×2-cell square and the other items flow around it; .full stretches over the whole row.
Named areas
The most visual way is to draw the layout in words:
.page {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"nav main"
"footer footer";
}
.page header { grid-area: header; }
.page nav { grid-area: nav; }
.page main { grid-area: main; }
.page footer { grid-area: footer; }
Result: a full-width header, below it a 200px menu on the left and the text on the right, a full-width footer at the bottom.
Each string in quotes is one grid row, and the words are cells. Identical neighbouring words merge into
one area. A dot . is an empty cell. Areas must be rectangular.
Grid or Flexbox?
- Flexbox — when items go in one line and their size depends on the content: a menu, buttons, a header.
- Grid — when you need a grid with even columns and rows: a gallery, a page layout.
They are often combined: the page is a grid, the header inside it is flex.
How it works inside
The browser first builds an "invisible lattice" from grid-template-columns and grid-template-rows
(fr columns share what remains after the pixels). Then it places the children into the cells: one by
one from left to right, and items with span, line numbers or grid-area go to their own places.
You can see the lattice in the developer tools: there is a grid badge next to the container.
Common mistakes
display: gridon the children. The grid is turned on for the container.%mixed withgap.25% 25% 25% 25%plusgapwon't fit the width.frdoesn't have this problem.- A typo in area names.
grid-area: hedaer— the item silently drops into an automatic cell. - A non-rectangular area.
"a a" "a b"— an L-shaped areaaisn't allowed, and the whole template breaks. - Setting
widthon the items for the columns. Column widths are set by the container, not the items.
Cheat sheet
| Property | What it does |
|---|---|
display: grid |
turn on the grid |
grid-template-columns: 1fr 2fr |
columns |
grid-template-rows: auto 1fr |
rows |
repeat(3, 1fr) |
repeat |
repeat(auto-fill, minmax(200px, 1fr)) |
as many as fit |
gap: 10px |
gaps |
grid-column: span 2 / 1 / -1 |
span 2 columns / the full width |
grid-row: span 2 |
span 2 rows |
grid-template-areas + grid-area |
a layout by named areas |