Skip to content
Kappa
Sidebar
@dicehub/kappav0.5.0

Sidebar

Compact application navigation with resizing, peeking, sliding views, and an accessible mobile drawer.

Installation

Follow the styles and theme setup once in your application. These examples use @lucide/vue for icons. Install it, or use your own Vue icons.

pnpm add @lucide/vue

Barrel

import { Sidebar } from "@dicehub/kappa";

Granular

import { Sidebar } from "@dicehub/kappa/components/sidebar";

Usage

Copy a snippet into a Vue file. The examples use SidebarLayout for the layout, header toggle, mobile Close button, and resize handle. Mount it in an application container with a set height so the navigation can scroll.

For complete application layouts, see Blocks → Sidebar: workspace, icon rail, inset, floating, two-column navigation, and top-header examples.

Put one Root and the application content inside a Provider. Give the layout a height, then let Content scroll between Header and Footer. Keep one Trigger in the application header so the mobile drawer and offcanvas Sidebar can be reopened. A second footer toggle is not needed. These demos align both desktop headers at 4rem. Include Close in the navigation header for mobile.

Use MenuItem around every top-level entry and MenuSubItem around nested entries. These explicit list items keep navigation semantics predictable when you compose a Collapsible.

Use icons for every top-level item when collapsible="icon". Labels remain accessible when visually hidden; tooltip adds a visible label on hover or focus. Custom header and footer content must also fit the rail. Wrap text in MenuLabel, or use Context to change the composition.

For hover reveal, use collapsible="offcanvas" and peekable. Add peek to the external header Trigger. Hover reveals a temporary panel; click or Enter pins it open. Set --kappa-sidebar-peek-top to your header height plus the panel gap, --kappa-sidebar-peek-gap to that gap, and --kappa-sidebar-peek-bottom to the bottom margin. Offsets are relative to the desktop Sidebar shell. The defaults are 0.75rem. Keep the trigger above the panel, next to the same logical edge.

Composition

Typical part hierarchy
Sidebar.Provider
├── Sidebar.Root <nav> / Ark Drawer on mobile
│   ├── Sidebar.Header
│   │   ├── Dropdown + MenuButton (namespace, optional)
│   │   └── Sidebar.Close (mobile)
│   ├── Sidebar.Content
│   │   └── Sidebar.Group
│   │       ├── Sidebar.GroupLabel
│   │       └── Sidebar.Menu <ul>
│   │           └── Sidebar.MenuItem <li>
│   │               ├── Sidebar.MenuButton
│   │               └── Sidebar.Collapsible (optional)
│   │                   ├── Sidebar.CollapsibleTrigger
│   │                   └── Sidebar.CollapsibleContent
│   │                       └── Sidebar.MenuSub <ul>
│   ├── Sidebar.Loading (alternative to Content)
│   ├── Sidebar.SlidingViews (optional)
│   │   └── Sidebar.SlidingView → Sidebar.Content
│   ├── Sidebar.Footer
│   │   ├── Dropdown + MenuButton (profile, optional)
│   │   └── Sidebar.Trigger
│   └── Sidebar.ResizeHandle (optional, desktop)
└── application content
    └── Sidebar.Trigger

Use the primitive parts when you want to supply your own application layout.

Sidebar and Sidebar.Root are the same navigation component. Provider supplies the state and flex layout; it does not provide an application header, router, or store.

Kappa owns desktop state, navigation markup, dimensions, and semantic-token styling. Ark owns mobile modality, scroll locking, focus trapping, Escape/outside dismissal, and section accessibility. Mobile panels teleport to body. Global Kappa theme tokens must reach the teleport target.

Resizing adapts Ark Splitter's two-panel size model to a pixel-width Sidebar and normal flex content. No extra application wrapper is required. The separator controls the navigation landmark. Ark's collapse threshold and keyboard behavior are preserved.

Set :collapse-on-resize="false" to stop resizing at minWidth. The separator is hidden while collapsed. The header Trigger can still hide the sidebar.

Start dragging on the divider or a few pixels into the content beside it. The visible line stays on the sidebar border.

The resize scope uses physical left/right panel order, including RTL. This avoids an installed Ark Splitter inconsistency between RTL pointer and keyboard direction without replacing its interaction code. Enter targets the navigation panel through Ark's collapse/expand API when that panel follows the content. Navigation retains the application locale.

