Skip to main content

AppShell

Templatebeta@macaw-ds/ui · since 0.1.0

The application frame: skip link, sticky TopBar, full-height SideNav column and the main area. One per app, at the root layout.

When to use

Once, at the root layout of a product. It gives you the skip link, the sticky TopBar, a full-height SideNav column and the main area, with depth by tone already applied.

When not to use

  • Marketing or documentation-only sites that have no primary navigation. You can still use AppShell without sideNav.
  • Inside a page. AppShell is the frame, never a section.

Anatomy

RegionSurfaceBehaviour
TopBarlayer-1Sticky at the top, height size.topbar
Side columnlayer-1Sticky below the TopBar, exactly the remaining viewport height, scrolls on its own
MaincanvasThe window scrolls it; pages start with a PageHeader

Recipe

<AppShell topBar={<TopBar brand={…} actions={…} />} sideNav={<SideNav aria-label="Main">…</SideNav>}>
  <PageHeader title="Invoices" actions={…} tabs={…} />
  <div className="layout-grid px-margin py-section">…</div>
</AppShell>

Density

Set data-density="compact" on html and the TopBar, SideNav items, page margins, gutters and card padding all tighten together.

Accessibility

  • A skip link is the first Tab stop and moves focus to main (WCAG 2.4.1).
  • Landmarks: banner, a named complementary region for the side column, and main.
  • The side column and sticky rows offset by the TopBar height, so focus is never obscured (WCAG 2.2 SC 2.4.11).

Do and don't

Do

  • Set data-density="compact" on html (or the AppShell) for data-dense products; the frame adapts.

Don't

  • Do not add a second scroll container around the page; the window scrolls main.

  • Do not give the main area its own background; it sits on the canvas.

Rules

Hard rules an agent must follow. The reviewer agent and the lint config check the ones that can be checked.

  1. One AppShell per application, at the root layout.
  2. Put a TopBar in topBar and a SideNav in sideNav; do not build the frame by hand.
  3. Start each page with a PageHeader, then the content in a px-margin py-section column.