Building a Documentation-Style Blog Layout
Technical documentation sites like MDN, Stripe Docs, and Tailwind CSS share a common layout pattern: main content in the center, a sticky sidebar with navigation on one side, and sometimes a table of contents on the other. This layout maximizes readability while providing navigation context.
Problem
Building this layout involves several challenges:
- The sidebar must be sticky but not overlap content on scroll
- The content area must have a maximum width for readability
- The layout must be responsive across screen sizes
- The navigation must highlight the current section
Each of these requirements interacts with the others, making the implementation more complex than it first appears.
Why It Happens
Simple layouts become complex when you add:
- Fixed/sticky navigation: Creates z-index and scroll offset challenges
- Responsive breakpoints: Layout structure must change at different sizes
- Sticky sidebar: Requires careful parent structure to function
- Dynamic content: ToC must sync with scroll position
Solution
Use CSS Grid for the overall layout, Flexbox for component-level alignment, and IntersectionObserver for scroll tracking.
Implementation
HTML Structure:
<div class="page-layout">
<nav class="fixed-navbar">
<!-- Site navigation -->
</nav>
<div class="doc-layout">
<aside class="sidebar-left">
<nav class="doc-nav sticky">
<!-- Documentation navigation -->
</nav>
</aside>
<main class="content">
<article class="prose">
<!-- Article content -->
</article>
</main>
<aside class="sidebar-right">
<nav class="toc sticky">
<!-- Table of contents -->
</nav>
</aside>
</div>
</div>
CSS Layout:
.page-layout {
min-height: 100vh;
}
.fixed-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 64px;
z-index: var(--z-fixed);
}
.doc-layout {
display: grid;
grid-template-columns: 280px 1fr 280px;
max-width: 1600px;
margin: 0 auto;
padding-top: 64px; /* Clear fixed navbar */
}
.sidebar-left,
.sidebar-right {
align-self: start; /* Critical for sticky to work */
}
.doc-nav.sticky,
.toc.sticky {
position: sticky;
top: 80px; /* 64px navbar + 16px gap */
max-height: calc(100vh - 96px);
overflow-y: auto;
}
.content {
padding: 2rem;
}
.prose {
max-width: 760px;
margin: 0 auto;
}
TIP:
align-self: starton sidebar columns prevents them from stretching to match the main content height, which would breakposition: sticky.
Responsive Behavior
/* Hide right sidebar on medium screens */
@media (max-width: 1279px) {
.doc-layout {
grid-template-columns: 280px 1fr;
}
.sidebar-right {
display: none;
}
}
/* Stack layout on small screens */
@media (max-width: 1023px) {
.doc-layout {
grid-template-columns: 1fr;
}
.sidebar-left {
display: none;
}
/* Show mobile nav button */
.mobile-nav-toggle {
display: block;
}
}
Example
Complete responsive implementation:
export function DocLayout({ children }: { children: React.ReactNode }) {
const [isMobileNavOpen, setMobileNavOpen] = useState(false);
return (
<div className="page-layout">
<Navbar onMenuClick={() => setMobileNavOpen(true)} />
{/* Mobile nav drawer */}
<MobileNavDrawer
isOpen={isMobileNavOpen}
onClose={() => setMobileNavOpen(false)}
/>
<div className="doc-layout">
<aside className="sidebar-left">
<nav className="doc-nav sticky">
<DocNavigation />
</nav>
</aside>
<main className="content">
<article className="prose">
{children}
</article>
</main>
<aside className="sidebar-right">
<TableOfContents />
</aside>
</div>
</div>
);
}
Common Mistakes
Forgetting scroll-padding-top
When clicking ToC links, the browser scrolls headings to the top, behind the fixed navbar:
/* Fix: Offset scroll position for fixed header */
html {
scroll-padding-top: 80px;
}
Sidebar overflow clipping content
Sidebars with overflow: hidden break sticky positioning inside them. Use overflow: clip or avoid overflow entirely.
Grid columns not responsive
Define breakpoints intentionally:
@media (max-width: 1279px) {
/* Two columns: nav + content */
}
@media (max-width: 1023px) {
/* Single column with mobile nav */
}
WARNING: Do not assume users have large screens. Test at 1024px and 768px widths minimum.
Hard-coded heights
Avoid pixel heights for main content areas:
/* Bad - breaks on different screen sizes */
.content {
height: 800px;
}
/* Good - grows with content */
.content {
min-height: calc(100vh - 64px);
}
Conclusion
Documentation layouts combine CSS Grid for structure, sticky positioning for sidebars, and IntersectionObserver for scroll tracking. Test at multiple breakpoints and ensure sidebars use align-self: start to enable sticky behavior. Add scroll-padding-top to handle fixed navbar offsets.