Docs Layout
Three-column documentation layout: sidebar, content, TOC. data-layout="docs" on a wrapper element.
The docs layout is the scaffold for this documentation site. Three columns: a sticky sidebar nav on the left, a constrained content column in the centre, and a sticky in-page TOC on the right. On narrow viewports, the TOC hides below 1100px and the sidebar hides below 768px.
You are looking at a live demo right now. The structure wrapping this article is [data-layout="docs"].
Structure
Three children, in order: a left <aside> for the sidebar nav, an <article> for the main content, and a right <aside data-toc> for the table of contents.
<div data-layout="docs">
<aside>
<nav aria-label="Documentation" data-nav="sidebar">
<small>Section</small>
<ul>
<li><a href="page.html" aria-current="page">Current page</a></li>
<li><a href="other.html">Other page</a></li>
</ul>
</nav>
</aside>
<article>
<!-- main content -->
</article>
<aside data-toc>
<nav aria-label="On this page" data-nav="toc">
<small>On this page</small>
<ul>
<li><a href="#section">Section</a></li>
</ul>
</nav>
</aside>
</div>
CSS grid assigns the three columns by source order. Left aside → article → right aside. Swapping order breaks the layout.
Columns
The grid template at full width: 220px 1fr 200px. The content column takes all remaining space. Max width of the whole layout: 1400px, centred.
| Column | Element | Width | Behaviour |
|---|---|---|---|
| Left | <aside> | 220px | Sticky, hides below 768px |
| Centre | <article> | 1fr (max 70ch) | Scrolls normally |
| Right | <aside data-toc> | 200px | Sticky, hides below 1100px |
The centre <article> has max-width: 70ch — comfortable reading line length. It won't stretch to fill the full 1fr column, so the grid gap appears on both sides at wider viewports.
Sticky sidebars
Both <aside> elements are position: sticky with top: calc(60px + space-4) — accounting for the top nav height. They scroll with the page until they reach their sticky top offset, then lock in place.
Both have max-height: calc(100vh - 80px) and overflow-y: auto, so a tall sidebar nav will scroll internally without moving the main content.
Stickiness requires the [data-layout="docs"] wrapper to have align-items: start. Without it, the aside stretches to the full grid row height and sticky has no room to work. This is set by the stylesheet — do not override align-items on the wrapper.
Prev / Next navigation
Add a [data-role="prev-next"] block at the end of the article to link to adjacent pages. The pattern handles both previous-only, next-only, and both.
<div data-role="prev-next">
<a href="prose.html" rel="prev">
<small>← Previous</small>
<span>Prose</span>
</a>
<a href="footer.html" rel="next">
<small>Next →</small>
<span>Footer</span>
</a>
</div>
Previous aligns left; next aligns right. Either can be omitted — the remaining link holds its position. The rel attribute is meaningful to search engines and screen readers, not just to the stylesheet.
Responsive behaviour
| Viewport | Layout |
|---|---|
| ≥ 1100px | Three columns: sidebar + content + TOC |
| 768px – 1099px | Two columns: sidebar + content (TOC hidden) |
| < 768px | Single column: content only (both sidebars hidden) |
The sidebars are hidden, not just narrowed — they disappear entirely on small screens. If sidebar navigation is critical on mobile, add a supplemental nav inside the article or use a different layout pattern.
Scroll-spy for TOC
The right sidebar TOC can track the active heading as the user scrolls. Include toc-spy.js before </body>:
<script src="../toc-spy.js"></script>
The script observes h2[id] and h3[id] inside <article> using IntersectionObserver. As each heading enters the viewport, it sets aria-current="true" on the matching TOC link. No dependencies, no configuration.
Without the script, set aria-current="page" on the active TOC link in HTML for a static active state. The stylesheet responds to any truthy aria-current value.
Reference
| Selector | Element | Purpose |
|---|---|---|
[data-layout="docs"] | Any block | Three-column grid wrapper |
> aside:first-of-type | <aside> | Left sidebar (sticky) |
> article | <article> | Main content column |
> aside[data-toc] | <aside data-toc> | Right TOC sidebar (sticky) |
[data-role="prev-next"] | Any block | Previous/Next navigation strip |