Learn
🤖 AI Hub 📰 News 📖 Dictionary 🧰 Resources
Grow
💼 Jobs 🚀 Community 🏆 Challenges
Explore by Stage
🌑 Zero 📚 Learn 🔧 Practice 🏗 Build 🚀 Launch 💰 Earn
Account
Login Sign Up
🏗️HTML 🎨CSS ⚡JavaScript 🐙Git & GitHub ⚛️React Soon 📘TypeScript Soon 🐍Python 🗄️Database & SQL Soon 🌐Web Development Soon ☁️Cloud & DevOps Soon 🤖AI & ML Soon 📄Resume Prep Soon 🎤Interview Prep Soon 💼Freelancing Soon 🚀SaaS & Side Projects Soon 🤖Automation Soon
Intermediate 🕑 14 min read 👁 4 views 📅 Oct 21, 2026

CSS Grid Layouts: Template Areas and Responsive Grids

Build full page layouts with grid-template-areas, make cards wrap with auto-fit and minmax(), rearrange areas for phones and align items, by creating a news homepage.

Laying Out a News Homepage

Open any news website on a laptop. At the top there is a header with the paper’s name. The big main story takes most of the width. On the right there is a “Trending” sidebar and an advertisement. At the bottom there is a footer. Now open the same site on a phone: everything is in one neat column.

In the CSS Grid Basics lesson you learned to make columns and rows and to place items with line numbers. That works, but for a whole page, line numbers get hard to read. In this lesson you will learn to draw the layout with words, make cards wrap on their own without any media query, rearrange the page for phones, align items inside cells, and put a grid inside a grid.

By the end you will build the homepage of a small city newspaper called Nagar News.

grid-template-areas: Draw Your Layout

With grid-template-areas you give each part of the page a name and then write a small “map” of the grid. Each string in quotes is one row. Each word inside the string is one cell.

Then, on each item, grid-area: name tells it which area to fill. Writing the same name in two cells makes that item cover both.