Namespace, profile, and Quick search examples compose Dropdown and CommandPalette. They are not application-specific Sidebar parts. The scroll-to-item example uses native scrolling scoped to Content; it does not add a Sidebar scrolling API or move the outer page.

Inside the mobile drawer, keep Dropdown.Content in the drawer with :teleport="false" and fixed positioning. This keeps popup focus inside the modal boundary. Desktop popups can teleport to body.

Offcanvas hover panels stay within the Provider's inline bounds. Teleport desktop menus to body so they can extend beyond those bounds.

Nested Sections

Icon collapse hides nested content without resetting each section's open preference. Activating a collapsed section requests expansion of the Sidebar and section. Controlled parents must accept both updates when both levels are controlled.

Examples

Each snippet is a complete Vue file for the named feature. Previews include additional menus and controls.

Namespace Selector

Current workspace details, account actions, and a workspace list with the selected checkmark on the right. Ctrl/⌘ + 1–3 works only while the menu is open. Mobile menus stay inside the drawer.

Profile Selector

A footer menu with initials, name, email, profile switching, and account actions. All data is synthetic. Actions update this example only.

Quick search opens a real CommandPalette. Type a page name, use the arrow keys, then press Enter to navigate. Escape returns focus to the search button. No global shortcut is registered.

Resizable

Drag the separator to change the width. Drag toward the rail to collapse; drag outward to expand. Arrow keys resize, Shift increases the step, Home/End move to the edge limits, and Enter toggles collapse. Ark owns the drag threshold between the minimum width and the rail. The handle is desktop-only.

Controlled Width

Keep the expanded width in application state. Lock width to reject resize requests. Collapse and mobile state remain separate.

Peeking

Hover or focus the collapsed rail to peek. The live state distinguishes a temporary peek from a pinned sidebar. Pin it open, then collapse it to try again. Namespace and profile menus keep the peek visible. Escape or leaving both the Sidebar and its popup closes it without moving the page.

Hover Reveal

Collapse the sidebar, then hover over the header toggle. Click to pin it open. Drag the pinned divider to resize within the width limits. Use Home, AI Chat, Projects, or Inbox to change the navigation below. Every context label appears as soon as the full row fits. Otherwise, only the selected label is shown. Context changes keep the current page and group state. Hover over the workspace name to show its menu icon. Its background stays highlighted while the menu is open. Use the search icon or press / to find a page across contexts. The shortcut works in the full example, or while focus is inside this preview. Escape closes menus and search before dismissing the panel. On mobile, the toggle opens a drawer.

The snippet adds hover reveal, pinning, and resizing. Use WorkspaceSwitcher, Tabs, and CommandPalette to add the preview's other controls.

The preview leaves room for its content when resizing. Open the full-screen example to try screen-centered content.

Scroll to Item

Jump to an item in a long navigation list without moving the documentation page or keyboard focus. Keep Settings visible does nothing when that row is already fully visible. This composition uses native Content scrolling; reduced motion disables smooth scrolling.

The snippet scrolls the navigation to Settings instantly. Its handler changes only the Sidebar's scroll position.

Sliding Views

Use the navigation header or the page button to switch between workspace and project views. You can also open Rotor study from the list and go back. The active view is shown beside the navigation. Inactive views stay mounted, hidden, and inert. Motion respects reduced-motion settings.

Compact

28px desktop rows for dense navigation. Mobile rows keep 44px touch targets.

Icon Collapse

Start with an icon rail. Tooltips appear on hover and keyboard focus. Activating a nested section expands the rail.

Offcanvas

Hide the entire desktop Sidebar. Keep a Trigger outside it so users can reopen it.

Non-collapsible

Keep desktop navigation expanded. Small viewports still use the accessible mobile drawer.

Controlled State

Control desktop and mobile state separately. Lock state to test a parent that rejects a desktop change request.

End Placement

Logical end placement follows the text direction. Drag the separator left to grow this Sidebar. Home grows it; End collapses it, following Ark panel order.

Right-to-left

Use DirectionProvider for Ark behavior and dir for text and layout. Start becomes the right edge.

Mobile Drawer

Open the drawer inside this resizable viewport. It starts at the preview's left edge, not at the edge of the documentation page. Tab stays inside the drawer. Escape, Close, and the backdrop dismiss it. Open example tests the same composition in a full browser tab.

The snippet uses the normal mobile breakpoint. Resize your browser below 768 px to open the drawer.

