Why CSS Grid Changed Web Layout Forever
Before CSS Grid, web designers wrestled with floats, clearfix hacks, and positioning tricks to create anything more complex than a basic column layout. Tables were the go-to for complex layouts well into the 2000s — and tables were never meant for design. Flexbox helped, but it fundamentally works in one dimension: a row or a column, not both at the same time.
CSS Grid, introduced in modern browsers around 2017, finally gave designers a proper two-dimensional layout system. With Grid, you can control both rows and columns simultaneously. You can create magazine-style layouts with overlapping elements, card grids that reflow automatically, and the mythical "holy grail" layout — header, footer, sidebar, and main content — in just a few lines of CSS. No hacks, no floats, no JavaScript.
Core Concepts: Container vs Items
Grid has two layers: the grid container and the grid items. The container is the parent element that holds everything. You turn any element into a grid container with one CSS property: display: grid. Every direct child of the container automatically becomes a grid item.
Once an element is a grid container, you gain access to a powerful set of properties for controlling how items are arranged. The key insight is that you control the grid itself on the container — you define tracks (rows and columns) — and then items either flow into those tracks automatically or you can explicitly position them.
Defining Rows and Columns with grid-template
The most important Grid properties are grid-template-columns and grid-template-rows. These let you define the size of each track (row or column) in your grid. You can use pixels, percentages, or the new fr unit:
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr;
grid-template-rows: auto 300px auto;
gap: 1rem;
}
This creates a three-column grid: the first column is 200px wide, and the second and third columns each take one fraction of the remaining space. The rows are auto-sized for content, then a fixed 300px middle row, then auto for the footer row.
The fr Unit: Fractions Made Simple
The fr unit is Grid's gift to designers. It stands for "fraction of available space." Unlike percentages, which are calculated before gaps are removed, fr units are calculated after. If you have a container 1000px wide with a 20px gap and three columns of 1fr 2fr 1fr, the math works out: 1000px minus 40px in gaps = 960px. That 960px is divided into 4 parts (1+2+1), giving you columns of 240px, 480px, and 240px. No percentages can do that reliably.
The real power comes with auto-fit and minmax(). Write grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) and your grid becomes fully responsive without a single media query. It fits as many 250px columns as possible and wraps automatically.
The Gap Property
The gap property controls the space between grid items. It replaced the older grid-gap syntax. You can set row gaps and column gaps separately: gap: 1rem 2rem gives you 1rem between rows and 2rem between columns. The shorthand gap: 1rem applies the same spacing in both directions.
Gaps are remarkably clean — they only appear between items, not on the outer edges of the grid. This means you don't need margin hacks on the first or last item to account for gutters. The browser handles the edges automatically.
Named Grid Areas for Readable Layouts
Grid lets you name sections of your layout and then assign items to those named areas. It's a surprisingly readable approach:
.container {
display: grid;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
grid-template-columns: 200px 1fr 150px;
grid-template-rows: auto 1fr auto;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }
This approach is intuitive enough that a designer could read the template definition and visualize the layout immediately. It's also easy to refactor — change the template and the whole layout shifts.
When to Use CSS Grid vs Flexbox
Both Grid and Flexbox are modern layout tools, but they're designed for different problems. Use Flexbox when you're laying out items in a single direction — a navigation bar, a row of buttons, a list of items that should wrap but align individually. Flexbox distributes space among items based on their content and the available room.
Use Grid when you want to control the overall arrangement of a layout — both rows and columns. Grid is for when the positions of items relative to each other matter, not just their individual sizing. A card grid with equal-height columns is a Grid job. A navigation bar with items spaced evenly is a Flexbox job. Most complex pages use both: Grid for the page structure and Flexbox for component-level alignment.
Common Grid Layout Patterns
Card Grid: The most common use. grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)) creates a responsive card grid that adds columns as room allows and wraps to the next row. No media queries needed.
The Holy Grail Layout: Header, footer, two sidebars, and main content. Named grid areas make this clean and maintainable.
Magazine Layout: With Grid's spanning abilities, you can make some items span multiple rows or columns. A featured article can span two columns while regular articles sit in single columns. This is where Grid truly shines over Flexbox.
Offset Items: You can start items at specific grid lines with grid-column: 2 / 4 or grid-row: 1 / 3. This lets you deliberately break the flow and position items exactly where you want them.