CSS3 & Responsive Design

Named Grid Areas & Template Areas

25 min Lesson 30 of 60

Introduction to Grid Template Areas

CSS Grid Layout gives you two powerful approaches to placing items on a grid. In previous lessons, you learned about line-based placement using grid-column and grid-row with line numbers. While line-based placement is precise and flexible, it can become difficult to visualize and maintain in complex layouts. That is where grid-template-areas comes in -- it lets you define your layout visually, using named areas that map directly to the structure of your page. Instead of counting line numbers, you literally draw your layout in CSS using a text-based representation.

The grid-template-areas property is one of the most intuitive and powerful features in all of CSS. It allows you to assign meaningful names to regions of your grid, then place items into those regions by name. This makes your layout code self-documenting, easier to read, easier to change, and much simpler to make responsive. In this lesson, we will explore every aspect of named grid areas, from basic syntax to complex real-world layouts.

Defining Template Areas with grid-template-areas

The grid-template-areas property is applied to the grid container. Each string you provide represents one row of the grid, and within each string, you use space-separated names to assign grid cells to named areas. The names you choose are entirely up to you -- they should describe the content that will go in that region.

Example: Basic Template Areas Syntax

.container {
    display: grid;
    grid-template-areas:
        "header  header  header"
        "sidebar content content"
        "footer  footer  footer";
    grid-template-columns: 200px 1fr 1fr;
    grid-template-rows: 80px 1fr 60px;
}

In the example above, we define a three-row, three-column grid. The first row is entirely occupied by an area named "header" spanning all three columns. The second row has "sidebar" in the first column and "content" spanning the second and third columns. The third row is entirely "footer" spanning all three columns. This creates a classic page layout that you can read and understand at a glance.

Note: Each string in grid-template-areas must have the same number of cell tokens. If the first row has three names, every subsequent row must also have exactly three names (or dot placeholders). Mismatched counts will invalidate the entire declaration.

Assigning Items to Named Areas with grid-area

Once you have defined your template areas on the container, you assign grid items to those areas using the grid-area property on each child element. The value of grid-area must exactly match one of the names used in grid-template-areas.

Example: Placing Items into Named Areas

<div class="container">
    <header class="site-header">My Website</header>
    <aside class="site-sidebar">Navigation</aside>
    <main class="site-content">Main Content</main>
    <footer class="site-footer">Footer</footer>
</div>

CSS: Assigning grid-area Names

.container {
    display: grid;
    grid-template-areas:
        "header  header  header"
        "sidebar content content"
        "footer  footer  footer";
    grid-template-columns: 250px 1fr 1fr;
    grid-template-rows: 80px 1fr 60px;
    min-height: 100vh;
}

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

Notice how clean and readable this is. The grid-template-areas property acts like an ASCII art representation of your layout. And each item simply declares which named area it belongs to. The order of the items in the HTML does not matter -- Grid will place them according to the area names, not the source order.

Pro Tip: Use consistent spacing and alignment in your grid-template-areas strings to make the visual layout pattern obvious. Align the column separations vertically so each column is easy to identify. This practice dramatically improves readability for you and your team.

Empty Cells with the Dot (.) Notation

Not every cell in your grid needs to be assigned to a named area. When you want a cell to remain empty -- with no content placed in it -- you use a dot (.) as a placeholder. You can use a single dot or multiple consecutive dots (like ...) for readability. As long as dots are not separated by spaces from other dots, they count as a single empty cell token.

Example: Using Dots for Empty Cells

.container {
    display: grid;
    grid-template-areas:
        "header header header"
        "...    content sidebar"
        "footer footer  footer";
    grid-template-columns: 200px 1fr 250px;
    grid-template-rows: 80px 1fr 60px;
}

In this example, the cell in the second row, first column is empty. No grid item will be placed there, and it will simply be blank space. This is useful for creating asymmetric layouts or adding visual breathing room.

Example: Multiple Empty Cells

