CSS Grid is the most powerful way to arrange web pages on a two-dimensional system of rows and columns. After years of wrestling with float, tables and fragile negative margins, Grid finally made it possible to control vertical and horizontal placement at the same time by writing a few lines on a container. In this guide we will move step by step, from the core concepts to building a real page skeleton.
Why is Grid different from Flexbox?
Both are modern layout tools, but they were designed for different jobs. Flexbox is one-dimensional: it aligns items along a single row or a single column. CSS Grid is two-dimensional: it defines rows and columns together, so you can place an item in exactly the cell you want on both axes.
- Flexbox: single-axis flows such as a navigation bar, a button group, or content inside a card.
- Grid: genuinely table-like structures such as a page skeleton, a gallery, a dashboard, or a card grid.
In practice you use them together: Grid for the outer skeleton, Flexbox inside the cards.
Your first grid: row and column templates
When you give an element display: grid, it becomes a grid container and its direct children become grid items. You define columns with grid-template-columns and rows with grid-template-rows:
.gallery {
display: grid;
grid-template-columns: 200px 200px 200px;
gap: 16px;
}
This creates three fixed columns with 16px of space between them. But fixed pixel values are not flexible. This is where my favourite Grid unit comes in: fr (the fraction unit). fr divides the available space into proportions:
.gallery {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
}
Now the three columns share the available width equally. Had you written 2fr 1fr 1fr, the first column would be twice as wide as the others.
repeat(), minmax() and real flexibility
Instead of writing the same value over and over, use the repeat() function. minmax() gives a column a lower and an upper bound. Combine the two and you get a responsive grid:
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 24px;
}
This single line is remarkably powerful. With auto-fit the browser works out how many columns fit on screen while keeping each one at least 220px wide, then spreads the remaining space evenly thanks to 1fr. The result: a card grid that shows four columns on a wide screen, two on a tablet and one on a phone, without a single media query.
The difference between auto-fit and auto-fill is subtle: auto-fit collapses empty tracks and stretches the existing items, while auto-fill keeps the invisible empty columns. For most card layouts auto-fit feels more natural.
A page skeleton with named areas
The most readable feature of Grid is grid-template-areas. You describe the layout almost like an ASCII drawing:
.page {
display: grid;
grid-template-columns: 220px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"sidebar header"
"sidebar content"
"sidebar footer";
min-height: 100vh;
gap: 16px;
}
.page header { grid-area: header; }
.page aside { grid-area: sidebar; }
.page main { grid-area: content; }
.page footer { grid-area: footer; }
This builds a classic application layout: a fixed-width sidebar on the left and a header, content and footer stacked on the right. To switch to a single column on mobile, you only need to redefine the template:
@media (max-width: 760px) {
.page {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"content"
"footer"
"sidebar";
}
}
Without touching the HTML, we moved the sidebar to the bottom just by changing the template. That maintainability is exactly what makes Grid shine.
Placing items by line numbers
Grid lines are numbered starting from 1. You decide how many columns or rows an item spans with grid-column and grid-row:
.featured {
grid-column: 1 / 3; /* from line 1 to line 3: spans two columns */
grid-row: 1 / 2;
}
The span keyword is more readable: grid-column: span 2; tells an item to cover two columns from wherever it sits. To align cells, use justify-items (horizontal) and align-items (vertical); to align a single item on its own, there are justify-self and align-self.
Common mistakes
- Using Grid for everything: a one-line menu stays simpler with Flexbox.
- Setting fixed heights: leave rows on
autoand let them grow with the content. - Using margin instead of
gap: Grid'sgapmanages spacing consistently without bleeding onto the edges. - Confusing auto-fit / auto-fill: if you see unexpected empty columns, you have most likely picked the wrong one.
Frequently Asked Questions
Which browsers support CSS Grid?
Every modern browser (Chrome, Firefox, Safari, Edge) has fully supported CSS Grid for years. Apart from legacy Internet Explorer there is practically no compatibility concern left, so you can safely use it in production on today's projects.
Should I use Grid or Flexbox?
If you are arranging content on two axes (both rows and columns), reach for Grid; if you are lining things up on a single axis, use Flexbox. In most real projects they nest together: Grid for the outer skeleton, Flexbox for alignment inside components.
Is the fr unit better than percentages?
Usually yes. fr distributes the remaining space while automatically accounting for gap spacing, whereas with percentages you have to subtract the gaps yourself. That makes fr a safer and cleaner choice for proportions inside a grid.
Need a fast, responsive, Grid-based interface for your project? I build clean, maintainable layouts with modern CSS. Get in touch to talk through your idea.