Sidebar
Compose a non-modal navigation panel from UI SDK core behaviors.
Composition
import { Sidebar, createSidebarController } from "@lenso/primitives/sidebar";
import { useState } from "react";
export function Navigation() {
const [controller] = useState(createSidebarController);
return (
<>
<button type="button" onClick={() => controller.setOpen(true)}>Open navigation</button>
<Sidebar.Root aria-label="Workspace navigation" controller={controller} defaultOpen>
<Sidebar.Trigger>Toggle navigation</Sidebar.Trigger>
<Sidebar.Panel aria-label="Workspace destinations">
<Sidebar.Header>Workspace</Sidebar.Header>
<Sidebar.Content>
<Sidebar.Menu><Sidebar.MenuItem>
<Sidebar.Item href="/" selected>Home</Sidebar.Item>
</Sidebar.MenuItem></Sidebar.Menu>
</Sidebar.Content>
<Sidebar.Footer>Account settings</Sidebar.Footer>
</Sidebar.Panel>
</Sidebar.Root>
</>
);
}The framework-independent definition lives in @lenso/primitives/sidebar/core.
It uses definePrimitive, createChange, bindPanel and createFocusRecovery.
The React entry only maps its slots to native elements with createParts.
open, defaultOpen (true by default) and onOpenChange(open, details) follow
native SDK controlled-state and cancellation contracts. details exposes
reason, event, source and cancel(). Reasons are trigger-press,
escape-key and controller. A controlled owner must commit the requested state.
Each root has an independent controller. Use createSidebarController() for
external operations: isOpen(), setOpen(open) and toggle(). Group roots with
native HTML. The former Group registry, Provider, targetId and separate direction
model are removed. dir inherits through the DOM.
Root, Trigger, Rail and Panel expose data-open, data-closed, data-side,
and render state { open, side }. Root's native slot is sidebar; structural
slots are prefixed sidebar-. Item renders an anchor, accepts selected and
nested, and exposes aria-current, data-selected and data-nested. Name a
Rail through its children or aria-label; it has no built-in English labels.
Toggle buttons use native Enter/Space activation. Opening retains focus; Tab uses browser navigation order. Escape inside a panel requests closing unless a descendant prevented it. SDK panel binding restores focus on a committed close; SDK focus recovery handles removal of a focused panel. Cancellation or a rejected controlled request leaves focus and visibility intact. There is no modal trap, outside-press dismissal or mobile drawer. Compose SDK Drawer for a modal surface.
The styled @lenso/ui/sidebar is navigation presentation for use inside a panel;
it has its own composition and is not a visual alias for this panel primitive.