Design FIELD NOTES · 4 MIN READ
CSS Grid: Tracks, fr Units, and Gaps
How grid-template-columns and rows define the skeleton: track sizes, the fr unit, repeat(), minmax(), and gap.
Grid looks intimidating because its grammar is dense — repeat(auto-fit, minmax(180px, 1fr)) — but the grammar has exactly three moving parts: how many tracks, how wide each one is, and how far apart they sit. Learn those three and every grid template on the web reads like a sentence.
Tracks: the skeleton of the layout
grid-template-columns and grid-template-rows define tracks — the columns and rows items snap into. Each value in the space-separated list is one track, and the list order is the layout order: grid-template-columns: 200px 1fr auto means a fixed 200px first column, a flexible second, and a third sized to whatever it contains. That trio is the sidebar-content-metadata layout, written in one line.
Track sizes come in four flavors. Fixed units (200px, 12rem) never budge. Percentages (30%) are shares of the container but overflow when summed past 100% once gaps exist. Keywords (auto, min-content, max-content) size to the content. And fr — the one that makes Grid feel magic — distributes the leftover space.
The fr unit: shares of what is left
One fr is one share of the free space remaining after fixed tracks and gaps are paid for. In 200px 1fr 2fr on a 1000px container, the 200px column goes first, then the remaining 800px splits 1:2 — so 266.67px and 533.33px. Crucially, repeat(3, 1fr) always fits the container exactly, while 30% 30% 30% overflows before any gap is added. That difference is why fr is the default choice for anything flexible and percentages are the exception.
display: grid;
grid-template-columns: 200px 1fr 2fr; /* 200px, then 1:2 of the rest */
repeat() and minmax(): the patterns worth memorizing
repeat(3, 1fr) is shorthand for 1fr 1fr 1fr — use it whenever tracks are equal, because the count is visible at a glance. The function accepts patterns too: repeat(2, 200px 1fr) tiles a two-track pattern. The combination that ends most responsive stylesheets is:
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
Read it inside-out: each column is at least 180px wide but may grow to an equal share (1fr) of the leftover space; auto-fit means "fit as many as possible". The result is a card grid that adds columns as the screen widens — no media queries. Its sibling auto-fill is identical except it keeps empty tracks alive when items run out, holding the layout's column count stable.
Implicit rows: the ones you didn't write
Card layouts usually declare only the columns. When items exceed the explicit rows — or when no rows are declared at all — the grid creates implicit rows sized to their content. That is the "leave rows empty" advice in practice: content-driven row heights are usually what you want, and grid-auto-rows styles the implicit ones when a minimum matters, e.g. minmax(100px, auto). Declare explicit rows only when the vertical rhythm is part of the design: repeat(2, 200px) for fixed-height rows, or a single auto plus implicit rows for hybrid layouts.
gap: the spacing that replaced the hacks
gap spaces tracks on both axes with one declaration — gap: 12px, or gap: 12px 24px for separate column and row gaps. It only sits between tracks: nothing is added before the first column or after the last row, so containers stay flush with their parents without negative-margin cleanup. That edge behavior is exactly what the old margin + :nth-child workflows tried to fake, and gap does it while surviving any change to the item count.
One nuance worth knowing: gap participates in the fr calculation before fr tracks divide the leftovers, so repeat(3, 1fr) with a 12px gap yields three equal columns each 12px narrower than the no-gap case — the gaps are paid first, the shares divide what remains.
Try it yourself
The CSS Grid Generator accepts either a plain track count or a full track list, validates every token against the real CSS grammar, and emits the declaration block with a live preview of the tracks. Pair it with the CSS Flexbox Generator for one-directional flows inside each cell, and the CSS Unit Converter when a design handoff swaps px for rem.