Sidebar
The persistent navigation panel used in this docs shell. Renders a collapsible nav tree from the navigation.ts data source, with a search trigger and mobile drawer behaviour.
Anatomy
| Zone | Contents |
|---|---|
| Logo header | Sitka wordmark + version badge. Height matches --header-height (52px). |
| Search trigger | Ghost button that calls onSearchOpen, which opens CommandPalette. Shows ⌘K shortcut. |
| Nav tree | Scrollable list of NavSection → NavGroup → NavItem. Groups are collapsible with ChevronRight indicators. |
| Footer | Branding strip at the bottom of the sidebar. |
Data source
The nav tree is driven by the navigation export from @/lib/navigation.ts. The hierarchy is:
| Type | Description | Required fields |
|---|---|---|
| NavSection | Top-level category label | title |
| NavGroup | Collapsible sub-group within a section | title, items |
| NavItem | Individual nav link | title, href |
NavItem also accepts an optional badge string (e.g. “New”, “Gold Standard”) and an external boolean.
Layout tokens
| Token | Value | Description |
|---|---|---|
| --sidebar-width | 220px | Fixed width of the sidebar panel |
| --header-height | 52px | Height of the logo row, shared with the top header |
Mobile behaviour
- →On screens narrower than md (768px) the sidebar is off-screen (translateX(-100%)) by default.
- →It slides in via CSS transition when isOpen is true, overlaying the content with a semi-transparent backdrop.
- →The backdrop click calls onClose; a close button (X icon) is rendered inside the sidebar header on mobile.
- →On desktop (md+) the sidebar is always visible and the isOpen prop has no effect.
Implementation
Sidebar.tsx
tsx
"use client";
// The Sidebar is used in the docs shell layout at
// src/components/layout/Sidebar.tsx
//
// It reads from src/lib/navigation.ts to render the nav tree.
// NavSection → NavGroup → NavItem hierarchy.
//
// Integrate it in your layout:
import { Sidebar } from "@/site/layout/Sidebar";
export default function DocsLayout({ children }: { children: React.ReactNode }) {
const [menuOpen, setMenuOpen] = useState(false);
const [searchOpen, setSearchOpen] = useState(false);
return (
<div style={{ display: "grid", gridTemplateColumns: "var(--sidebar-width) 1fr" }}>
<Sidebar
isOpen={menuOpen}
onClose={() => setMenuOpen(false)}
onSearchOpen={() => setSearchOpen(true)}
/>
<main>{children}</main>
</div>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen | boolean | — | Whether the sidebar is visible on mobile. On desktop (md+) the sidebar is always visible. |
onClose | () => void | — | Called when the mobile backdrop is clicked or the close button is pressed. |
onSearchOpen | () => void | — | Called when the search trigger inside the sidebar is clicked. |
Accessibility
- →The sidebar renders as <aside> — a complementary landmark, available in screen reader quick-navigation shortcuts.
- →Navigation links are wrapped in <nav>, making them another accessible landmark.
- →Group toggle buttons use aria-expanded to communicate collapsed/expanded state.
- →Active links receive aria-current="page" via the data-active class pattern (handled by NavLinkItem).
- →The mobile close button has aria-label="Close navigation".