Real-World Grid Layouts

โ–ถ Try It Yourself

You now know every major CSS Grid property. In this final lesson you will put it all together โ€” building four production-quality layouts from scratch: a responsive dashboard, a photo masonry gallery, a full-page app shell, and a pricing page. For each layout you will see which Grid features are chosen and exactly why, reinforcing the decision-making skills that separate confident Grid users from those who still reach for floats or tables.

Choosing Grid vs Flexbox

Scenario Best Tool Reason
Row of buttons, nav links Flexbox One-dimensional; items flow in a line
Card grid with equal columns Grid Two-dimensional; strict column alignment
Page layout (header/sidebar/main) Grid Named areas; row+column control simultaneously
Media object (image + text) Flexbox Single row; alignment + gap is all you need
Masonry / magazine layout Grid Items span multiple tracks; explicit placement
Chat bubble alignment Flexbox column Items stack; align-self moves individual bubbles
Form layout (label + input pairs) Grid Two-column alignment across rows

Grid Subgrid (Modern)

Feature Property Use Case
Subgrid columns grid-template-columns: subgrid Child inherits parent column tracks โ€” aligns across cards
Subgrid rows grid-template-rows: subgrid Child rows align with parent row tracks
Support Chrome 117+, Firefox 71+, Safari 16+ Progressive enhancement โ€” check support before using

CSS Grid Shorthand: grid-template

Longhand grid-template Shorthand
grid-template-rows: 60px 1fr 48px; grid-template-columns: 240px 1fr; grid-template-areas: ... grid-template: "h h" 60px "n m" 1fr "f f" 48px / 240px 1fr
Note: CSS Grid and Flexbox are not mutually exclusive โ€” they are designed to be used together. The most powerful real-world layouts use Grid for the page structure (rows and columns of the overall layout) and Flexbox inside individual grid areas for aligning component-level content like navigation items, buttons, and media objects.
Tip: For equal-height cards that all have their footer at the same vertical position, use a nested Grid inside each card: grid-template-rows: auto auto 1fr auto (image, tag, body, footer). The 1fr row stretches the body to fill available space, pushing the footer to the bottom โ€” without needing the outer grid to do any special work.
Warning: The grid-template shorthand is very powerful but also complex and easy to misread. For production codebases with multiple developers, prefer the explicit longhand properties โ€” they are easier to understand at a glance, easier to update at breakpoints, and less likely to cause merge conflicts in version control.

