webdevpuneet.com

Holy Grail Layout — CSS Grid Header Nav Aside Footer

Holy Grail Layout · Layouts · Plain HTML, CSS & JS · Live preview

CategoryLayouts

Copy & paste

Source Code

<div class="hg">
  <header class="hg-header">
    <span class="hg-logo">◆ Acme</span>
    <button type="button" class="hg-burger" id="hgBurger" aria-label="Toggle navigation" aria-expanded="false">☰</button>
  </header>
  <div class="hg-body">
    <nav class="hg-nav" id="hgNav">
      <a href="#" class="hg-active">Dashboard</a>
      <a href="#">Projects</a>
      <a href="#">Team</a>
      <a href="#">Reports</a>
      <a href="#">Settings</a>
    </nav>
    <main class="hg-main">
      <h1>Holy Grail Layout</h1>
      <p>Header and footer span the full width. Between them, a left nav and right aside flank a fluid main column that takes the remaining space — and all three stretch to the same height.</p>
      <div class="hg-cards"><div></div><div></div><div></div><div></div></div>
    </main>
    <aside class="hg-aside">
      <h4>On this page</h4>
      <a href="#">Overview</a>
      <a href="#">Activity</a>
      <a href="#">Members</a>
    </aside>
  </div>
  <footer class="hg-footer">© 2026 Acme · Holy grail in CSS grid</footer>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Three-row page grid
auto / 1fr / auto rows pin the footer to the bottom — a sticky footer for free.
Fixed-fluid-fixed columns
One grid-template-columns line creates the nav / main / aside row, all equal height.
Natural source order
Nav, main, and aside sit in reading order in the HTML — no float source-order tricks.
Two-stage responsive
The aside drops below at tablet width; columns collapse and nav drawers at phone width.
Off-canvas mobile nav
The nav slides in as a transform-animated drawer behind a hamburger on small screens.
Accessible toggle
A real button with aria-expanded, and links auto-close the drawer.
Layout needs no JS
JavaScript only toggles the mobile drawer; the grid layout is pure CSS.
Drop-in app shell
The canonical structure for dashboards, docs, and admin panels.

About this UI Snippet

Holy Grail Layout — Header, Footer, Two Sidebars & a Fluid Centre in CSS Grid

Screenshot of the Holy Grail Layout snippet rendered live

The "holy grail" layout — a full-width header and footer with three columns between them (a fixed-width left nav, a fluid main content area, and a fixed-width right aside, all equal height) — was famously hard in the float era. With CSS grid it's a handful of lines. This snippet builds the complete, responsive holy-grail layout in plain HTML and CSS, with a small bit of JavaScript only for the mobile nav toggle, and no library.

The page as a three-row grid

The outer container is a grid with grid-template-rows: auto 1fr auto — the header and footer size to their content while the middle row expands to fill the viewport, which is what pins the footer to the bottom on short pages (a "sticky footer" for free). min-height: 100vh ensures the layout always fills the screen. This row structure is the skeleton every app shell shares.

The grail row in one line

The middle row is itself a grid: grid-template-columns: 200px 1fr 220px. That single declaration is the entire trick the float era struggled with — fixed-fluid-fixed columns that the browser sizes automatically, with all three stretching to the same height because grid items fill their row by default. No clearfix, no negative margins, no source-order gymnastics: the nav, main, and aside appear in natural reading order in the HTML and the grid places them correctly.

Responsive collapse in two steps

The layout degrades gracefully at two breakpoints. On tablets the right aside drops below the main content by spanning the full grid width (grid-column: 1 / -1) and laying its links out in a row — keeping the nav and main side by side where there's still room. On phones the columns collapse to a single column and the nav becomes an off-canvas drawer behind a hamburger, sliding in with a transform. This two-stage approach reflows for the space available rather than forcing one mobile layout everywhere.

Accessible, progressive mobile nav

The hamburger is a real button with aria-expanded that toggles as the drawer opens and closes, and choosing any nav link auto-closes the drawer — the expected behaviour on mobile. Crucially, the nav is part of the normal layout on desktop and only becomes a drawer at the small breakpoint, so there's no JavaScript dependency for the layout itself: the JS does nothing but toggle a class on small screens.

A reference app shell

Because it's the canonical structure behind dashboards, docs sites, and admin panels, this layout is a drop-in starting shell — fill the main with your content and wire the nav to your routes. It's also the clearest possible demonstration of why CSS grid replaced the float-and-clear contortions that made this layout a "holy grail" in the first place.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA full holy-grail layout renders: header, left nav, main, right aside, and footer.
  2. 2
    Resize the windowAt tablet width the aside drops below main; at phone width the nav becomes a hamburger drawer.
  3. 3
    Open the mobile navOn a narrow screen, tap the hamburger to slide in the nav; picking a link closes it.
  4. 4
    Fill in your contentReplace the main column's placeholder content and wire the nav links to your routes.
  5. 5
    Adjust column widthsChange the 200px / 1fr / 220px grid-template-columns to fit your nav and aside.
  6. 6
    Tune the breakpointsEdit the 860px and 560px media queries to match your design's reflow points.

Real-world uses

Common Use Cases

Dashboard and admin shells
Use as the frame for an admin app with a dashboard layout inside, with a header and footer spanning the full width.
Docs three-column shells
Place a sidebar nav for sections on the left and a table of contents in the right aside.
Content and article pages
Present a main article beside related links, with the columns in natural source order and no float tricks.
Web app starting points
Begin any multi-section interface from a responsive shell, with the aside dropping below at tablet width and columns collapsing on phones.
Settings areas and grid learning
Put a nav of sections beside a settings panel, or compare with a bento grid while learning how auto 1fr auto rows pin the footer.
Related: Resizable Split Pane
See the Resizable Split Pane for a related layouts pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

For years it was the hard-to-achieve ideal: a header and footer spanning full width, with a fixed left sidebar, a fluid centre, and a fixed right sidebar between them — all equal height, with the content in natural source order for accessibility. Floats and tables could only approximate it with hacks. CSS grid makes it trivial, which is why it's the canonical "look how much grid simplified things" example.

The page container is a grid with grid-template-rows: auto 1fr auto and min-height: 100vh. The header and footer rows size to their content (auto), while the middle row takes all remaining space (1fr). On a short page the 1fr row stretches to fill the viewport, pushing the footer to the bottom; on a long page everything grows naturally. No JavaScript or absolute positioning needed.

Grid items stretch to fill their grid area by default (align-items defaults to stretch). Because the nav, main, and aside all sit in the same grid row, they each fill that row's full height automatically — so the sidebars match the main column's height regardless of which has the most content. This was the single hardest part to achieve with floats.

No — the layout is pure CSS grid. JavaScript is used only to toggle the off-canvas nav drawer on phone-width screens (open/close a class and update aria-expanded). On tablet and desktop the nav is part of the grid and the JS does nothing, so the core layout works even with scripts disabled.

Wrap it as a layout component with slots/children for header, nav, main, aside, and footer; in React put it in a layout component (or Next.js layout.tsx), in Vue use <slot>s, in Angular use <ng-content>. The grid CSS is framework-agnostic — only the mobile-drawer open state moves into component state with a class binding.