GitHub

App shell

Top app bar, collapsible sidebar navigation, and a scrolling content region — the chrome every prototype app starts from. esa-app-bar sets the brand color context, esa-sidebar-nav carries the navigation as data, and everything below the chrome is just page content.

When to use: Starting any multi-page prototype app. Stand the shell up first — every other pattern assumes this frame around it.

Live demo

The miniature below is Estuary, an invented site-and-permit monitoring app for an environmental consulting team. It is the real composition, just framed at a fixed height: use the chevrons at the top of the sidebar to collapse it to an icon rail, expand Reports to see accordion children, and scroll the content region — the chrome never moves.

Estuary

Dashboard

Active permits

Across 14 monitored sites

  • Shoreline stabilization — Carquinez Strait Active
  • Eelgrass restoration — Tomales Bay Renewal due
  • Maintenance dredging — Mare Island In review

Upcoming field visits

Next two weeks

  • Jun 16 Sediment sampling — Suisun Marsh
  • Jun 19 Avian nesting survey — Hamilton Wetlands
  • Jun 24 Water quality — Petaluma River mouth

Composition notes

  • Two flex passes make the frame. A column lays the bar over the body; a row lays the nav beside the content. Only .shell-content scrolls (flex: 1 + min-height: 0 + overflow-y: auto) — in a spoke the shell runs height: 100vh; here it's 440px to frame the miniature.
  • The bar brands itself from tokens. tone="brand" sets background and currentColor from --color-primary / --color-text-inverse; the wordmark, icon buttons, and user menu trigger all inherit. A spoke re-brands the whole bar by re-pointing --color-primary in its theme file — never by touching the component.
  • The sidebar re-skins through its component tokens. --sidebar-width, --sidenav-bg, --sidenav-link-text-active, and friends are set on the shell wrapper and inherit through the shadow boundary — that's exactly how this demo narrows the rail to 248px.
  • Navigation is data, not markup. esa-sidebar-nav takes items as a JS array property (not an attribute): groups, badges, accordion children, and the active flag all live in the array, so a spoke swaps its whole nav by swapping data.
  • Page content owns everything below the chrome. The scrolling region is plain composition space — here a page head plus esa-card / esa-badge legos. The shell makes no assumptions about what goes inside it.