Sidebar · TopBar Composition
The app shell: navigation down the side, context across the top. Both are slot-driven, because every product disagrees about what belongs in its chrome.
Usage
Overview lives here.
<Sidebar sections={sections} collapsed={collapsed} onCollapsedChange={setCollapsed} header={<Logo />} footer={<Avatar name={user.name} />}/>
<TopBar title={<Breadcrumb items={trail} />} center={<Search />} actions={<Actions />} />Submenus
An item with items is a disclosure rather than a destination: it expands in place, its children
indent against a rail, and a child being active keeps the parent open and marked.
Collapsed is still navigable
Collapsing hides the labels visually and keeps them in the accessible name, so a screen reader still reads "Invoices" where a sighted user sees only an icon. Hovering or focusing a rail item opens a flyout carrying that label — and, for a parent, its whole submenu — so a collapsed rail stays readable instead of becoming a column of guesses.
Do
Mark the active item with active — it sets aria-current='page', which is how the current page is announced.
Don't
Put more than two levels of navigation in the sidebar; the third level belongs on the page.
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
| sections | NavSection[] | — | Each has an optional title and its items: label, icon, href or onClick, active, adornment, items. |
| item.items | NavItem[] | — | One level of children. The parent becomes a disclosure, and collapsed it becomes a flyout. |
| header / footer | ReactNode | — | Brand at the top, account at the bottom. Both are yours. |
| collapsed / onCollapsedChange | boolean | fn | — | Controlled. Omit the handler and the toggle button disappears. |
| TopBar title / subtitle | ReactNode | — | A string, or a Breadcrumb trail. |
| TopBar center / actions | ReactNode | — | Search in the middle, controls at the end. |
| TopBar sticky | boolean | true | Sticks to the top of its scroll container. |