Code of the Day
IntermediateLayout Systems

Lab — Layout Challenge

Build a two-column blog page layout and a card grid using Grid for the page structure and Flexbox for the component interiors — no floats, no absolute positioning tricks.

Lab · optionalWeb FoundationsIntermediate30 min
By the end of this lesson you will be able to:
  • Build a full page layout using named grid template areas
  • Create a responsive card grid with repeat and minmax
  • Use Flexbox inside each card to push a CTA button to the bottom
  • Confirm the layout works at any viewport width above 320px without a media query

This lab puts the whole module together. You will build two things: a page-level layout with a header, sidebar, main content area, and footer — all positioned with CSS Grid and named areas — and a responsive card grid inside the main content area where each card uses Flexbox to pin its call-to-action button to the bottom regardless of content length.

No floats. No hard-coded widths in percentages. No media queries. The layout holds at any viewport width above 320px using only what you have learned.

What you are building

┌─────────────────────────────────────────┐
│              Header (full width)        │
├──────────┬──────────────────────────────┤
│          │  Card  │  Card  │  Card  │   │
│ Sidebar  │────────┼────────┼────────┤   │
│          │  Card  │  Card  │        │   │
├──────────┴──────────────────────────────┤
│              Footer (full width)        │
└─────────────────────────────────────────┘

On narrow viewports, the card grid collapses to fewer columns automatically. The sidebar stays fixed-width; the main content shrinks to fill the rest.

Part 1: Page layout

Start with the HTML skeleton:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Layout Challenge</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div class="page">
    <header class="page-header">The Blog</header>
    <aside class="page-sidebar">
      <nav>
        <ul>
          <li><a href="#">Technology</a></li>
          <li><a href="#">Design</a></li>
          <li><a href="#">Business</a></li>
        </ul>
      </nav>
    </aside>
    <main class="page-main">
      <!-- Card grid goes here (Part 2) -->
    </main>
    <footer class="page-footer">© 2026 The Blog</footer>
  </div>
</body>
</html>

Now the CSS for the page layout. Write this in styles.css:

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: system-ui, sans-serif;
}

.page {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
}

.page-header  { grid-area: header;  padding: 16px 24px; background: #1d3557; color: white; }
.page-sidebar { grid-area: sidebar; padding: 24px 16px; background: #f8f8f8; }
.page-main    { grid-area: main;    padding: 24px; }
.page-footer  { grid-area: footer;  padding: 12px 24px; background: #333; color: white; font-size: 0.875rem; }

Open the file in a browser. You should see the full layout: header spanning the full width, a fixed 220px sidebar on the left, a stretchy main area on the right, and a full-width footer at the bottom.

The 1fr on grid-template-columns is why the main content grows to fill all remaining horizontal space. The sidebar is fixed at 220px; everything left over goes to the main column. Change it to 240px 1fr to widen the sidebar — the main content adjusts automatically.

Part 2: Responsive card grid

Inside .page-main, add five or six article cards. Here is the HTML for one:

<div class="card-grid">
  <article class="card">
    <div class="card-body">
      <h2 class="card-title">How AI changes product design</h2>
      <p class="card-excerpt">
        The tools have shifted but the core questions have not. Understanding
        what users actually need still requires human judgment.
      </p>
    </div>
    <div class="card-footer">
      <a href="#" class="card-cta">Read more</a>
    </div>
  </article>

  <!-- Repeat for additional cards with different titles and excerpts -->
</div>

Add cards with varying excerpt lengths — some short, some long. This is the key test: the CTA button should sit at the bottom of every card regardless of how much text is above it.

The CSS for the card grid and individual cards:

/* Responsive grid: as many columns as fit, each at least 240px */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
}

/* Each card: a vertical flex container */
.card {
  display: flex;
  flex-direction: column;   /* stack card-body and card-footer vertically */
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  overflow: hidden;
}

/* Card body grows to consume all available height */
.card-body {
  flex: 1;                  /* grows to push card-footer to the bottom */
  padding: 20px;
}

.card-title {
  margin: 0 0 12px;
  font-size: 1.125rem;
}

.card-excerpt {
  margin: 0;
  color: #555;
  line-height: 1.5;
}

/* Card footer is pinned to the bottom because card-body has flex: 1 */
.card-footer {
  padding: 16px 20px;
  border-top: 1px solid #e0e0e0;
}

.card-cta {
  display: inline-block;
  padding: 8px 16px;
  background: #1d3557;
  color: white;
  text-decoration: none;
  border-radius: 4px;
  font-size: 0.875rem;
}

The key is the two-layer layout pattern:

  1. .card-grid is a Grid container — it places cards into a responsive column arrangement.
  2. Each .card is a Flexbox container — it arranges the card body and footer vertically, with flex: 1 on the body to consume all leftover vertical space and push the footer to the bottom.

Self-review checklist

Open your layout in a browser and work through these checks:

  • Full-width header and footer — both span across the sidebar and main content columns. Check grid-template-areas if they do not.
  • Sidebar width is fixed, main content fills the rest — resize the browser window and confirm the main content stretches without the sidebar changing width.
  • Card grid columns adapt without a media query — narrow the browser to around 500px. Cards should collapse from three columns to two to one automatically.
  • CTA buttons are bottom-aligned — cards with short excerpts and cards with long excerpts should both have the "Read more" button at the same vertical position (the bottom of the card). If they are not aligned, check that .card has display: flex; flex-direction: column and .card-body has flex: 1.
  • No horizontal scroll at 320px — resize to the narrowest point. If cards overflow, check the minmax(240px, 1fr) value — 240px may need to be reduced, or replaced with minmax(min(240px, 100%), 1fr).
  • Layout holds without any media queries — search your CSS for @media. If you added any, try removing them and see if the intrinsic layout handles it.

If you want to go further: try collapsing the sidebar below a certain viewport width using a single media query. The named template areas make this clean — you redefine grid-template-areas and grid-template-columns in the media query and the whole layout restructures. Everything else stays the same.

Concepts applied in this lab

ConceptWhere it appeared
grid-template-areasPage-level layout (header, sidebar, main, footer)
grid-areaAssigning each section to its named area
1frStretchy main content column
repeat(auto-fit, minmax())Responsive card grid columns
display: flex; flex-direction: columnVertical layout inside each card
flex: 1 on .card-bodyPushing footer to the bottom
gapSpacing between both grid cells and flex children

When you can build this pattern from memory — page Grid + card Flexbox — you have the layout vocabulary to handle ninety percent of real-world CSS layout problems.

Finished reading? Mark it complete to track your progress.

On this page