CSS grid-template-areas in Practice —
Layout Design by Name, Not by Number

What if you could draw your layout map directly in CSS? That's exactly what grid-template-areas does. Name your regions, assign them, and watch a complex header-sidebar-main-footer structure snap into place — no coordinate math required.

The "Which Cell Is This Again?" Problem

If you've spent any time with CSS Grid, you've probably written something like grid-column: 1 / 3 or grid-row: 2 / 4 — and then come back a week later completely unable to picture what that means without staring at DevTools.

Numbers work. But they don't communicate. The moment your layout has more than three or four regions, coordinate-based placement becomes a chore to read and a nightmare to maintain.

Enter grid-template-areas. Instead of placing elements by column and row indices, you draw an ASCII map of your layout in the CSS itself, then give each element a name to match. The code looks like the layout looks. That's the whole idea — and it's surprisingly powerful.

📌 Key Point
grid-template-areas is essentially a layout blueprint written in plain text. Anyone reading your CSS can picture the page structure without opening DevTools.

The Basics — Get It Running in 10 Lines

The setup is two-step: define the map on the parent with grid-template-areas, then assign each child element its name with grid-area. That's it.

HTML
<div class="layout">
  <header class="l-header">Header</header>
  <nav    class="l-sidebar">Sidebar</nav>
  <main   class="l-main">Main</main>
  <footer class="l-footer">Footer</footer>
</div>
CSS
.layout {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
  gap: 12px;
}

.l-header  { grid-area: header; }
.l-sidebar { grid-area: sidebar; }
.l-main    { grid-area: main; }
.l-footer  { grid-area: footer; }

Look at the grid-template-areas value. Those three quoted strings are your layout map — row by row. "header header" means header spans both columns in that row. The visual structure of the code mirrors the visual structure of the page.

▶ Live Demo — Basic Layout
header
sidebar
main

Real-World Patterns — Three You'll Actually Use

① Responsive layouts — just redraw the map

This is where grid-template-areas really shines. For responsive layouts, you don't touch the children at all. You rewrite the map on the parent, and every named element falls into its new position automatically.

Responsive map swap
.layout {
  display: grid;
  gap: 16px;

  /* Mobile: stack everything vertically */
  grid-template-columns: 1fr;
  grid-template-areas:
    "header"
    "main"
    "sidebar"
    "footer";
}

@media (min-width: 768px) {
  .layout {
    grid-template-columns: 1fr 280px;
    grid-template-areas:
      "header  header"
      "main    sidebar"
      "footer  footer";
  }
}

On mobile, sidebar drops below main. On desktop, it moves to the right. No changes to the child elements. The entire layout shift is managed in one place — the parent's map.

② Empty cells with . (dot notation)

Need a gap in your layout — a cell with nothing in it? Use a dot. You can chain multiple dots for multi-cell empty areas.

Dot notation for empty cells
grid-template-areas:
  "header header header"
  ".      main   sidebar"  /* left column intentionally empty */
  "footer footer footer";

③ NG vs. OK — why number-based placement loses

❌ Coordinate-based (hard to read)
.header  { grid-column: 1 / 3; grid-row: 1; }
.sidebar { grid-column: 1; grid-row: 2; }
.main    { grid-column: 2; grid-row: 2; }
.footer  { grid-column: 1 / 3; grid-row: 3; }
✅ Named areas (structure is visible)
/* Entire layout managed in one place */
.layout {
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
}
.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }
💡 From Experience
In practice, a natural split tends to emerge: grid-template-areas for page-level skeletons, number-based placement for fine-grained item positioning within a grid. The two approaches complement each other nicely.

Going Further — Using It Inside Card Components

Page-level layouts aren't the only use case. Card components — thumbnail, category, title, excerpt, tags — are a perfect fit for named areas too. If you ever need to reorder the elements visually without changing the HTML, you just redraw the map.

Card component with named areas
.article-card {
  display: grid;
  grid-template-columns: 120px 1fr;
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
    "thumb  category"
    "thumb  title"
    "thumb  excerpt"
    "thumb  tags";
  gap: 4px 16px;
}

.card-thumb    { grid-area: thumb; }
.card-category { grid-area: category; }
.card-title    { grid-area: title; }
.card-excerpt  { grid-area: excerpt; }
.card-tags     { grid-area: tags; }

Notice that thumb appears in all four rows of the map — which means it automatically spans all four rows without needing a single grid-row declaration. The spanning comes from the map, not from numbers you have to calculate and update every time the layout changes.

⚠️ One Constraint to Know
Named areas must always form a rectangle. L-shaped or T-shaped regions aren't valid. If you need non-rectangular placement, combine named areas for the overall structure with number-based placement for the exceptions.

Takeaways

  • grid-template-areas lets you write your layout as a text map — the CSS structure mirrors the visual structure of the page.
  • Children only need grid-area: name; — no coordinates, no span calculations.
  • Responsive layouts become a map swap on the parent. Child elements need zero changes.
  • Use . (dot) to define intentionally empty cells in the grid.
  • Works great for both page-level skeletons and component-level layouts like cards.
  • All named regions must be rectangular — combine with coordinate placement for non-rectangular exceptions.