C.W.K.
Stream
Lesson 02 of 04 · published

grid-template-areas: Layout As ASCII Art

~11 min · grid-template-areas, named-areas, grid-area

Level 0Markup Novice
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The CSS reads like a sketch of the layout. That's not a metaphor — that's literally what the syntax is."

The Idea

Most layouts can be described as named regions: header, sidebar, main, footer. grid-template-areas lets you draw that map directly in the CSS using strings, where each string is a row and each word is a cell name. Items then reference areas by name with grid-area.

The result is the most readable layout syntax CSS has — the CSS literally looks like a sketch.

The Syntax

On the grid container:

.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
}

Each string is one row. Within a string, names are separated by whitespace, with each name corresponding to one column. The string above defines three rows:

  • Row 1: header spans both columns.
  • Row 2: sidebar in column 1, main in column 2.
  • Row 3: footer spans both columns.

To span a cell, repeat the name. To leave a cell empty, use a single period . (or a sequence of dots).

Placing Items By Name

Now items use grid-area: <name> to land in their area:

header  { grid-area: header; }
aside   { grid-area: sidebar; }
main    { grid-area: main; }
footer  { grid-area: footer; }

Note: grid-area: header (single name) is interpreted as the area name. grid-area: 1 / 1 / 2 / 3 (four numbers) is the line-number shorthand. The parser disambiguates.

Empty Cells With .

If you want a cell to be empty (a gap or padding region), use a period:

grid-template-areas:
  "header header header"
  ".      main   ."
  "footer footer footer";

This creates a layout where the main area is in the center column of the middle row, with empty cells on either side.

Responsive Reflow With Template Swap

The killer feature: in a media query, you redefine the template — completely rearranging the layout in one declaration, without touching the items.

Reading The Visual Sketch

When you align the strings carefully, the template literally looks like the layout you're describing:

Use grid-template-areas for page-level layouts. Anywhere you'd reach for an ASCII-art comment to explain what's happening, the template IS that comment — and it builds the layout. Code as documentation.

Limitations

  • Areas must be rectangles. You can't define an L-shaped area. If you try ("a a b" / "a b b"), CSS rejects the template as invalid.
  • Names are simple strings. No camelCase enforcement; the parser is permissive. Use kebab-case or single words.
  • One template per container. Nested grids each have their own template; they don't inherit.

When To Use Line Numbers Instead

  • Highly dynamic placement — items computed in JS that need to land in specific cells based on data. Line numbers are easier to compute.
  • Photo galleries with auto-placement — let auto-placement handle it; use span to make some items larger.
  • Layouts that change item count per breakpoint — line numbers compose better with media queries.

For everything page-level — app shells, dashboard frames, marketing pages with a fixed region structure — template-areas wins on readability every time.

Pippa's Note

The cwk-quests detail page uses template-areas: a nav on the left, main in the middle, meta on the right, and a header at the top. The CSS template literally looks like the page. When Dad and I added the quest-evolution-log sidebar, we redefined the template to add an evolution row instead of restructuring all the item placements. The visual readability of template-areas saved an hour of refactoring.

Code

Three regions, one template·css
/* Classic page shell: header, sidebar, main, footer */
.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
  gap: 1rem;
}

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

/* The template literally maps to the visual layout */
Responsive: rearrange via media query template swap·css
/* Responsive: rearrange by redefining the template in a media query */
@media (max-width: 768px) {
  .layout {
    grid-template-columns: 1fr;        /* single column on narrow */
    grid-template-areas:
      "header"
      "main"        /* main BEFORE sidebar on mobile */
      "sidebar"
      "footer";
  }
}

/* The .site-* declarations don't change. The container's template
   re-arranges them. This is the killer feature: layout transforms
   without touching item placements. */
Holy Grail + centered modal with empty cells·css
/* Holy Grail layout — header, three columns, footer */
.holy-grail {
  display: grid;
  grid-template-columns: 200px 1fr 240px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header   header  header"
    "nav      main    aside"
    "footer   footer  footer";
  min-height: 100vh;
  gap: 1rem;
}

header { grid-area: header; }
nav    { grid-area: nav; }
main   { grid-area: main; }
aside  { grid-area: aside; }
footer { grid-area: footer; }

/* On narrow screens, stack and reorder */
@media (max-width: 768px) {
  .holy-grail {
    grid-template-columns: 1fr;
    grid-template-areas:
      "header"
      "main"        /* main comes first visually */
      "nav"
      "aside"
      "footer";
  }
}

/* Layout with an empty cell */
.centered-modal {
  display: grid;
  grid-template-columns: 1fr 600px 1fr;
  grid-template-rows: 1fr auto 1fr;
  grid-template-areas:
    ". .       ."
    ". content ."
    ". .       .";
  min-height: 100vh;
}
.modal-content { grid-area: content; }

External links

Exercise

Build the Holy Grail layout (header, left nav, main, right aside, footer) using grid-template-areas. Then add a media query at 768px that switches to a single-column stack with main first. Verify in DevTools that the source HTML order doesn't have to change — the template handles the visual reorder. Then experiment: try adding an L-shaped area and observe the layout fall back to auto-placement.
Hint
L-shaped areas fail silently — CSS doesn't error, it just ignores the template. Inspect with DevTools' grid overlay to see what actually renders. The visual diagram tells you instantly whether the template was accepted.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.