Full-screen Mobile

Resize the viewport, then open the full-screen navigation. The drawer fills only that viewport, so the documentation stays visible. Try namespace and profile menus, Quick search, and a nested page such as Refinement. Its breadcrumb remains visible after the drawer closes.

The snippet uses the normal mobile breakpoint. Resize your browser below 768 px to open the drawer.

Long Navigation

Header and Footer stay visible while Content scrolls. Long labels do not change the width.

Loading

Deterministic placeholder rows with an accessible loading status. Motion stops under reduced-motion preferences.

Routing and State

Supply href for normal links, or compose a router link with as-child. The library does not read the URL or select routes. Set active from your route state. For custom children, put the icon and MenuLabel inside the link.

MenuButton does not close the mobile drawer automatically. Call setMobileOpen(false) after your router accepts the navigation. This keeps rejected and asynchronous navigation under application control.

Provider supports v-model:open and v-model:mobile-open. Uncontrolled defaults are read once. Leaving mobile mode requests that mobile state close; the desktop preference is unchanged. Controlled parents must handle update events. No cookies, storage, global keyboard shortcuts, or application state are added.

SSR renders the desktop structure, then checks the viewport after hydration. Navigation content remounts when switching between desktop and mobile. Keep persistent form state and controlled section state outside Root when they must survive that transition.

Accessibility

  • Root is a named navigation landmark, not an ARIA menu. Use Tab and Shift+Tab to move between links; Enter activates links, and Enter or Space activates buttons and disclosures.
  • Active entries use aria-current="page". Disabled entries cannot navigate or activate and leave the tab order.
  • Mobile uses a named modal dialog. Focus stays inside while open. Escape, Close, and backdrop interaction dismiss it; focus returns to the opening Trigger.
  • Use a visible Close in mobile headers. Do not rely only on the backdrop.
  • Collapsed offcanvas content is hidden and inert. A temporary hover panel exposes its links. Icon labels remain in the accessibility tree; nested hidden links leave the tab order.
  • ResizeHandle is a focusable vertical separator. Arrow keys, Shift+Arrow, Home, End, and Enter use Ark's resize behavior. End placement reverses which edge grows the Sidebar. The handle is not rendered on mobile.
  • Peeking keeps the desktop layout width fixed. Escape dismisses it without moving the pointer. It closes when pointer and focus leave and its popups are closed. Popups linked through aria-controls keep the panel available; their first Escape closes the popup.
  • Sliding views keep inactive content mounted, hidden, and inert. In-view navigation transfers focus; an external view change does not take focus from its external control.
  • Content is a keyboard-focusable scrolling region. Use a descriptive aria-label if its default is not suitable.
  • Pass translated Root, Trigger, Close, and Loading labels. Pair DirectionProvider with a DOM dir attribute for RTL.
  • Motion respects reduced-motion preferences. Mobile navigation controls keep a 44px minimum height, including compact mode.

API Reference

Sidebar.Provider

PropTypeDefaultDescription
open / defaultOpenbooleanundefined / trueControlled or initial desktop expanded state.
mobileOpen / defaultMobileOpenbooleanundefined / falseIndependent controlled or initial mobile drawer state.
collapsible"icon" | "offcanvas" | "none""icon"Desktop collapse mode. none ignores desktop collapse requests.
side"start" | "end""start"Logical edge. End placement orders the desktop Sidebar after the application content.
compactbooleanfalse28px instead of 32px minimum desktop rows. Mobile remains 44px.
mobileBreakpointnumber768Viewport widths below this many pixels use a drawer. Zero disables mobile mode.
width / collapsedWidth / mobileWidthCSS length"16.25rem" / "3.25rem" / "18rem"Expanded desktop, icon rail, and mobile widths. Mobile width leaves at least 3rem for the backdrop.
resizablebooleanfalseEnable desktop resizing through ResizeHandle. The numeric resize width replaces the fixed width prop.
collapseOnResizebooleantrueAllow the separator to collapse navigation. Set false to stop at minWidth and hide the separator during collapse. Sidebar.Trigger can still hide the sidebar.
resizeWidth / defaultWidthnumber (px)undefined / 260Controlled or initial expanded resize width. Resize emits requests; a controlled parent can reject them.
minWidth / maxWidthnumber (px)180 / 400Expanded resize bounds. A collapsed icon rail can be smaller than minWidth.
peekablebooleanfalseTemporarily reveal a collapsed icon rail on hover/focus, or offcanvas navigation through a peek Trigger. Desktop only; ignored in none mode.
idstringVue useId()Stable ID prefix for navigation and dialog relationships. Set IDs here, not on Root.

