Skip to content

Sidebar / nav rail

In review
Live · web

Sidebar

dizyx · nockerl-design

Streaming · 2 tools running

nockerl-design · docs site

Designing the sidebar to the token-reactive standard.

Expanded · viewing nockerl-design · docs site. Tab in, arrow-key the rows, expand Tasks, collapse to the rail. The sidebar stays in the layout. The island is live.

// Web: React, consuming @dizyx/nockerl-tokens. (Web is the laggard platform. Spec + live demo,
// not yet exported from @dizyx/nockerl-react; promotion is tracked.)
export function Shell() {
const [section, setSection] = useState('chat');
const [collapsed, setCollapsed] = useState(false);
return (
<div className="layout">
<Sidebar
collapsed={collapsed} // expanded ↔ icon-only rail
onToggleCollapse={() => setCollapsed((c) => !c)}
selected={section}
onSelect={setSection}
sections={[
{ label: 'Workspace', items: [
{ id: 'home', label: 'Home', icon: <HomeIcon /> },
{ id: 'chat', label: 'Chat', icon: <ChatIcon />, dot: 'streaming' },
{ id: 'tasks', label: 'Tasks', icon: <TasksIcon />, badge: { count: 3, tone: 'attention' },
children: [{ id: 'tasks-mine', label: 'Assigned to me' }, { id: 'tasks-review', label: 'In review' }] },
{ id: 'files', label: 'Files', icon: <FilesIcon />, badge: { count: 12 } },
] },
]}
footer={<ProfileMenu name="Ada Lovelace" onSettings={openSettings} />}
/>
<main>{/* the content region; the sidebar stays beside it, never over it */}</main>
</div>
);
}
PropTypeDefaultDescription
sections *NavSection[]Grouped nav: { label, items }. Each item is { id, label, icon, badge?, dot?, children? } and renders as one focusable button.
selected *stringActive destination id (a leaf, meaning a child id when a sub-item is active). Drives the cyan indicator + which content shows.
onSelect *(id: string) => voidFired when a (leaf) item is activated: click / Enter / Space.
collapsedbooleanfalseCollapses the sidebar to the icon-only rail; labels + counts hide and each item exposes its label via tooltip / aria-label.
onToggleCollapse() => voidToggles collapsed from the rail button.
badge{ count: number; tone?: 'neutral' | 'attention' }Per-item trailing count. neutral = mono chip; attention = warm count (status signal). Right-aligned; collapses to a corner dot on the rail.
childrenNavSubItem[]Nested sub-items on an item, which turn its row into an expandable disclosure (aria-expanded), indented under the parent.
footerReactNodePinned to the bottom: profile (avatar + name) + a settings cog (its own focusable target).