Layout Challenges

Difficulty: Advanced

CSS layout challenges are practical interview tasks that test your ability to implement common web page patterns using modern CSS. These challenges go beyond theoretical knowledge and require hands-on proficiency with Flexbox, Grid, and responsive design techniques. The most commonly asked layouts include the holy grail, sticky footer, equal-height columns, and responsive card grids. Being able to build these from scratch, quickly and correctly, is a key differentiator in frontend interviews.

The holy grail layout is a classic three-column design with a header, footer, left sidebar, right sidebar, and a fluid main content area in the center. The key requirements are: the content area fills all available horizontal space, the sidebars have fixed widths, all three columns have equal height (matching the tallest), the footer stays below all content, and the layout is responsive. CSS Grid makes this trivially easy with grid-template-areas: you define named areas (header, sidebar-left, main, sidebar-right, footer) and place elements into them, creating a robust layout in about 10 lines of CSS.

The sticky footer pattern ensures the footer stays at the bottom of the viewport when page content is short, but scrolls down naturally when content is tall enough. The modern solution uses min-height:100vh on the body or wrapper with Flexbox or Grid. With Flexbox, set the main content area to flex:1 so it expands to fill remaining space. With Grid, use grid-template-rows:auto 1fr auto where 1fr on the main content row absorbs all remaining height. Both approaches are concise and avoid the old negative margin or calc() hacks.

Equal-height columns were historically one of CSS's hardest problems. Before Flexbox, developers used faux-column background images, table display hacks, or JavaScript to equalize column heights. Flexbox solved this by default: flex items in a row stretch to the height of the tallest item (align-items:stretch is the default). CSS Grid goes further, ensuring all items in the same row share the same height through explicit row tracks. For card grids, this means all cards in a row appear the same height regardless of content length.

Responsive card grids are built most effectively with CSS Grid's auto-fill and minmax() functions. The pattern grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)) creates a grid that automatically adjusts the number of columns based on container width: each column is at least 280px wide and grows to fill available space. This single line replaces the need for media queries at multiple breakpoints. Combined with gap for gutters and auto-fit (which collapses empty tracks to let items stretch), you get a fully responsive layout with minimal code.

Code examples

Holy Grail layout with CSS Grid

<style>
  .holy-grail {
    display: grid;
    grid-template-areas:
      "header  header  header"
      "sidebar-l main  sidebar-r"
      "footer  footer  footer";
    grid-template-columns: 250px 1fr 200px;
    grid-template-rows: auto 1fr auto;
    min-height: 100vh;
    gap: 0;
  }

  .hg-header {
    grid-area: header;
    background: #1f2937;
    color: #fff;
    padding: 16px 24px;
  }

  .hg-sidebar-left {
    grid-area: sidebar-l;
    background: #f3f4f6;
    padding: 16px;
  }

  .hg-main {
    grid-area: main;
    padding: 24px;
  }

  .hg-sidebar-right {
    grid-area: sidebar-r;
    background: #f3f4f6;
    padding: 16px;
  }

  .hg-footer {
    grid-area: footer;
    background: #1f2937;
    color: #fff;
    padding: 16px 24px;
  }

  /* Responsive: stack on small screens */
  @media (max-width: 768px) {
    .holy-grail {
      grid-template-areas:
        "header"
        "main"
        "sidebar-l"
        "sidebar-r"
        "footer";
      grid-template-columns: 1fr;
    }
  }
</style>

<div class="holy-grail">
  <header class="hg-header">Header</header>
  <aside class="hg-sidebar-left">Left Sidebar (250px)</aside>
  <main class="hg-main">Main Content (fluid)</main>
  <aside class="hg-sidebar-right">Right Sidebar (200px)</aside>
  <footer class="hg-footer">Footer</footer>
</div>

grid-template-areas creates a visual layout map. Fixed sidebar widths (250px, 200px) flank the fluid 1fr main content. min-height:100vh ensures full viewport coverage. The media query collapses to a single column by redefining the grid areas and columns.

Sticky footer with Flexbox and Grid