.gallery-layout {
    display: grid;
    grid-template-areas:
        "img1 img1 .    img2"
        "img1 img1 img3 img3"
        ".    img4 img3 img3";
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(3, 200px);
    gap: 10px;
}

.photo-1 { grid-area: img1; }
.photo-2 { grid-area: img2; }
.photo-3 { grid-area: img3; }
.photo-4 { grid-area: img4; }

This creates an interesting gallery layout where images span different numbers of cells, with two empty cells creating visual asymmetry. This kind of layout would be much harder to understand using line numbers alone.

Rules for Valid Named Areas

There are important rules that grid-template-areas values must follow for them to be valid:

  • Rectangular shapes only: Each named area must form a rectangle. You cannot create L-shaped, T-shaped, or other non-rectangular areas. The browser will invalidate the entire declaration if an area does not form a perfect rectangle.
  • Consistent row lengths: Every row string must contain the same number of cell tokens.
  • No diagonal spanning: A named area cannot span diagonally -- it must span contiguous rows and contiguous columns.
  • Area names must be valid CSS identifiers: They cannot start with a number, and they should not conflict with CSS keywords. Use descriptive names like header, sidebar, content, nav, or banner.

Example: Invalid L-Shaped Area (Will Not Work)

/* INVALID -- "sidebar" forms an L-shape, not a rectangle */
.container {
    display: grid;
    grid-template-areas:
        "sidebar header  header"
        "sidebar content content"
        "sidebar footer  sidebar";  /* sidebar appears disconnected! */
}
Warning: If any named area in grid-template-areas does not form a contiguous rectangle, the entire grid-template-areas declaration is invalid and will be ignored by the browser. Use your browser's developer tools to check for warnings if your layout is not working as expected.

Combining Template Areas with Explicit Track Sizing

The grid-template-areas property defines the shape and names of your grid areas, but it does not define the sizes of rows and columns by itself. You typically combine it with grid-template-columns and grid-template-rows to specify the dimensions of each track. The number of columns defined must match the number of names in each row string, and the number of rows defined must match the number of row strings.

Example: Template Areas with Detailed Sizing

.page {
    display: grid;
    grid-template-areas:
        "nav     nav     nav"
        "sidebar main    aside"
        "footer  footer  footer";
    grid-template-columns: 220px 1fr 180px;
    grid-template-rows: auto 1fr auto;
    min-height: 100vh;
    gap: 16px;
    padding: 16px;
}

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

Here, the navigation spans all three columns and sizes to its content height (auto). The middle row has a fixed-width sidebar on the left (220px), a flexible main content area (1fr), and a fixed-width aside on the right (180px). The middle row takes up all remaining vertical space because of 1fr. The footer auto-sizes to its content.

Named Grid Lines from Template Areas

When you define named areas, CSS Grid automatically creates named grid lines based on those area names. For each named area, the browser generates lines named {area-name}-start and {area-name}-end for both rows and columns. This means you can reference these implicit named lines in other grid properties like grid-column or grid-row.

Example: Using Implicit Named Lines

.container {
    display: grid;
    grid-template-areas:
        "header header header"
        "sidebar content content"
        "footer footer footer";
    grid-template-columns: 200px 1fr 1fr;
    grid-template-rows: 80px 1fr 60px;
}

/* An extra element placed using the implicit named lines */
.overlay {
    grid-column: content-start / content-end;
    grid-row: header-start / footer-end;
    /* This element spans from the start of the content column
       to the end of the content column, and from the top of
       the header row to the bottom of the footer row */
    background: rgba(0, 0, 0, 0.1);
    pointer-events: none;
}

This feature is incredibly useful for adding decorative elements, overlays, or additional content that needs to align with your named areas but does not fit neatly into one specific area. You can mix and match named area placement with line-based placement in the same grid.

Responsive Layouts by Changing Template Areas

One of the greatest strengths of grid-template-areas is how easy it makes responsive design. Instead of rearranging complex line numbers in media queries, you simply redefine the grid-template-areas strings. The same HTML elements automatically reflow to their new positions because their grid-area names stay the same.

