Responsive

Decide what each region does as width changes: divide, reveal, resize, or swap.

Breakpoints

Decide what each region does as width changes. Write the responsive contract down for every region before you call the layout done.

GuidancePractices
Do

Write the contract down for every region before you call the layout done

Do

Decide per region whether it is revealed, resized, or swapped at each width

Do

Drop a region rather than let it compete for width it does not have

Don't

Hold three regions at a width where none of them has usable space

Don't

Shrink every region uniformly instead of swapping or dropping one

Don't

Wire a breakpoint in CSS that the contract comment never mentions

Responsive contract

Lock what each region does as width changes, and pair every line of the contract with the prop or hook that enforces it.

  • Divide: how many regions survive at each width
  • Reveal: which regions earn their width only when there is room, and open on demand below that
  • Resize: content flexes while fixed regions hold their budgets, and text stays capped by contentWidth so line length holds
  • Swap: navigation becomes MobileNav at the AppShell mobileNav breakpoint; the side panel becomes a Dialog or BottomSheet, driven by useMediaQuery
Contract wired to props
tsx
// Recommended thresholds: 3 regions above 1024, 2 from 768,
// 1 below. Text reads best at 40–60 characters per line.
// >1024 SideNav 256 | content | side panel 380
// <=1024 panel moves to a Dialog (useMediaQuery)
// <=768 nav collapses to MobileNav (mobileNav "md")
const isNarrow = useMediaQuery('(max-width: 1024px)');
​
<AppShell sideNav={<SideNav />} mobileNav={{breakpoint: 'md'}}>
<Layout
content={<LayoutContent>{/* rows */}</LayoutContent>}
end={isNarrow ? undefined : <LayoutPanel width={380} hasDivider />}
/>
</AppShell>

Verify: every contract line names a mechanism, so the comment cannot drift from the behavior.