<style>
  .page {
    display: grid;
    grid-template-columns: 3fr 1fr;
    grid-template-areas:
      "header  header"
      "main    sidebar"
      "main    ads"
      "footer  footer";
    gap: 8px;
  }
  .page > * { padding: 16px; border-radius: 6px; }
  header  { grid-area: header;  background: #fecaca; }
  main    { grid-area: main;    background: #fef08a; min-height: 160px; }
  aside   { grid-area: sidebar; background: #bbf7d0; }
  .ads    { grid-area: ads;     background: #bfdbfe; }
  footer  { grid-area: footer;  background: #e5e7eb; }
</style>

<div class="page">
  <header>Header</header>
  <main>Main story</main>
  <aside>Trending</aside>
  <div class="ads">Ads</div>
  <footer>Footer</footer>
</div>

Read the map from top to bottom: the header covers both columns, the main story covers two rows on the left, the sidebar and ads sit on the right, and the footer covers the full width. Anyone reading your CSS can now see the layout at a glance.

Two rules for the map:

  • Every row must have the same number of cells.
  • Each named area must form a rectangle. An L shape is not allowed.

To leave a cell empty, write a full stop (.) in its place, for example "header .".

Named Lines (A Quick Look)

In the last lesson you used numbered lines like grid-column: 1 / 3. You can also give lines names by writing them in square brackets inside grid-template-columns. This is useful for article pages, where text stays in a narrow middle column and photos stretch wider.

<style>
  .article {
    display: grid;
    grid-template-columns: [full-start] 1fr [text-start] 3fr [text-end] 1fr [full-end];
    gap: 8px;
  }
  .article p {
    grid-column: text-start / text-end;
    background: #f3f4f6;
    margin: 0;
    padding: 10px;
  }
  .article .photo {
    grid-column: full-start / full-end;
    background: #a5b4fc;
    padding: 30px;
    text-align: center;
  }
</style>

<div class="article">
  <p>Heavy rain is expected in the city this weekend.</p>
  <div class="photo">Wide photo of the clouds</div>
  <p>Schools may stay closed on Monday if roads flood.</p>
</div>

You will not need named lines every day, but when you see square brackets in someone else’s grid code, now you know what they are.

Responsive Cards Without Media Queries

News sites show many small story cards. On a phone you want one card per row, on a tablet two or three, and on a laptop four or five. Grid can do this with one line:

grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));

Let us read it piece by piece:

  • minmax(160px, 1fr): each column is at least 160px wide and at most one share of the free space.
  • auto-fit: instead of a fixed number like 4, the browser fits as many columns as it can.
<style>
  .news-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px;
  }
  .news-cards article {
    background: #fff7ed;
    border: 1px solid #fdba74;
    border-radius: 8px;
    padding: 12px;
  }
</style>

<div class="news-cards">
  <article>Metro line opens</article>
  <article>Board results out</article>
  <article>Monsoon update</article>
  <article>Local cricket final</article>
  <article>New bus routes</article>
</div>

Press Try it yourself and make the result panel narrower and wider. The cards jump between rows on their own. No media query was needed.

auto-fit or auto-fill?

They are the same when there are enough items to fill a row. The difference shows when there are only a few items:

<style>
  .fit, .fill {
    display: grid;
    gap: 8px;
    margin-bottom: 20px;
    border: 2px dashed #9ca3af;
  }
  .fit  { grid-template-columns: repeat(auto-fit,  minmax(100px, 1fr)); }
  .fill { grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); }
  .fit div, .fill div { background: #c4b5fd; padding: 12px; }
</style>

<p>auto-fit: the two cards stretch to fill the row</p>
<div class="fit"><div>Card 1</div><div>Card 2</div></div>

<p>auto-fill: empty columns are kept, cards stay small</p>
<div class="fill"><div>Card 1</div><div>Card 2</div></div>
KeywordWhen items do not fill the rowUse it for
auto-fitEmpty columns collapse, so items stretch widerMost card layouts
auto-fillEmpty columns stay, so items keep their sizeWhen cards must look the same size on every row

Changing the Areas in a Media Query

For the main page layout you still want full control, and here template areas really shine. Write the phone layout first, as one column, and then redraw the map inside a media query for bigger screens. The HTML does not change at all.

<style>
  .page {
    display: grid;
    gap: 8px;
    grid-template-areas:
      "header"
      "main"
      "sidebar"
      "ads"
      "footer";
  }
  @media (min-width: 600px) {
    .page {
      grid-template-columns: 2fr 1fr;
      grid-template-areas:
        "header  header"
        "main    sidebar"
        "main    ads"
        "footer  footer";
    }
  }
  .page > * { padding: 14px; border-radius: 6px; }
  header { grid-area: header;  background: #fecaca; }
  main   { grid-area: main;    background: #fef08a; }
  aside  { grid-area: sidebar; background: #bbf7d0; }
  .ads   { grid-area: ads;     background: #bfdbfe; }
  footer { grid-area: footer;  background: #e5e7eb; }
</style>

<div class="page">
  <header>Header</header>
  <main>Main story</main>
  <aside>Trending</aside>
  <div class="ads">Ads</div>
  <footer>Footer</footer>
</div>

On a narrow screen, everything stacks in the order of the map. Above 600px, the same items move into two columns. You only changed one map, not every item. This mobile first approach was covered in the CSS Media Queries lesson.

Keep the HTML order sensible. Grid can show items in any order on screen, but screen readers and the Tab key still follow the HTML order. Put the main story before the sidebar in your HTML, and use grid only to change the visual position.

Aligning Items Inside Their Cells

By default a grid item stretches to fill its whole cell. Sometimes you want a small item, like a badge or an icon, to sit in the middle or a corner instead.

PropertySet onDirection
justify-itemsContainerLeft to right (inline direction)
align-itemsContainerTop to bottom (block direction)
place-itemsContainerBoth at once: align-items then justify-items
justify-self, align-selfOne itemOverrides the container for that item only

The values are start, end, center and stretch (the default). In this example each cell is one column wide and 90px tall. The items shrink to the size of their text and sit in the middle of their cells, except the “Live” badge, which moves to the top right corner. Turn on the grid overlay in DevTools to see the cell edges:

<style>
  .icons {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: 90px;
    gap: 8px;
    place-items: center;   /* align-items + justify-items */
  }
  .icons div {
    background: #fde68a;
    padding: 8px 12px;
    border-radius: 20px;
  }
  .icons .corner {
    justify-self: end;
    align-self: start;
  }
</style>

<div class="icons">
  <div>Sports</div>
  <div>Politics</div>
  <div class="corner">Live</div>
  <div>Weather</div>
  <div>Business</div>
  <div>Tech</div>
</div>

place-items: center is one of the shortest ways in CSS to put something exactly in the middle of a box.

Nesting Grids

A grid item can also be a grid container for its own children. This is called a nested grid. Real pages do this all the time: the page is one grid, and inside the main area, the list of smaller stories is a second grid. Each grid only controls its own direct children. You will see a nested grid in the next example.

Real-Life Example: The Nagar News Homepage

Here is the full homepage. The page uses template areas that change at 700px. Inside main, the “more news” cards form a nested grid with auto-fit, so they wrap by themselves. The photo needs an internet connection.

<style>
  body { margin: 0; font-family: Georgia, serif; background: #f9fafb; }
  .page {
    display: grid;
    gap: 12px;
    padding: 12px;
    grid-template-areas: "header" "main" "sidebar" "ads" "footer";
  }
  @media (min-width: 700px) {
    .page {
      grid-template-columns: 3fr 1fr;
      grid-template-areas:
        "header header"
        "main   sidebar"
        "main   ads"
        "footer footer";
    }
  }
  header { grid-area: header; background: #991b1b; color: white; padding: 12px 16px; }
  header h1 { margin: 0; font-size: 26px; }
  main   { grid-area: main; }
  aside  { grid-area: sidebar; background: white; padding: 12px; }
  .ads   { grid-area: ads; background: #fef3c7; padding: 12px; text-align: center; }
  footer { grid-area: footer; text-align: center; color: #6b7280; font-size: 14px; }

  .top-story { background: white; padding: 16px; margin-bottom: 12px; }
  .top-story img { width: 100%; height: auto; display: block; }

  /* a nested grid inside main */
  .more-news {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 10px;
  }
  .more-news article { background: white; padding: 10px; font-size: 15px; }
  aside ol { padding-left: 20px; margin: 0; }
</style>

<div class="page">
  <header><h1>Nagar News</h1></header>
  <main>
    <article class="top-story">
      <img src="https://picsum.photos/seed/news1/800/350" alt="Crowd at the new metro station">
      <h2>New metro line opens for the public</h2>
      <p>Thousands of commuters took the first ride on Sunday morning.</p>
    </article>
    <div class="more-news">
      <article>State board results to be declared on Friday</article>
      <article>City team reaches the cricket final</article>
      <article>Monsoon arrives two days early</article>
    </div>
  </main>
  <aside>
    <h3>Trending</h3>
    <ol>
      <li>Petrol prices today</li>
      <li>Exam timetable released</li>
      <li>Weekend weather</li>
    </ol>
  </aside>
  <div class="ads">Advertisement: Diwali sale at the city mall</div>
  <footer>Nagar News, a sample site for learning CSS</footer>
</div>

Try these changes:

  1. Move the ads above the sidebar on big screens by swapping the words sidebar and ads in the desktop map.
  2. Add a fourth story card inside .more-news and watch the nested grid make room.
  3. Change the desktop columns to 2fr 1fr to give the sidebar more space.

Common Mistakes

  • An area that is not a rectangle. If one name makes an L shape, or rows have different cell counts, the browser ignores the whole grid-template-areas value. See the example below.
  • Forgetting grid-area on the items. The map does nothing until each item says which area it belongs to. Without it, items are placed one by one into the cells.
  • Putting quotes around the name in grid-area. Write grid-area: main;, not grid-area: "main";. Quotes are only used in grid-template-areas.
  • Using repeat(auto-fit, 1fr). This is not valid. auto-fit needs a minimum size, so always pair it with minmax().

In this broken map, main appears in two places that do not touch as a rectangle, so the layout is ignored:

<style>
  .page {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "header main"
      "main   footer";   /* main is not a rectangle */
    gap: 8px;
  }
  .page > * { padding: 12px; background: #e0e7ff; }
  header { grid-area: header; }
  main   { grid-area: main; }
  footer { grid-area: footer; }
</style>

<div class="page">
  <header>Header</header>
  <main>Main</main>
  <footer>Footer</footer>
</div>

Fixed: every area is a rectangle, and every row has two cells:

<style>
  .page {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "header header"
      "main   main"
      "footer footer";
    gap: 8px;
  }
  .page > * { padding: 12px; background: #e0e7ff; }
  header { grid-area: header; }
  main   { grid-area: main; }
  footer { grid-area: footer; }
</style>

<div class="page">
  <header>Header</header>
  <main>Main</main>
  <footer>Footer</footer>
</div>

Practice Task: A Sports Desk Page

The sports editor of Nagar News wants a separate page for cricket. Use the starter code and complete the TODO steps.

  • On phones, the order is header, live scores, news, footer (fans want scores first).
  • From 650px wide, the news column (2fr) sits on the left and scores (1fr) on the right.
  • The player cards inside the news section wrap by themselves using auto-fit and minmax().
<style>
  body { font-family: system-ui, sans-serif; margin: 0; }
  .page { display: grid; gap: 10px; padding: 10px; }
  .page > * { padding: 14px; border-radius: 6px; background: #e0f2fe; }
  /* TODO 1: on phones, stack the areas: header, scores, news, footer */
  /* TODO 2: from 650px wide, put news (2fr) and scores (1fr) side by side */
  /* TODO 3: make .players a grid of cards using auto-fit and minmax(120px, 1fr) */
</style>

<div class="page">
  <header>Sports Desk</header>
  <section class="news">
    <h2>Today's match report</h2>
    <div class="players">
      <div>Batter of the day</div>
      <div>Best bowler</div>
      <div>Best catch</div>
      <div>Fastest fifty</div>
    </div>
  </section>
  <aside class="scores">Live scores</aside>
  <footer>Footer</footer>
</div>
Show one possible answer (try it yourself first!)
<style>
  body { font-family: system-ui, sans-serif; margin: 0; }
  .page {
    display: grid;
    gap: 10px;
    padding: 10px;
    grid-template-areas: "header" "scores" "news" "footer";
  }
  @media (min-width: 650px) {
    .page {
      grid-template-columns: 2fr 1fr;
      grid-template-areas:
        "header header"
        "news   scores"
        "footer footer";
    }
  }
  .page > * { padding: 14px; border-radius: 6px; background: #e0f2fe; }
  header  { grid-area: header; }
  .news   { grid-area: news; }
  .scores { grid-area: scores; }
  footer  { grid-area: footer; }
  .players {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 8px;
  }
  .players div { background: white; padding: 10px; border-radius: 6px; }
</style>

<div class="page">
  <header>Sports Desk</header>
  <section class="news">
    <h2>Today's match report</h2>
    <div class="players">
      <div>Batter of the day</div>
      <div>Best bowler</div>
      <div>Best catch</div>
      <div>Fastest fifty</div>
    </div>
  </section>
  <aside class="scores">Live scores</aside>
  <footer>Footer</footer>
</div>

Key Takeaways

  • grid-template-areas lets you draw the layout with names, and grid-area puts each item in its area.
  • Every named area must be a rectangle, and every row of the map must have the same number of cells.
  • repeat(auto-fit, minmax(160px, 1fr)) makes cards wrap on their own, without media queries.
  • Redraw the map inside a media query to rearrange the page for bigger screens.
  • place-items, justify-items and align-items position items inside their cells.
  • Any grid item can be a grid itself. Each grid controls only its own children.

❓ Frequently Asked Questions

Yes, but they will sit on top of each other in the same area. Usually each area name belongs to one element.
Use template areas for big page layouts with a few named parts, because the map is easy to read. Use line numbers or span for galleries and small grids with many similar items.
Not completely. It is perfect for lists of similar cards. For changing a whole page layout, such as moving a sidebar, you still use media queries.
There is no fixed limit, but two or three levels are usually enough. Deeper nesting makes the code hard to understand.
Was this lesson helpful?

🎯 Finished this lesson?

🔗 Related Tutorials
🤖 Got a Doubt? Ask AI
Get instant answers in Hindi or English
🤖
Z2T Bot
Online
Ask me anything about this tutorial — I'm here! 👋