DEV Community

Michael Lip
Michael Lip

Posted on • Originally published at zovo.one

CSS Grid in Practice: The Layouts That Actually Come Up at Work

I've been using CSS Grid in production since 2018. After eight years of building layouts with it, I've noticed something: the same five or six patterns account for about 95% of my Grid usage. The specification is enormous -- grid has more properties than any other CSS module -- but you don't need all of it. You need the patterns that solve real layout problems.

Here are the ones I use constantly, with the exact CSS.

The responsive card grid

This is the single most common Grid use case. A grid of cards that adapts to the available width without media queries.

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 24px;
}
Enter fullscreen mode Exit fullscreen mode

auto-fill creates as many columns as will fit. minmax(280px, 1fr) says each column must be at least 280px but can grow to fill available space equally. On a 1200px container, you get four columns. On a 600px container, you get two. On a phone, you get one. No media queries.

The alternative auto-fit collapses empty tracks, which matters when you have fewer items than columns. If you have two items and the container could fit four columns, auto-fill creates four columns (two empty), while auto-fit stretches the two items to fill the space. For card grids, auto-fill is usually what you want.

The holy grail layout

Header, footer, sidebar, and main content area. This was genuinely difficult before Grid.

.layout {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }
Enter fullscreen mode Exit fullscreen mode

The grid-template-areas property is one of Grid's best features and one of the most underused. It gives you a visual ASCII-art representation of your layout right in the CSS. It's self-documenting and makes the structure immediately obvious to anyone reading the code.

For responsive behavior, redefine the areas at a breakpoint:

@media (max-width: 768px) {
  .layout {
    grid-template-columns: 1fr;
    grid-template-areas:
      "header"
      "main"
      "sidebar"
      "footer";
  }
}
Enter fullscreen mode Exit fullscreen mode

The sidebar moves below the main content on mobile. The order in the DOM doesn't need to change.

The centered content with constrained width

For article or documentation layouts where the content needs a maximum width but edge-to-edge elements (like images) should break out:

.article {
  display: grid;
  grid-template-columns:
    1fr
    min(65ch, 100% - 48px)
    1fr;
}

.article > * {
  grid-column: 2;
}

.article > .full-width {
  grid-column: 1 / -1;
}
Enter fullscreen mode Exit fullscreen mode

Every child lands in the center column by default, constrained to 65 characters wide (the optimal line length for reading). Elements with the .full-width class span all three columns, breaking out of the content container.

This is cleaner than the max-width + margin: auto approach because breakout elements don't need negative margins or calc() hacks.

The dashboard panel layout

Dashboards need panels of different sizes in a fluid grid. Named grid areas make this manageable:

.dashboard {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: auto;
  gap: 16px;
}

/* A panel that spans two columns */
.panel-wide {
  grid-column: span 2;
}

/* A panel that spans two rows */
.panel-tall {
  grid-row: span 2;
}

/* A panel that spans the full width */
.panel-full {
  grid-column: 1 / -1;
}
Enter fullscreen mode Exit fullscreen mode

The span keyword is the practical way to handle panels. You don't need to specify exact start and end lines; you just say how many tracks the element should occupy.

Overlapping elements

Grid allows multiple items to occupy the same cell, which enables overlay effects that previously required absolute positioning:

.hero {
  display: grid;
  grid-template: 1fr / 1fr; /* single cell */
}

.hero > * {
  grid-area: 1 / 1; /* everything in the same cell */
}

.hero-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-text {
  align-self: end;
  padding: 32px;
  color: white;
  z-index: 1;
}
Enter fullscreen mode Exit fullscreen mode

Both children occupy the same grid cell. The text overlays the image. No position: absolute, no container with position: relative. The alignment properties (align-self, justify-self) position the overlapping elements within the shared cell.

Subgrid: the missing piece

Subgrid, now supported in all major browsers, solves a problem that has frustrated Grid users since the beginning: aligning child content across sibling grid items.

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 24px;
}

.card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3; /* header, body, footer */
}
Enter fullscreen mode Exit fullscreen mode

Without subgrid, each card is an independent grid. The header in one card might be taller than in another, causing the body sections to misalign. With subgrid, the card's rows participate in the parent grid's row tracks, so headers, bodies, and footers align across all cards.

Grid vs Flexbox decision rule

I use a simple rule: Grid for two-dimensional layouts (rows AND columns), Flexbox for one-dimensional layouts (a row OR a column). In practice:

  • Navigation bar items in a row: Flexbox
  • Card grid: Grid
  • Centering a single element: either works, but display: grid; place-items: center is the shortest CSS
  • Form layout with labels and inputs aligned in columns: Grid
  • Sidebar + main content: Grid
  • Buttons in a row with spacing: Flexbox

There are edge cases where either tool works equally well. When that happens, I pick whichever produces the simplest CSS.

For visually building Grid layouts without memorizing the shorthand syntax, I put together a CSS Grid generator at zovo.one/free-tools/css-grid-generator that lets you define rows, columns, and areas interactively and exports the CSS.

CSS Grid has a large API surface, but the patterns are finite. Learn the responsive card grid, the named-areas layout, the centered-content pattern, and the overlap technique, and you've covered the vast majority of production layout work.


I'm Michael Lip. I build free developer tools at zovo.one. 350+ tools, all private, all free.

Top comments (0)