grid-template-areas — Dashboard Layout Without Framework
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
| Job | Prefer |
|---|---|
| Page shell regions | grid-template-areas |
| Sidebar width + fluid main | Grid columns |
| Horizontal nav links | Flex inside header |
| Form label + field rows | Grid or flex inside main |
| Overlapping decorative layers | Grid 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%chains —min-height: 100dvhon the shell is simpler.minmaxforgotten — long sidebar labels can blow a fixed240pxcolumn; useminmax(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.