<style>
  /* Method 1: Flexbox sticky footer */
  .flex-layout {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
  }
  .flex-layout header { padding: 16px 24px; background: #1f2937; color: #fff; }
  .flex-layout main {
    flex: 1;
    /* flex: 1 expands to fill all remaining vertical space */
    /* Pushes footer to bottom when content is short */
    padding: 24px;
  }
  .flex-layout footer { padding: 16px 24px; background: #1f2937; color: #fff; }

  /* Method 2: Grid sticky footer */
  .grid-layout {
    display: grid;
    grid-template-rows: auto 1fr auto;
    /* auto = header shrinks to fit content */
    /* 1fr  = main takes all remaining space */
    /* auto = footer shrinks to fit content */
    min-height: 100vh;
  }
  .grid-layout header { padding: 16px 24px; background: #374151; color: #fff; }
  .grid-layout main { padding: 24px; }
  .grid-layout footer { padding: 16px 24px; background: #374151; color: #fff; }
</style>

<!-- Flexbox version -->
<div class="flex-layout">
  <header>Header</header>
  <main>
    <p>Short content. Footer stays at bottom.</p>
  </main>
  <footer>Footer (always at bottom)</footer>
</div>

<!-- Grid version -->
<div class="grid-layout">
  <header>Header</header>
  <main>
    <p>Short content. Footer stays at bottom.</p>
  </main>
  <footer>Footer (always at bottom)</footer>
</div>

Both approaches use min-height:100vh on the wrapper. Flexbox uses flex:1 on main to absorb remaining space. Grid uses grid-template-rows: auto 1fr auto where 1fr does the same thing. When content is tall enough, the layout grows naturally beyond 100vh and footer scrolls with the content.

Equal-height columns and card grids

<style>
  /* Equal-height columns with Flexbox */
  .columns {
    display: flex;
    gap: 16px;
  }
  .column {
    flex: 1;
    /* align-items: stretch is the default */
    /* All columns automatically match the tallest */
    background: #f3f4f6;
    padding: 16px;
    border-radius: 8px;
  }

  /* Card grid: equal-height cards within each row */
  .card-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
  }
  .card {
    /* All cards in the same row have equal height (grid row tracks) */
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }
  .card-body {
    padding: 16px;
    flex: 1;
    /* flex:1 pushes footer to bottom of card */
  }
  .card-footer {
    padding: 12px 16px;
    border-top: 1px solid #e5e7eb;
    background: #f9fafb;
  }
</style>

<div class="columns">
  <div class="column">
    <h3>Short</h3>
    <p>Brief content.</p>
  </div>
  <div class="column">
    <h3>Tall Column</h3>
    <p>Much longer content that makes this column taller than the others. All columns will stretch to match this height.</p>
  </div>
  <div class="column">
    <h3>Medium</h3>
    <p>Some content.</p>
  </div>
</div>

<div class="card-grid">
  <div class="card">
    <div class="card-body">
      <h3>Card 1</h3>
      <p>Short text.</p>
    </div>
    <div class="card-footer">Footer always at bottom</div>
  </div>
  <div class="card">
    <div class="card-body">
      <h3>Card 2</h3>
      <p>Longer text that makes this card's content taller than the others.</p>
    </div>
    <div class="card-footer">Footer always at bottom</div>
  </div>
  <div class="card">
    <div class="card-body">
      <h3>Card 3</h3>
      <p>Medium text.</p>
    </div>
    <div class="card-footer">Footer always at bottom</div>
  </div>
</div>

Flexbox stretch (default) gives equal-height columns. For cards with internal structure, combine grid (for equal card heights per row) with flexbox inside each card (flex-direction:column + flex:1 on the body pushes the card footer to the bottom regardless of content height).

Responsive card grid without media queries

<style>
  /* auto-fill: creates as many columns as fit */
  .responsive-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 20px;
    padding: 20px;
  }

  /* auto-fit: collapses empty tracks (items stretch more) */
  .responsive-grid-fit {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 20px;
    padding: 20px;
  }

  .card {
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    padding: 20px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  }

  .card h3 { margin: 0 0 8px; color: #111827; }
  .card p { margin: 0; color: #6b7280; font-size: 14px; }

  /*
    How it works:
    - Container 1200px wide: 4 columns (280px each + gaps)
    - Container 900px wide:  3 columns
    - Container 600px wide:  2 columns
    - Container 300px wide:  1 column
    All automatic, no media queries needed!

    auto-fill vs auto-fit:
    - auto-fill: keeps empty tracks (columns stay at minmax size)
    - auto-fit:  collapses empty tracks (items grow beyond min)
    With many items, they behave identically.
    With few items, auto-fit stretches them; auto-fill doesn't.
  */
</style>

<div class="responsive-grid">
  <div class="card">
    <h3>Card 1</h3>
    <p>Automatically responsive grid layout with no media queries.</p>
  </div>
  <div class="card">
    <h3>Card 2</h3>
    <p>Each card is at least 280px wide and grows to fill space.</p>
  </div>
  <div class="card">
    <h3>Card 3</h3>
    <p>The number of columns adjusts to the container width.</p>
  </div>
  <div class="card">
    <h3>Card 4</h3>
    <p>Works perfectly for product grids, blog listings, and dashboards.</p>
  </div>
  <div class="card">
    <h3>Card 5</h3>
    <p>No JavaScript, no media queries, just CSS Grid.</p>
  </div>
</div>

repeat(auto-fill, minmax(280px, 1fr)) creates a fully responsive grid. Each column is minimum 280px and maximum 1fr (equal share of space). The browser automatically calculates how many columns fit. This single line replaces multiple media queries for responsive card layouts.

Key points

Concepts covered

Holy Grail Layout, Sticky Footer, Equal Height Columns, Responsive Cards, CSS Grid Layout, Flexbox Layout