grid-template-areas — Dashboard Layout Without Framework

webdev css-grid layout dashboard tools

A team reaches for Bootstrap’s twelve-column grid to build a three-region admin shell: sidebar, top bar, main. CSS Grid already has a readable way to draw that shell — grid-template-areas — without a framework or a pile of offset classes. The ASCII map in CSS becomes documentation that stays true as long as you keep the strings honest.

Draw the layout as ASCII

.dashboard {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: 56px 1fr;
  grid-template-areas:
    "nav  header"
    "nav  main";
  min-height: 100dvh;
}

.nav    { grid-area: nav; }
.header { grid-area: header; }
.main   { grid-area: main; }
<div class="dashboard">
  <nav class="nav">…</nav>
  <header class="header">…</header>
  <main class="main">…</main>
</div>

Each row must have the same number of cells. Repeat a name to span:

grid-template-areas:
  "header header"
  "nav    main";

That spanning pattern is how you get a full-width header without inventing nested rows of bootstrap columns.

Responsive rearrange without new markup

@media (max-width: 800px) {
  .dashboard {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto 1fr;
    grid-template-areas:
      "header"
      "nav"
      "main";
  }
}

Same components, new map. That is the feature teams fake with duplicate HTML fragments in component libraries. Keep one set of landmarks and let the template describe where they sit at each breakpoint.

Areas vs flex — split responsibilities

JobPrefer
Page shell regionsgrid-template-areas
Sidebar width + fluid mainGrid columns
Horizontal nav linksFlex inside header
Form label + field rowsGrid or flex inside main
Overlapping decorative layersGrid with explicit lines / absolute

Grid does not obsolete flex. It removes the need for outer row/column class soup. Put flex inside cells for toolbars; put areas on the shell.

Accessibility and source order

Visual placement can differ from DOM order, but screen readers and tab order follow the DOM. Keep a skip link, nav landmarks, and main content in a meaningful sequence. Do not invent a cute visual order that jumps focus unpredictably. If the mobile map stacks header → nav → main, consider matching DOM to that story so keyboard users are not surprised.

Common breakage

  • Uneven row lengths in the areas string → invalid template.
  • Typo grid-area: mai → item missing or auto-placed.
  • Margins on grid items fighting expectations — prefer gap.
  • height: 100% chainsmin-height: 100dvh on the shell is simpler.
  • minmax forgotten — long sidebar labels can blow a fixed 240px column; use minmax(0, 1fr) on fluid tracks when overflow appears.

Sketch templates in a CSS grid playground before wiring components — especially when spanning header across columns.

Nested grids

Main content can be its own grid for cards and widgets. Name areas only where the map clarifies. Deep ASCII maps for every widget become noise; reserve named areas for the shell and maybe one dense featured view. Inside widgets, plain columns or flex usually read better.

When a framework still helps

Design systems, complex responsive utilities, and preexisting component libraries may already own layout. Adding areas inside a Bootstrap page is fine for one shell; rewriting the world mid-sprint is not. Use Grid where you own the CSS, and keep framework grids where the design system already encodes spacing tokens.

Dashboard shells are maps. grid-template-areas lets you read the map in CSS, rearrange it for small screens, and keep flex for the toolbars living inside each region — no twelve-column mythology required. Once the shell is stable, most “layout bugs” turn out to be content overflow inside a cell, not a missing framework class.