Sidebar

The dashboard's side navigation: the pages in a menu (the app itself keeps the studio and signing out in its header's StudioMenu). shadcn's sidebar as vendored, unrestyled — porcelain beside the white page, the current page on a faint ink wash.

Playground

Okami

Studio Lumen

Home

The page goes here.

Sidebar
Menu button

Held in a frame here with collapsible="none"; the dashboard's sidebar is fixed to the viewport's edge (see Collapsing).

In the dashboard

The labelled pages, opened from okami's logo in a popover on a phone where the shell has no tab bar; the dashboard has one, the TabBar under its composer (from lg the same pages are the NavRail). The nav marks the current page from the path — an exact match for Home at the root, a prefix match for the rest, so a service's own page still lights Services.

Menu button states

At rest it's plain ink; hover and press lay a faint ink wash; isActive holds the wash and sets the label medium; disabled dims it and ignores the pointer.

Collapsing

From md up the sidebar is fixed to the left edge and offcanvas slides it away when closed; below md it becomes a sheet. SidebarTrigger toggles either, and so does ⌘B / Ctrl B. The open state is kept in the sidebar_state cookie. It moves as a panel reveal: out on duration-medium, back on duration-slow, softly blurred, the page following in step; the sheet and its dimmed page do the same.

Home

Collapse the sidebar with the button, or ⌘B / Ctrl B.

<SidebarInset>
  <header className="flex h-14 items-center gap-2 border-b px-4 md:hidden">
    <SidebarTrigger />
    <span className="text-sm font-medium">{venue.name}</span>
  </header>
  {children}
</SidebarInset>

API reference

SidebarProvider

PropTypeDefaultDescription
defaultOpenbooleantrueOpen on first render, uncontrolled.
openboolean—Controls the open state, with onOpenChange.
onOpenChange(open: boolean) => void—Called when the trigger, rail or ⌘B toggles it.

Sidebar

PropTypeDefaultDescription
collapsible"offcanvas" | "icon" | "none""offcanvas"Slide away, shrink to icons, or sit in place. The app's menu sits, in the logo's popover.
side"left" | "right""left"Which edge it's fixed to.
variant"sidebar" | "floating" | "inset""sidebar"Flush with a hairline edge, a floating panel, or inset beside a rounded page.

SidebarMenuButton

PropTypeDefaultDescription
renderReactElement<button>The element to render as — a link for navigation: render={<Link href=… />}.
isActivebooleanfalseThe current page: holds the wash, medium label.
size"default" | "sm" | "lg""default"32, 28 or 48px tall.
variant"default" | "outline""default"Outline adds a hairline edge on a white fill.
tooltipstring | TooltipContent props—Shown beside the icon when collapsed to icons, on desktop.

The rest are structure, passing everything through: SidebarHeader, SidebarContent and SidebarFooter stack in the sidebar (the content scrolls); SidebarGroup pads a section; SidebarMenu and SidebarMenuItem are its <ul> and <li>; SidebarInset is the page's <main>; SidebarTrigger is the icon button that toggles it.

Guidelines

  • The dashboard's one navigation — one flat menu, a handful of pages. Each item is an icon and a one-word noun: “Home”, “Clients”.
  • Items navigate, so each renders a Link through render; exactly one is isActive, set from the path.
  • Icons are Hugeicons, drawn by Glyph and hidden from assistive tech — the label names the page.
  • The wash glides. Each menu draws the ghost Button's wash on two layers behind its items: one follows the pointer to the nearest item (quick, smooth-out; no blink at the gaps), one holds the current page and slides to the next (fast, as tabs slide). Both appear in place first, and with reduced motion they fade rather than travel. A separate SidebarMenu keeps its own.
  • Only the parts the dashboard uses are exported from shared/sidebar. Need a group label or a sub-menu? Re-export it there first, and document it here.