How to build an accessible AppShell in React
The hidden nav uses transform/inline-size + inert (never display:none alone), so it stays in the a11y tree and is keyboard-reachable when open while leaving the tab order when closed; Escape and a scrim close the mobile drawer and focus returns to the toggle.
When to use a AppShell
- Building an application screen with a persistent header and side navigation
- You want the header burger to toggle the side nav with an animated, accessible show/hide out of the box
- You need one scroll container (the main area) with a fixed header and full-height nav
When not to use it
- Marketing/document pages where the whole page should scroll — use a plain Header instead
- A single, always-visible sidebar with no toggle — compose SideNav directly
- You need a persisted collapsible sidebar, a top progress bar, or a right aside — that is the richer copy-paste `layout/app-shell` (different props: `sideNav`/`aside`/`persistKey`/`state`), not this drop-in.
Keyboard interactions
Role none, verified at WCAG 2.2-AA.
Escape
Common mistakes
Avoid: Animating the nav collapse with grid-template-columns: 1fr → 0fr on an auto-width sidebar
Prefer: AppShell animates an explicit inline-size (desktop) / transform (mobile drawer)
fr units have no definite size to resolve against on a shrink-to-fit flex item, so the track never animates
Example
<AppShell
header={<ShellHeader brand={{ name: 'Acme' }} />}
nav={<SideNav items={items} />}
>
<h1>Dashboard</h1>
</AppShell>