Building CSS Grid Layouts Visually: Columns, Rows, and the fr Unit
A practical guide to building CSS grid layouts visually — columns, rows, gaps, the fr unit, named template areas, and responsive grids you can ship.
Building CSS Grid Layouts Visually: Columns, Rows, and the fr Unit
CSS Grid is the first layout system in the browser that lets you think in two dimensions at once. You decide how many columns and rows a container has, how each track sizes itself, and where each child sits — all without floats, without a grid framework, without nesting six wrapper divs. The catch is that the syntax is dense, and the gap between "I know roughly what I want" and "I have the correct CSS" is wider than it should be. That gap is exactly what a visual builder closes: you drag the tracks, name the regions, watch the preview, and copy code that already works.
This post walks through the properties that matter, the one unit that makes grids feel effortless, and how to keep a layout from falling apart on small screens.
Start With Columns and Rows
A grid container is born the moment you write display: grid. From there, two properties shape the skeleton: grid-template-columns defines the vertical tracks, and grid-template-rows defines the horizontal ones. Each takes a space-separated list of track sizes, and the number of values is the number of tracks.
The values can be anything the browser understands as a track size: a fixed 200px for a sidebar that should never move, auto for a track that hugs its content, minmax(100px, 1fr) for a track that has a floor but can grow, or a raw fraction. You mix and match freely — 200px 1fr 1fr is a fixed left column with two flexible columns next to it.
When you build this visually, you set columns from 1 to 12 and pick each track's size from a small input instead of memorizing the order. The preview updates as you type, so you never have to count tracks in your head or guess why the fourth column is missing.
The fr Unit Splits Free Space Into Fractions
The single most useful piece of Grid is the fr unit. One fr means "one fraction of the free space left over after fixed tracks are subtracted." Write 1fr 1fr 1fr and the browser carves the remaining width into three equal columns. Write 2fr 1fr and the first column gets twice the leftover space of the second. There is no percentage math, no calc(), no worrying that your columns add up to 99% or 101% — the fractions always resolve to exactly the available room.
This is why repeat(3, 1fr) is the workhorse of card grids. It says "three equal columns, share the space," and it stays equal whether the container is 600px or 1400px wide. Fixed and fractional tracks live happily together: 280px 1fr gives you a fixed-width sidebar and a main column that absorbs everything else, the classic app shell in two values.
One concrete gotcha worth internalizing: default grid tracks are really minmax(auto, 1fr), and that auto floor means a track refuses to shrink below its largest unbreakable child. A long URL or a wide image can blow the column out of its container. The fix is minmax(0, 1fr) — now the track can shrink past its content and overflow or word-break actually takes effect. It solves the overwhelming majority of "why is my grid broken" moments.
Gap Adds the Gutters
Spacing between tracks used to mean margins on every child and a mental tax to keep them even. Grid replaces all of that with one property: gap. Write gap: 16px and every gutter between rows and columns is 16 pixels, with no extra space leaking around the outer edge of the grid. You can split it — row-gap and column-gap — when you want tighter vertical rhythm than horizontal, and the values take px or rem like any other length.
gap is the modern name; the older grid-gap is just an alias kept for backward compatibility. Use gap, because it also works inside Flexbox now, and writing both is redundant.
A Worked Example
Here is a small product layout: three equal columns, a 24-pixel gutter, and one card that spans two columns to act as a feature tile.
.product-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto;
gap: 24px;
}
.product-grid .feature {
grid-column: 1 / 3; /* span the first two columns */
grid-row: 1;
}
.product-grid .promo {
grid-column: 3;
grid-row: 1 / 3; /* tall card down the right side */
}
<div class="product-grid">
<div class="feature">Featured release</div>
<div class="promo">Limited promo</div>
<div>Card three</div>
<div>Card four</div>
<div>Card five</div>
</div>
The feature card claims columns 1 through 2 on the top row, the promo card stretches down the right edge across both rows, and the remaining cards flow into the open cells automatically. Notice how little code this is — three declarations on the container, two placements on the special children, and the browser handles the rest of the flow placement for you. You can build this exact structure in the CSS Grid Visual Generator, drag the spans until they match, and copy the CSS plus a matching HTML scaffold in one paste.
Name Your Template Areas for Readable Layouts
Line numbers like grid-column: 1 / 3 are precise but hard to read once a layout grows past a handful of regions. This is where grid-template-areas earns its keep. You draw the layout in ASCII strings, naming each cell, then point each child at its name:
.page {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 16px;
}
.page header { grid-area: header; }
.page nav { grid-area: sidebar; }
.page main { grid-area: main; }
.page footer { grid-area: footer; }
Repeating a name across neighbouring cells merges them into one region — header spans both columns because it appears twice on its row. Two rules govern it: every cell must be assigned (use . for a deliberately empty cell), and each area must form a rectangle, never an L-shape. For page shells with five to ten regions, named areas are far more readable than line numbers, and anyone reading the stylesheet can picture the layout instantly.
Responsive Grids Without the Copy-Paste Tax
A real layout needs to change shape on smaller screens. The pattern is straightforward: redefine the grid inside a media query. A two-column desktop shell collapses to a single stacked column on tablet, and the sidebar can disappear entirely on mobile.
@media (max-width: 1024px) {
.page {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"sidebar"
"main"
"footer";
}
}
@media (max-width: 640px) {
.page nav { display: none; }
}
Because named areas describe intent rather than coordinates, re-drawing the layout for tablet is mostly a matter of rewriting the strings. The visual generator lets you design all three breakpoint states and wraps the overrides in the @media blocks for you, so you stop hand-tuning three near-identical CSS rules.
I rebuilt my own portfolio's homepage with this approach last month. The old version used a tangle of Flexbox wrappers, and every responsive tweak meant chasing margins across four nested containers. Switching the shell to a single named-area grid cut the layout CSS roughly in half, and the tablet breakpoint became three lines instead of a fresh wrapper. When I needed Flexbox again, it was only for the row of nav links inside the header — Grid for the page, Flex for the line of things that wraps. If you want to feel that one-dimensional case directly, the Flexbox Playground Generator is the right tool for those wrapping rows.
Grid or Flexbox?
The honest rule of thumb: if you'd sketch the layout on graph paper, reach for Grid. If you'd describe it as "a row of things that wrap," reach for Flexbox. Most production pages use Grid for the page shell and Flex inside each cell — they compose, and you rarely have to choose just one. Grid gives you the two-dimensional control and the readable named regions; Flex gives you effortless single-axis alignment. Build the skeleton visually, copy the code, and spend your time on the content instead of counting grid lines.
Made by Toolora · Updated 2026-06-13