Foundations
Grid and layout
Layout is designed at each breakpoint rather than scaled down from the desktop. What changes between sizes is which parts of the interface are present, not just how wide they are.
BreakpointsPermalink to this section
Six steps, and each one exists because something specific has to change there — not because it is a round number.
| Token | Min width | Device | What changes |
|---|---|---|---|
Base | 0–639px | Phone | Single column. Sidebar in a drawer, table of contents hidden. |
sm | 640px | Large phone | Dialogs become centred panels. Paired short fields sit side by side. |
md | 768px | Tablet | Two-column card grids. The Get started button appears in the navbar. |
lg | 1024px | Small laptop | Sidebar becomes persistent. Section links appear in the navbar. |
xl | 1280px | Desktop | Table of contents appears. The full three-column shell. |
2xl | 1536px | Wide | Content caps at 1440px and centres. Nothing else grows. |
The documentation shellPermalink to this section
Three columns at full width. Each drops out at the point where keeping it would squeeze the content column below a readable measure.
1440px maximum · sidebar 272px · content flexible · contents 224px
Sidebar
272px · lg and up
Content
minmax(0, 1fr), capped at 820px
On this page
224px · xl and up
<div className="mx-auto w-full max-w-[var(--content-max)] px-4 sm:px-6">
<div className="lg:grid lg:grid-cols-[var(--sidebar-width)_minmax(0,1fr)] xl:grid-cols-[var(--sidebar-width)_minmax(0,1fr)_var(--toc-width)]">
{/* minmax(0, 1fr) rather than 1fr — without it, a wide code
block would push the content column past the viewport. */}
<aside className="hidden lg:block">…</aside>
<main id="main-content" tabIndex={-1}>…</main>
<aside className="hidden xl:block">…</aside>
</div>
</div>ContainersPermalink to this section
Four widths cover every page in the product. Content does not grow indefinitely on a wide monitor.
| Container | Max width | Use for |
|---|---|---|
| Shell | 1440px | The outer frame — navbar, docs shell, footer |
| Content | 820px | Documentation body, so the measure stays readable |
| Prose | 68ch | Running paragraphs inside that content |
| Form | 512px | Single-column forms and settings |
Designing for each sizePermalink to this section
The difference between a responsive layout and a shrunk one is whether the smaller version was designed or merely allowed to happen.
Content
Sidebar → drawer below lg
Do — change what is present
The sidebar becomes a drawer rather than a narrow strip. Its purpose survives; its form changes.
Nav
Content
TOC
Don't — squeeze the desktop layout
Three columns at 375px leaves nothing usable. Drop what does not fit instead of compressing everything.
RulesPermalink to this section
The layout decisions that come up in almost every review.
- Use minmax(0, 1fr) rather than 1fr for any grid column that can contain a code block or a table — otherwise the content forces the whole page to scroll horizontally.
- Wide content scrolls inside its own container. The page body never scrolls sideways.
- Sticky regions are offset by --header-height, so nothing hides behind the navbar.
- Independently scrollable regions get their own max-height and the .scroll-region class.
- Forms stay a single column at every width — a two-column form makes the reading order ambiguous.
- Grids go 1 → 2 → 3 columns. Four columns of cards is almost always too many to compare.
- Horizontal padding steps 16px → 24px at sm, and stops there.
The most common layout bug
A grid column defined as 1fr has a minimum size of auto, which means it refuses to shrink below its content. One long code sample then widens the entire page. Use minmax(0, 1fr).