Layout

App shell and sidebar

The signed-in app frame: a fixed sidebar with tree navigation, a main column, and a drawer with a top bar under 768px.

Examples

A sidebar with a folder, a section and the collapsed state

<AppShell>
  <SidebarOverlay open={mobileOpen} onClose={close} />
  <Sidebar id="app-navigation" aria-label="Primary navigation" open={mobileOpen}>
    <SidebarHeader collapsed={collapsed}>
      <SidebarBrand component={Link} href="/dashboard" tile={<GitHubIcon size={15} />}>Observatory</SidebarBrand>
      <SidebarToggle collapsed={collapsed} onToggle={toggle} />
    </SidebarHeader>
    <SidebarNav>
      <TreeNode component={Link} href="/dashboard" icon={<LayoutDashboard size={18} />} label="Dashboard" active collapsed={collapsed} />
      <TreeGroup>
        <TreeNode component="button" icon={<BarChart3 size={18} />} label="Analytics" chevron="open" onClick={toggleAnalytics} />
        <TreeChildren>
          <TreeNode component={Link} href="/analytics/runs" icon={<Terminal size={16} />} label="Run logs" />
        </TreeChildren>
      </TreeGroup>
      <SidebarSection>Agentic assistance</SidebarSection>
      <TreeNode component="button" icon={<Plus size={16} />} label="New analysis chat" accent onClick={newChat} />
    </SidebarNav>
    <SidebarFooter>…</SidebarFooter>
  </Sidebar>
  <AppContent>
    <MobileHeader title="Observatory" onOpenMenu={open} navId="app-navigation" />
    <AppMain id="main-content">{children}</AppMain>
  </AppContent>
</AppShell>

Guidelines

  • AppShell wraps SidebarOverlay, Sidebar and AppContent; AppContent holds MobileHeader and AppMain. The sidebar's width is --sidebar-width; <html data-sidebar="collapsed"> narrows it, so set that attribute before first paint from the stored preference.
  • A TreeNode is a link (pass component={Link}), a folder toggle (component="button" with chevron) or an action (accent for the one primary action). Collapsed rows show the icon only and the label in a tooltip.
  • Product routes first, then a SidebarSection per secondary group; the author's own links last. The footer holds the account and the sign-in or sign-out control.
  • Under 768px the Sidebar opens as a drawer over SidebarOverlay; the MobileHeader's button opens it and Escape closes it.

Props

PropTypeWhat it does
TreeNode: component, href, icon, label, active, collapsed, tooltip, chevron, trailing, external, disabled, accentsee the typesOne row; a button without an href, a link with one.
Sidebar: open, id, aria-labelboolean, string, stringThe drawer state and the id the MobileHeader points at.
MobileHeader: title, onOpenMenu, menuOpen, navId, trailingReactNode, () => void, boolean, string, ReactNodeThe 56px bar under 768px.
SidebarHeader: collapsed · SidebarToggle: collapsed, onToggle · SidebarBrand: component, href, tile…The header row.