Parts

PartPropsDefaultDescription
Rootlabel"Main navigation"Accessible navigation and mobile dialog name. Give multiple Sidebars distinct names.
RootfullScreenOnMobilefalseMobile dialog covers the viewport.
TriggerexpandLabel / collapseLabel / openLabel / closeLabelEnglish action labelsOptional localized labels for the built-in icon. Custom content must provide its own accessible name.
Triggerdisabled / asChildfalseDisable the action or compose it with a custom button.
TriggerpeekfalseHover reveals peekable offcanvas navigation. Click or Enter pins it open. aria-expanded includes temporary visibility.
Closelabel / asChild"Close sidebar" / falseMobile-only close action. Hidden on desktop.
MenuButtonhref / asChildundefined / falseA native link when href is set; otherwise a button. asChild accepts a router link or native element.
MenuButtonactive / disabledfalseCurrent-page styling and aria-current; disabled controls block activation and leave the tab order.
MenuButtonicon / tooltipundefinedVue icon component (or #icon slot), and optional collapsed-rail tooltip.
Collapsibleopen / defaultOpen / disabled / idundefined / false / false / generatedArk disclosure behavior with controlled and uncontrolled state. Sections retain their open preference during icon collapse.
CollapsibleTrigger / CollapsibleContentasChildfalseCompose Ark parts with Sidebar.MenuButton or a custom element.
Loadingrows / label5 / "Loading navigation"1–20 placeholder rows and a localized accessible status name.
ResizeHandlelabel / disabled"Resize sidebar" / falseFocus-visible desktop separator. Ark handles pointer and keyboard resizing. In offcanvas mode, reopen with an external Trigger.
SlidingViewsactiveKey / directionrequired / "left"Controlled active view key and physical transition direction (left or right). Reverse direction for back navigation.
SlidingViewvalue / labelrequired / valueMatching view key and accessible group name. Inactive views stay mounted but are hidden and inert.
Structural partsasChildfalseHeader, Footer, Content, Group, GroupLabel, Menu, MenuItem, MenuSub, MenuSubItem, MenuLabel, MenuBadge, and Separator forward attributes to their semantic element.

Events and Context

Provider emits update:open(boolean), openChange({ open }), update:mobileOpen(boolean), and mobileOpenChange({ open }). Collapsible emits update:open and openChange. Controlled components emit requests without changing the supplied value.

Resizing emits update:resizeWidth(number), resize({ width }), and resizeEnd({ width }). Widths are in pixels. The expanded width is retained during collapse and mobile mode. Use v-model:resize-width to control it.

Sidebar.Context exposes unwrapped open, mobileOpen, isMobile, state, iconCollapsed, side, compact, and collapsible, plus setOpen, setMobileOpen, and toggle. useSidebarContext() exposes the same values as Vue refs in a descendant setup function.

Context also exposes resizable, width, isResizing, setWidth, peekable, and isPeeking. State can be expanded, collapsed, or peeking. Peeking never emits a desktop open change.

Use focusTrigger() to return focus to a visible Sidebar Trigger.

A peek Trigger reports visible hover navigation through aria-expanded. Its click toggles persistent open. Keyboard focus alone does not reveal offcanvas navigation. Escape returns focus to a visible Trigger when focus was inside the panel; page focus stays in place during pointer-only use.

Styling and Exports

Structural parts forward attributes, classes, styles, and listeners. Root and Provider use reserved generated IDs; supply the Provider id prop to customize their relationships. Root exposes data-state, data-side, data-collapsible, data-mobile, and data-compact. MenuButton exposes data-active and data-disabled; Ark parts retain their state attributes.

Colors inherit Kappa control, default, subtle, line, tint, overlay, and focus tokens. Dimensions use --kappa-sidebar-width, --kappa-sidebar-collapsed-width, and --kappa-sidebar-mobile-width; prefer the matching Provider props so teleported mobile content receives them too.

Every compound part has a named export, such as SidebarProvider, SidebarRoot, and SidebarMenuButton. Public props, slots, state, events, SidebarContextValue, and SIDEBAR_DEFAULTS are exported from the same subpath.