ESC

Type to search the knowledge base.

Grid Template Areas

Name grid regions with grid-template-areas — ascii layouts, grid-area placement, responsive rearrangements without absolute positioning.

intermediate3 min read
  • css
  • grid
  • template-areas

grid-template-areas lets you draw a page skeleton in ASCII and place children with grid-area: name. Rearranging a layout at a breakpoint becomes rewriting the string map — not juggling line numbers.

Docs: MDN grid-template-areas, MDN line-based placement.

Basic page shell

<div class="page">
  <header class="header">…</header>
  <nav class="nav">…</nav>
  <main class="main">…</main>
  <aside class="aside">…</aside>
  <footer class="footer">…</footer>
</div>
.page {
  display: grid;
  min-height: 100dvh;
  grid-template-columns: 14rem 1fr 16rem;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header header"
    "nav    main   aside"
    "footer footer footer";
  gap: 0;
}

.header { grid-area: header; }
.nav    { grid-area: nav; }
.main   { grid-area: main; min-width: 0; }
.aside  { grid-area: aside; }
.footer { grid-area: footer; }

Each string is a row. Identical names in adjacent cells merge into one region. All rows must have the same number of cells.

Rules that reject your template

  • Names must form rectangles — no L-shaped regions.
  • Use . for an empty cell.
  • Don’t leave holes that make a name non-rectangular.
grid-template-areas:
  "logo nav  nav"
  "side main main"
  ".    main main"; /* invalid if main isn't rectangular — this main is OK (2x2 block) */

Responsive remap

@media (max-width: 60rem) {
  .page {
    grid-template-columns: 1fr;
    grid-template-areas:
      "header"
      "nav"
      "main"
      "aside"
      "footer";
  }
}

Same elements, new map — no changing HTML order for visual order (still consider source order for accessibility and tab order; don’t use areas to invent a confusing focus sequence).

Component-level areas

.media {
  display: grid;
  grid-template-columns: 8rem 1fr;
  grid-template-areas: "img body";
  gap: 1rem;
  align-items: center;
}

.media__img  { grid-area: img; }
.media__body { grid-area: body; }

@media (max-width: 30rem) {
  .media {
    grid-template-columns: 1fr;
    grid-template-areas:
      "img"
      "body";
  }
}

Shorthand pairing

.page {
  display: grid;
  grid-template:
    "header header" auto
    "nav    main"   1fr
    "footer footer" auto
    / 12rem 1fr;
}

grid-template can set areas, rows, and columns together.

Interview out-loud

“grid-template-areas names rectangular regions in a row-string map. Children use grid-area to land in a region. Every row needs the same cell count, and each name must form a rectangle. I use it for page shells and media objects, and I rewrite the map at breakpoints instead of absolute positioning.”

Footguns

  1. L-shaped names — invalid, whole template can fail.
  2. Typos between grid-area and template strings.
  3. Forgetting min-width: 0 on main content regions.
  4. Visual order diverging badly from DOM order for keyboard users.
  5. Overusing areas for tiny components where line-based placement is simpler.

Debugging invalid templates

If areas “do nothing,” check:

  1. Same number of tokens in every row string.
  2. Each name forms a solid rectangle.
  3. grid-area names match exactly (typos).
  4. The element is a child of the grid container.
  5. display: grid is actually applied (not overridden).
/* Invalid L-shape — will not create the area as intended */
grid-template-areas:
  "a a b"
  "a c c";

a is not rectangular. Split into separate areas or reshape the map.

Named lines vs areas

You can combine areas with line names for hybrid placement. Prefer one mental model per layout: areas for page shells, line-based for dense dashboards with many spanning widgets. Mixing both freely works, but onboarding cost rises.

Further reading

Related guides