Basic Example โ€” Four Production Layouts

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Real-World Grid Layouts</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: system-ui, sans-serif; background: #f1f5f9; padding: 32px; }
    .section-title { font-size: 0.75rem; font-weight: 800; text-transform: uppercase;
                     letter-spacing: 0.08em; color: #64748b; margin: 40px 0 12px; }

    /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
       LAYOUT 1: Holy Grail with named areas
    โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
    .holy-grail {
      display: grid;
      grid-template-columns: 200px 1fr 180px;
      grid-template-rows: 52px 1fr 44px;
      grid-template-areas:
        "header  header  header"
        "nav     main    aside"
        "footer  footer  footer";
      height: 360px;
      border-radius: 12px;
      overflow: hidden;
      border: 1px solid #e2e8f0;
      gap: 0;
      margin-bottom: 32px;
    }
    .hg-header { grid-area: header; background: #1e293b; color: white; padding: 0 20px; display: flex; align-items: center; font-weight: 700; font-size: 0.9rem; }
    .hg-nav    { grid-area: nav;    background: #f8fafc; border-right: 1px solid #e2e8f0; padding: 16px 0; }
    .hg-main   { grid-area: main;   background: white; padding: 24px; overflow-y: auto; }
    .hg-aside  { grid-area: aside;  background: #f8fafc; border-left: 1px solid #e2e8f0; padding: 16px; }
    .hg-footer { grid-area: footer; background: #1e293b; color: #94a3b8; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; font-size: 0.75rem; }
    .hg-navlink { display: block; padding: 8px 16px; font-size: 0.8rem; color: #475569; text-decoration: none; }
    .hg-navlink:hover { background: #ede9fe; color: #4f46e5; }
    .hg-main h2 { font-size: 1rem; margin-bottom: 10px; color: #0f172a; }
    .hg-main p  { font-size: 0.85rem; color: #64748b; line-height: 1.6; }
    .hg-aside p { font-size: 0.8rem; color: #64748b; line-height: 1.5; }

    /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
       LAYOUT 2: Responsive card grid
    โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
    .card-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
      gap: 16px;
      margin-bottom: 32px;
    }
    .grid-card {
      background: white;
      border-radius: 12px;
      border: 1px solid #e2e8f0;
      padding: 20px;
      display: grid;
      grid-template-rows: auto 1fr auto;
      gap: 8px;
    }
    .grid-card-icon { font-size: 1.5rem; }
    .grid-card-title { font-size: 0.95rem; font-weight: 700; color: #0f172a; }
    .grid-card-desc  { font-size: 0.8rem; color: #64748b; line-height: 1.5; }
    .grid-card-link  { font-size: 0.8rem; color: #4f46e5; font-weight: 600; text-decoration: none; }

    /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
       LAYOUT 3: Magazine grid
    โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
    .magazine {
      display: grid;
      grid-template-columns: repeat(6, 1fr);
      grid-auto-rows: 120px;
      gap: 8px;
      margin-bottom: 32px;
    }
    .mag-item {
      border-radius: 8px;
      display: flex;
      align-items: flex-end;
      padding: 12px;
      font-size: 0.75rem;
      font-weight: 700;
      color: white;
    }
    .mag-lead { grid-column: span 4; grid-row: span 2; background: linear-gradient(135deg,#4f46e5,#7c3aed); font-size: 1rem; }
    .mag-a    { grid-column: span 2; background: linear-gradient(135deg,#0891b2,#0284c7); }
    .mag-b    { grid-column: span 2; background: linear-gradient(135deg,#10b981,#059669); }
    .mag-c    { grid-column: span 2; background: linear-gradient(135deg,#f59e0b,#d97706); }
    .mag-d    { grid-column: span 3; background: linear-gradient(135deg,#ef4444,#dc2626); }
    .mag-e    { grid-column: span 3; background: linear-gradient(135deg,#8b5cf6,#7c3aed); }

    /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
       LAYOUT 4: Pricing grid with subgrid-like alignment
    โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */
    .pricing-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
      gap: 16px;
      align-items: start;
    }
    .pricing-card {
      background: white;
      border-radius: 16px;
      border: 2px solid #e2e8f0;
      padding: 28px;
      display: grid;
      grid-template-rows: auto auto 1fr auto;
      gap: 16px;
    }
    .pricing-card.featured { border-color: #4f46e5; background: #fafafe; }
    .pricing-plan { font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em; color: #4f46e5; }
    .pricing-price { font-size: 2.5rem; font-weight: 800; color: #0f172a; line-height: 1; }
    .pricing-price span { font-size: 1rem; font-weight: 400; color: #64748b; }
    .pricing-features { list-style: none; font-size: 0.85rem; color: #475569; display: grid; gap: 8px; }
    .pricing-features li::before { content: "โœ“  "; color: #10b981; font-weight: 700; }
    .pricing-cta { display: block; text-align: center; padding: 12px; background: #4f46e5; color: white; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 0.875rem; }
  </style>
</head>
<body>

  <p class="section-title">1 โ€” Holy Grail: grid-template-areas</p>
  <div class="holy-grail">
    <header class="hg-header">MyApp Dashboard</header>
    <nav class="hg-nav">
      <a class="hg-navlink" href="#">Dashboard</a>
      <a class="hg-navlink" href="#">Projects</a>
      <a class="hg-navlink" href="#">Reports</a>
    </nav>
    <main class="hg-main">
      <h2>Holy Grail Layout</h2>
      <p>Built with <code>grid-template-areas</code> โ€” header, nav, main, aside, footer defined as named regions. The HTML is clean semantic markup; CSS draws the layout map.</p>
    </main>
    <aside class="hg-aside"><p><strong>Sidebar</strong><br>aside: grid-area: aside</p></aside>
    <footer class="hg-footer"><span>&copy; 2025</span><span>CSS Grid Demo</span></footer>
  </div>

  <p class="section-title">2 โ€” Auto-responsive card grid</p>
  <div class="card-grid">
    <div class="grid-card"><span class="grid-card-icon">📈</span><h3 class="grid-card-title">Analytics</h3><p class="grid-card-desc">Track key metrics and visualise trends across your entire platform.</p><a class="grid-card-link" href="#">View reports →</a></div>
    <div class="grid-card"><span class="grid-card-icon">👥</span><h3 class="grid-card-title">Team</h3><p class="grid-card-desc">Manage members, roles, and permissions for your organisation.</p><a class="grid-card-link" href="#">Manage team →</a></div>
    <div class="grid-card"><span class="grid-card-icon">🔒</span><h3 class="grid-card-title">Security</h3><p class="grid-card-desc">Configure two-factor auth, API keys, and audit logs for your account.</p><a class="grid-card-link" href="#">Security settings →</a></div>
    <div class="grid-card"><span class="grid-card-icon">⚙</span><h3 class="grid-card-title">Integrations</h3><p class="grid-card-desc">Connect your workflow with Slack, GitHub, Jira and 50+ other tools.</p><a class="grid-card-link" href="#">Browse integrations →</a></div>
  </div>

  <p class="section-title">3 โ€” Magazine grid with explicit placement</p>
  <div class="magazine">
    <div class="mag-item mag-lead">Lead story โ€” col span 4, row span 2</div>
    <div class="mag-item mag-a">Feature A</div>
    <div class="mag-item mag-b">Feature B</div>
    <div class="mag-item mag-c">Feature C</div>
    <div class="mag-item mag-d">Wide D โ€” span 3</div>
    <div class="mag-item mag-e">Wide E โ€” span 3</div>
  </div>

  <p class="section-title">4 โ€” Pricing cards with pinned CTA</p>
  <div class="pricing-grid">
    <div class="pricing-card">
      <div class="pricing-plan">Starter</div>
      <div class="pricing-price">$0<span> / mo</span></div>
      <ul class="pricing-features"><li>5 projects</li><li>1 GB storage</li><li>Email support</li></ul>
      <a href="#" class="pricing-cta">Get started</a>
    </div>
    <div class="pricing-card featured">
      <div class="pricing-plan">Pro โ€” Most Popular</div>
      <div class="pricing-price">$19<span> / mo</span></div>
      <ul class="pricing-features"><li>Unlimited projects</li><li>20 GB storage</li><li>Priority support</li><li>Advanced analytics</li><li>Custom domains</li></ul>
      <a href="#" class="pricing-cta">Start free trial</a>
    </div>
    <div class="pricing-card">
      <div class="pricing-plan">Enterprise</div>
      <div class="pricing-price">$49<span> / mo</span></div>
      <ul class="pricing-features"><li>Everything in Pro</li><li>100 GB storage</li><li>SSO & SAML</li><li>24/7 phone support</li></ul>
      <a href="#" class="pricing-cta">Contact sales</a>
    </div>
  </div>

</body>
</html>

How It Works

Step 1 โ€” Holy Grail Uses Zero Line Numbers

Every element finds its place via grid-area: name โ€” no line numbers, no span calculations, no fragile offsets. Changing the layout at a breakpoint means only rewriting the areas string and column definition. The semantic HTML never changes.

Step 2 โ€” Auto-fill Card Grid Handles Responsiveness Automatically

repeat(auto-fill, minmax(240px, 1fr)) creates as many 240px+ columns as fit. Each card uses an internal grid with grid-template-rows: auto auto 1fr auto โ€” the description row gets 1fr to absorb height differences, keeping the “learn more” link consistently aligned across all cards.

Step 3 โ€” Magazine Uses Explicit Placement for Asymmetry

The lead story spans 4 columns and 2 rows using grid-column: span 4; grid-row: span 2. The remaining items auto-place into the 2-column space on the right and then into the next full row. The six-column base grid provides enough granularity for flexible spans.

Step 4 โ€” Pricing Cards Pin CTA via Internal Row Grid

Each pricing card is a 4-row grid: auto auto 1fr auto. The feature list row gets 1fr โ€” it stretches to fill all surplus height. The CTA button is in the last auto row, always at the card’s bottom regardless of feature count. All CTAs align perfectly across cards.

Step 5 โ€” Grid + Flexbox Together

The header and footer of the holy grail layout use display: flex internally for their alignment needs (logo left, user right). CSS Grid handles the page structure; Flexbox handles component-level alignment within each grid area โ€” the two systems complement each other perfectly.

Real-World Example: Responsive Form Layout

/* form-grid.css */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #f8fafc; padding: 40px 24px; }

.form-card {
  background: white;
  border-radius: 16px;
  border: 1px solid #e2e8f0;
  padding: 32px;
  max-width: 640px;
  margin: 0 auto;
  box-shadow: 0 4px 16px rgba(0,0,0,0.06);
}

.form-title { font-size: 1.25rem; font-weight: 700; margin-bottom: 24px; color: #0f172a; }

/* โ”€โ”€ Grid form layout โ”€โ”€ */
.form-grid {
  display: grid;
  grid-template-columns: 1fr;  /* single column mobile */
  gap: 16px;
}

@media (min-width: 520px) {
  .form-grid { grid-template-columns: 1fr 1fr; } /* two-column tablet+ */
}

/* Fields that should span full width */
.field-full { grid-column: 1 / -1; }

/* Label + input group */
.field { display: grid; gap: 6px; }

label {
  font-size: 0.8rem; font-weight: 600;
  color: #374151; letter-spacing: 0.01em;
}
input, select, textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1.5px solid #e2e8f0;
  border-radius: 8px;
  font-size: 0.875rem;
  font-family: system-ui, sans-serif;
  color: #1e293b;
  background: white;
  transition: border-color 0.15s;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: #4f46e5;
  box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
textarea { resize: vertical; min-height: 100px; }

/* Form footer with submit button */
.form-footer {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  padding-top: 8px;
}
.btn-cancel {
  padding: 10px 20px; border: 1.5px solid #e2e8f0;
  border-radius: 8px; background: white; color: #374151;
  font-size: 0.875rem; cursor: pointer; font-family: system-ui, sans-serif;
}
.btn-submit {
  padding: 10px 24px; background: #4f46e5;
  border: none; border-radius: 8px; color: white;
  font-size: 0.875rem; font-weight: 600; cursor: pointer;
  font-family: system-ui, sans-serif;
}

Common Mistakes

Mistake 1 โ€” Using Grid where Flexbox is simpler

โŒ Wrong โ€” Grid overkill for a simple horizontal button row:

.btn-group {
  display: grid;
  grid-template-columns: auto auto auto;
  gap: 8px;
}

โœ… Correct โ€” Flexbox is cleaner for a simple row of items:

.btn-group { display: flex; gap: 8px; }

Mistake 2 โ€” Hardcoding column counts instead of using auto-fill

โŒ Wrong โ€” breaks at unexpected viewport widths:

.grid { display: grid; grid-template-columns: repeat(4, 1fr); }
@media (max-width: 768px) { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .grid { grid-template-columns: 1fr; } }

โœ… Correct โ€” auto-fill handles all widths with one declaration:

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }

Mistake 3 โ€” Not providing grid-auto-rows for dynamic content

โŒ Wrong โ€” implicit rows collapse to 0 height when items are taller than the cell:

.grid { display: grid; grid-template-columns: repeat(3, 1fr); }
/* rows are auto-created but have no minimum height โ€” content overflows */

โœ… Correct โ€” define a minimum row height for auto-created rows:

.grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: minmax(100px, auto); }

▶ Try It Yourself

Quick Reference

Pattern Key CSS Notes
Responsive card grid repeat(auto-fill, minmax(240px, 1fr)) No media queries needed
Holy grail layout grid-template-areas + named grid-area Redefine areas at breakpoints
Magazine grid grid-column: span N; grid-row: span N; 6-column base for flexibility
Pinned card footer Card: grid 4 rows; body row: 1fr; footer row: auto Works across unequal content heights
Two-column form grid-template-columns: 1fr 1fr; field-full: 1/-1 Single column on mobile via media query
Grid + Flexbox Grid for page structure; Flexbox inside areas They complement each other โ€” use both

🧠 Test Yourself

You need a responsive product grid that automatically creates as many columns as will fit, each at least 280px wide. Which declaration achieves this with no media queries?





โ–ถ Try It Yourself