Skip to content
WebTricks

A practical guide to CSS Grid

CSS Grid from the ground up — tracks, the fr unit, repeat and minmax, auto-fit, template areas and placement — with copy-paste patterns.

If flexbox handles one direction, CSS Grid handles two at once — rows and columns that line up together. It’s the right tool for page layouts and any design where things share a common alignment. Here’s the working knowledge.

Tracks: rows and columns

A grid is made of tracks — the columns and rows you declare on the container:

.grid {
  display: grid;
  grid-template-columns: 200px 1fr 200px; /* three columns */
  grid-template-rows: auto 1fr auto;      /* three rows */
  gap: 16px;
}

Children flow into the cells automatically, left to right, top to bottom. gap spaces the tracks — no margins needed.

The fr unit — fractions of free space

fr means “a share of the leftover space” after fixed sizes are taken:

  • 1fr 1fr 1fr → three equal columns.
  • 2fr 1fr → the first column is twice as wide as the second.
  • 200px 1fr → a fixed sidebar next to a flexible main area.

fr is what makes grids fluid without percentages or calc().

repeat(), minmax() and the responsive one-liner

repeat() saves typing; minmax() sets a floor and ceiling for a track:

grid-template-columns: repeat(3, 1fr);              /* 1fr 1fr 1fr */
grid-template-columns: repeat(2, minmax(0, 1fr));   /* two safe equal cols */

Combine them with auto-fit for a card grid that reflows with no media queries — fit as many columns as will hold a minimum width, then stretch:

grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));

That’s the whole idea behind the responsive grid with no media queries snippet. (minmax(0, 1fr) also avoids the same overflow trap flexbox has — content forcing a track wider than its share.)

Placing items across tracks

Make an item span multiple tracks with line numbers or span:

.feature {
  grid-column: 1 / 3;   /* from line 1 to line 3 = two columns */
  grid-row: span 2;     /* span two rows */
}

Lines are numbered from 1 at the start; negative numbers count from the end, so grid-column: 1 / -1 means “full width” regardless of column count.

Named areas — layouts you can read

grid-template-areas lets you draw the layout as ASCII art and assign elements to names. It’s the clearest way to express a page shell:

.layout {
  display: grid;
  grid-template:
    "header header" auto
    "nav    main"   1fr
    "footer footer" auto / 160px 1fr;
}
.site-header { grid-area: header; }
.site-main   { grid-area: main; }

Switching to a single column for mobile is just redefining the areas in a media query. The full pattern is in the holy grail layout snippet.

Alignment in two dimensions

Grid has the same axis-aligned properties as flexbox, doubled:

  • justify-items / align-items — position content inside each cell (inline and block axes).
  • justify-content / align-content — position the whole grid when it’s smaller than the container.
  • justify-self / align-self — override alignment for a single item.

A neat trick: put several children in one cell (grid-area: 1 / 1) and they overlap, with align-self / justify-self placing each layer — no position: absolute. See overlap with grid.

Grid or flexbox?

  • One direction, content-driven (toolbars, nav, button rows, a media object) → flexbox.
  • Two directions, where rows and columns align together (page shells, card galleries, dashboards) → grid.

They compose well: a grid for the page, flexbox inside each region. Building as you read? Keep the CSS unit converter and aspect ratio calculator handy. Browse all snippets or subscribe via RSS.