Example: Responsive Layout with Template Areas

/* Mobile: Single column, stacked layout */
.page {
    display: grid;
    grid-template-areas:
        "header"
        "nav"
        "content"
        "sidebar"
        "footer";
    grid-template-columns: 1fr;
    grid-template-rows: auto auto 1fr auto auto;
    min-height: 100vh;
    gap: 16px;
}

/* Tablet: Two columns */
@media (min-width: 768px) {
    .page {
        grid-template-areas:
            "header  header"
            "nav     nav"
            "sidebar content"
            "footer  footer";
        grid-template-columns: 250px 1fr;
    }
}

/* Desktop: Three columns */
@media (min-width: 1200px) {
    .page {
        grid-template-areas:
            "header  header  header"
            "nav     nav     nav"
            "sidebar content aside"
            "footer  footer  footer";
        grid-template-columns: 250px 1fr 200px;
    }
}

.header  { grid-area: header; }
.nav     { grid-area: nav; }
.content { grid-area: content; }
.sidebar { grid-area: sidebar; }
.aside   { grid-area: aside; }
.footer  { grid-area: footer; }

Look at how clean this is. The HTML stays exactly the same. The grid-area assignments on the children never change. Only the container's grid-template-areas and grid-template-columns change between breakpoints. You can literally see the layout change by reading the ASCII-art strings. This is why template areas are the preferred approach for page-level layouts in modern CSS.

Pro Tip: When an area is not needed at a certain breakpoint (like aside on mobile), simply do not include it in the template areas. The element with grid-area: aside will still exist in the DOM but will not be placed on the grid. You can hide it with display: none at that breakpoint to prevent it from appearing outside the grid.

Complete Dashboard Layout

Let us build a real-world dashboard layout using named grid areas. Dashboards typically have a top navigation bar, a side panel for navigation, a main content area with widgets, and possibly a status bar at the bottom.

HTML: Dashboard Structure

<div class="dashboard">
    <header class="dash-header">
        <h1>Admin Dashboard</h1>
        <div class="user-menu">Profile</div>
    </header>
    <nav class="dash-nav">
        <ul>
            <li>Dashboard</li>
            <li>Users</li>
            <li>Reports</li>
            <li>Settings</li>
        </ul>
    </nav>
    <main class="dash-main">
        <div class="widget">Revenue</div>
        <div class="widget">Users</div>
        <div class="widget">Activity</div>
    </main>
    <aside class="dash-notifications">
        <h3>Notifications</h3>
    </aside>
    <footer class="dash-status">
        <span>Server: Online</span>
    </footer>
</div>

CSS: Dashboard Grid with Template Areas

.dashboard {
    display: grid;
    grid-template-areas:
        "header  header  header  header"
        "nav     main    main    notif"
        "nav     main    main    notif"
        "status  status  status  status";
    grid-template-columns: 220px 1fr 1fr 280px;
    grid-template-rows: 64px 1fr 1fr 40px;
    min-height: 100vh;
    gap: 0;
}

.dash-header        { grid-area: header;
                      background: #1a1a2e;
                      color: white;
                      display: flex;
                      align-items: center;
                      justify-content: space-between;
                      padding: 0 24px; }

.dash-nav           { grid-area: nav;
                      background: #16213e;
                      color: white;
                      padding: 20px; }

.dash-main          { grid-area: main;
                      background: #f0f2f5;
                      padding: 24px;
                      display: grid;
                      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
                      gap: 20px;
                      align-content: start; }

.dash-notifications { grid-area: notif;
                      background: #ffffff;
                      padding: 20px;
                      border-left: 1px solid #e0e0e0; }

.dash-status        { grid-area: status;
                      background: #1a1a2e;
                      color: #aaa;
                      display: flex;
                      align-items: center;
                      padding: 0 24px;
                      font-size: 0.85rem; }

/* Responsive: collapse on smaller screens */
@media (max-width: 1024px) {
    .dashboard {
        grid-template-areas:
            "header header"
            "nav    main"
            "nav    main"
            "status status";
        grid-template-columns: 200px 1fr;
    }
    .dash-notifications { display: none; }
}

@media (max-width: 640px) {
    .dashboard {
        grid-template-areas:
            "header"
            "main"
            "status";
        grid-template-columns: 1fr;
        grid-template-rows: 64px 1fr 40px;
    }
    .dash-nav { display: none; }
}

This dashboard demonstrates the full power of template areas. The desktop layout has four columns with a header spanning the top, navigation on the left, main content in the center, and notifications on the right. On tablet, we collapse to two columns and hide notifications. On mobile, we go to a single column and hide the side navigation entirely. The HTML never changes -- only the CSS grid template areas are redefined.

Blog Layout with Template Areas

Blog layouts often require a featured article section, a grid of posts, a sidebar with categories and popular posts, and a footer. Template areas make this structure clear and maintainable.

CSS: Blog Page Layout

.blog-page {
    display: grid;
    grid-template-areas:
        "featured featured featured"
        "posts    posts    sidebar"
        "posts    posts    sidebar"
        "more     more     more";
    grid-template-columns: 1fr 1fr 300px;
    grid-template-rows: 400px auto auto auto;
    gap: 24px;
    max-width: 1200px;
    margin: 0 auto;
    padding: 24px;
}

.featured-article { grid-area: featured;
                    background: linear-gradient(135deg, #667eea, #764ba2);
                    border-radius: 12px;
                    color: white;
                    display: flex;
                    align-items: flex-end;
                    padding: 40px; }

.post-grid        { grid-area: posts;
                    display: grid;
                    grid-template-columns: repeat(2, 1fr);
                    gap: 20px; }

.blog-sidebar     { grid-area: sidebar; }
.load-more        { grid-area: more;
                    text-align: center;
                    padding: 40px; }

@media (max-width: 768px) {
    .blog-page {
        grid-template-areas:
            "featured"
            "posts"
            "sidebar"
            "more";
        grid-template-columns: 1fr;
        grid-template-rows: 250px auto auto auto;
    }
    .post-grid {
        grid-template-columns: 1fr;
    }
}

Landing Page Layout

Landing pages have distinct sections that benefit greatly from template areas. A hero section, feature highlights, testimonials, a call to action, and a footer can all be defined clearly.

CSS: Landing Page Layout

.landing {
    display: grid;
    grid-template-areas:
        "hero       hero"
        "feature-l  feature-r"
        "stats      stats"
        "cta        cta"
        "footer     footer";
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 90vh auto auto auto auto;
    gap: 0;
}

.hero       { grid-area: hero;
              display: flex;
              flex-direction: column;
              align-items: center;
              justify-content: center;
              background: #0f0c29;
              color: white;
              text-align: center;
              padding: 60px 20px; }

.feature-l  { grid-area: feature-l;
              padding: 80px 40px;
              background: #f8f9fa; }

.feature-r  { grid-area: feature-r;
              padding: 80px 40px;
              background: #ffffff; }

.stats      { grid-area: stats;
              display: grid;
              grid-template-columns: repeat(4, 1fr);
              text-align: center;
              padding: 60px 20px;
              background: #1a1a2e;
              color: white; }

.cta        { grid-area: cta;
              text-align: center;
              padding: 80px 20px;
              background: linear-gradient(135deg, #667eea, #764ba2);
              color: white; }

.land-footer { grid-area: footer;
               padding: 40px;
               background: #0f0c29;
               color: #999; }

@media (max-width: 768px) {
    .landing {
        grid-template-areas:
            "hero"
            "feature-l"
            "feature-r"
            "stats"
            "cta"
            "footer";
        grid-template-columns: 1fr;
        grid-template-rows: 80vh auto auto auto auto auto;
    }
    .stats {
        grid-template-columns: repeat(2, 1fr);
    }
}

Accessibility: Visual Order vs DOM Order

One of the most important considerations when using grid-template-areas is the relationship between visual order (what the user sees) and DOM order (the order elements appear in the HTML). Grid allows you to visually rearrange elements freely, but screen readers and keyboard navigation follow the DOM order, not the visual order.

This means that if you use template areas to place a navigation element visually below the main content, a screen reader user will still encounter the navigation first if it comes first in the HTML. This can be beneficial -- the navigation is logically important and should be accessible early. But it can also be problematic if the visual and logical orders diverge significantly, causing confusion for keyboard users who tab through elements in DOM order while seeing them in a different visual order.

Warning: The CSS Grid specification explicitly warns against using grid placement as a substitute for correct source ordering. Your HTML should follow a logical reading order. Use template areas to enhance the visual presentation, not to fix a poorly structured document. Always test with keyboard navigation (Tab key) to ensure the tab order makes sense with your visual layout.

Example: Maintaining Logical DOM Order

<!-- Good: DOM order is logical -->
<div class="page">
    <header>...</header>    <!-- 1st in DOM, displayed at top -->
    <nav>...</nav>          <!-- 2nd in DOM, displayed as sidebar -->
    <main>...</main>        <!-- 3rd in DOM, displayed as main content -->
    <aside>...</aside>      <!-- 4th in DOM, displayed as right sidebar -->
    <footer>...</footer>    <!-- 5th in DOM, displayed at bottom -->
</div>

<!-- The visual order via grid-template-areas: -->
<!-- header header header -->
<!-- nav    main   aside  -->
<!-- footer footer footer -->
<!-- This is fine because DOM order is logically correct -->

Advanced Pattern: Nested Grids with Template Areas

Template areas work beautifully in nested grid contexts. A grid item that is placed into a named area can itself become a grid container with its own template areas. This pattern is essential for complex interfaces like dashboards where each section has its own internal layout.

Example: Nested Template Areas

/* Outer grid: page layout */
.page {
    display: grid;
    grid-template-areas:
        "header header"
        "sidebar main"
        "footer footer";
    grid-template-columns: 240px 1fr;
    grid-template-rows: 60px 1fr 50px;
    min-height: 100vh;
}

/* Inner grid: main content area has its own grid */
.main-content {
    grid-area: main;
    display: grid;
    grid-template-areas:
        "chart  chart  stats"
        "table  table  table";
    grid-template-columns: 1fr 1fr 300px;
    grid-template-rows: 350px 1fr;
    gap: 20px;
    padding: 20px;
}

.chart-section { grid-area: chart; }
.stats-panel   { grid-area: stats; }
.data-table    { grid-area: table; }

The grid-template Shorthand

CSS Grid provides a shorthand property called grid-template that combines grid-template-rows, grid-template-columns, and grid-template-areas into a single declaration. While this is more concise, it can be harder to read for complex layouts.

Example: Using the grid-template Shorthand

/* Longhand version */
.container {
    display: grid;
    grid-template-areas:
        "header header header"
        "nav    main   aside"
        "footer footer footer";
    grid-template-rows: 80px 1fr 60px;
    grid-template-columns: 200px 1fr 200px;
}

/* Shorthand version -- row sizes come after each area string */
.container {
    display: grid;
    grid-template:
        "header header header" 80px
        "nav    main   aside"  1fr
        "footer footer footer" 60px
        / 200px 1fr 200px;
}

In the shorthand, each row string is followed by its row track size, and after the last row, a forward slash (/) introduces the column track sizes. This is compact but can become unwieldy with many columns.

Practical Example: Magazine-Style Layout

Let us build a complex magazine-style layout that demonstrates the full potential of template areas. This layout has a large hero article, smaller featured stories, a sidebar, and a content grid.

CSS: Magazine Layout

.magazine {
    display: grid;
    grid-template-areas:
        "hero     hero     hero     promo"
        "story1   story2   story3   promo"
        "trending trending sidebar  sidebar"
        "trending trending sidebar  sidebar";
    grid-template-columns: 1fr 1fr 1fr 300px;
    grid-template-rows: 500px 250px auto auto;
    gap: 16px;
    max-width: 1400px;
    margin: 0 auto;
    padding: 20px;
}

.hero-article  { grid-area: hero;
                 background-size: cover;
                 background-position: center;
                 border-radius: 12px;
                 display: flex;
                 align-items: flex-end;
                 padding: 30px; }

.promo-sidebar { grid-area: promo;
                 background: #f8f9fa;
                 border-radius: 12px;
                 padding: 20px; }

.story-1       { grid-area: story1; }
.story-2       { grid-area: story2; }
.story-3       { grid-area: story3; }
.trending      { grid-area: trending; }
.sidebar       { grid-area: sidebar; }

@media (max-width: 1024px) {
    .magazine {
        grid-template-areas:
            "hero    hero"
            "story1  story2"
            "story3  promo"
            "trending trending"
            "sidebar sidebar";
        grid-template-columns: 1fr 1fr;
        grid-template-rows: 350px auto auto auto auto;
    }
}

@media (max-width: 640px) {
    .magazine {
        grid-template-areas:
            "hero"
            "story1"
            "story2"
            "story3"
            "promo"
            "trending"
            "sidebar";
        grid-template-columns: 1fr;
    }
}

Performance Considerations

Named grid areas have no significant performance overhead compared to line-based placement. The browser resolves area names to line numbers during the layout calculation phase, which happens extremely fast. However, keep these best practices in mind:

  • Avoid deeply nested grids with template areas unless necessary. Each nested grid adds a layout calculation pass.
  • Keep your grid structures as flat as possible for optimal performance.
  • When animations or transitions cause layout changes, the grid recalculates. Animating properties that trigger layout (like grid track sizes) can be expensive. Prefer animating transforms and opacity instead.
  • Template areas are resolved once during layout, so changing them dynamically via JavaScript is no more expensive than changing any other grid property.

Common Pitfalls and Debugging

Here are the most common mistakes developers make with template areas and how to fix them:

Pitfall 1: Non-rectangular areas

/* BROKEN -- "content" is not rectangular */
grid-template-areas:
    "header  content"
    "sidebar sidebar"
    "content footer";   /* "content" appears disconnected! */

/* FIXED -- each area forms a rectangle */
grid-template-areas:
    "header  header"
    "sidebar content"
    "footer  footer";

Pitfall 2: Mismatched grid-area names

/* Container defines "nav" */
grid-template-areas:
    "header header"
    "nav    content";

/* BROKEN -- typo in grid-area name */
.navigation { grid-area: navigation; }  /* Should be "nav" */

/* FIXED */
.navigation { grid-area: nav; }

Pitfall 3: Unequal row lengths

/* BROKEN -- first row has 3 tokens, second has 2 */
grid-template-areas:
    "header header header"
    "sidebar content";

/* FIXED -- all rows have 3 tokens */
grid-template-areas:
    "header  header  header"
    "sidebar content content";

Practice Exercise

Exercise 1: Create a portfolio page layout using grid-template-areas. The layout should have a header spanning the full width, a hero section spanning the full width, a two-column section with project cards on the left and an about section on the right, and a footer spanning the full width. Make it responsive: on mobile, everything should stack in a single column.

Exercise 2: Build an e-commerce product page layout. The desktop version should have the product image on the left (spanning 2 rows), product title and price on the top right, product description and reviews on the bottom right, and related products spanning the full width at the bottom. Use at least 3 breakpoints to make it fully responsive. Test your layout by resizing the browser and ensuring it adapts smoothly.

Exercise 3: Take an existing layout you have built with Flexbox or line-based Grid placement and refactor it to use grid-template-areas. Compare the readability of the two approaches. Which one is easier to understand at a glance? Which one is easier to modify for a different screen size?