Skip to content

Sidebar ​

A collapsible side bar: on desktop, collapsed narrows it to an icon strip; on mobile (narrower than mobile-breakpoint, default 768px) it automatically becomes an overlay drawer with a backdrop. Clicking outside, the close button or Esc collapses it.

Basic usage ​

Pass menu JSON via the items attribute ([{label, value, icon?}]); the default slot holds custom content.

Default sidebar

Custom content area
Right content area

Collapsing ​

Clicking the bottom「«」button toggles collapsed (a controlled attribute; fires oas-collapse). In the collapsed state only icons are shown, and menu items without an icon are hidden automatically. hide-toggle hides the button (host opt-out, e.g. for static sidebars).

Nested submenus use a flyout panel in the collapsed state: a tree parent renders as an icon item; clicking (or hovering with a short delay) opens a submenu panel anchored to the item's inline-end (mirrored to the left in RTL) with collision avoidance. Clicking a leaf fires oas-select and closes the panel; nested subtrees expand inline inside the panel; Esc closes and refocuses the parent; clicking outside closes; only one flyout is open at a time. Note (contract change): clicking a collapsed-state parent only opens the flyout and no longer fires oas-select for the parent itself (a parent is not a navigable page) — if you previously relied on the parent's oas-select in the collapsed state, listen for oas-select from the flyout's leaf items instead.

hide-toggle (no collapse button, static sidebar)

Static sidebar: no「«」collapse button at the bottom (compare with the "Collapsing" demo).

collapsed icon state

Toggle collapsed
Right content area

Mobile drawer ​

On narrow screens the sidebar automatically becomes an overlay drawer: the floating ☰ button at the top-left opens it, and the backdrop / close button / Esc collapses it. Tune the breakpoint with mobile-breakpoint (this demo uses 700px so it can be observed on a wide screen).

Mobile drawer (breakpoint 700)

Shrink the window / preview container below 700px, then click the ☰ at the top-left to open the drawer

Controlled mobile drawer ​

drawer-open is a controlled attribute: setting/removing it externally opens/closes the mobile drawer (no need to click the floating ☰). This demo raises the breakpoint to force the mobile form so it can be observed on a wide screen.

Controlled drawer-open

Open drawer (set drawer-open)Close drawer (remove drawer-open)
External buttons set drawer-open to control the drawer; after it is closed via the mask / ✕ / Esc, the attribute is removed.

Controlled highlight ​

active is a controlled attribute: setting/removing it externally migrates the currently highlighted item (no click needed).

Controlled active

Highlight DashboardHighlight SettingsClear highlight
External buttons set active to control the current highlighted item; clearing it leaves no item highlighted.

Groups ​

Menu items support an optional group field: consecutive items in the same group get a group title rendered before the group's first item (display-only, non-clickable). Group titles are hidden in the collapsed icon strip and shown in the mobile drawer.

Groups (items.group)

“Overview” and “Business” are group titles; “Profile” has no `group` field and renders flat.

Nested submenus ​

Items support children nesting: a parent item toggles expansion on click (without firing oas-select); a parent containing the active child auto-expands. Subtrees are hidden in the collapsed icon strip. Expand/collapse runs a smooth height animation (degrades automatically under prefers-reduced-motion); accordion makes siblings mutually exclusive — expanding one parent collapses the others at the same level (great for long navs that keep only one section open).

Nesting depth: mechanically unlimited (recursive rendering; level 3 verified — per-level expansion, active-cascade auto-expansion and child-selected all work). But each level adds ~28px of indentation, so keep it to ≤3 levels in a 220px sidebar — use oas-tree for deeper trees (built for arbitrary depth: connecting lines / virtual scroll).

Three-level nesting (Business → Users → User groups)

The "Business → Users → User groups" three-level chain: with `active` pointing at the third level ("Admins"), both ancestors auto-expand with child-selected indication; indentation and guide lines stack per level.

Accordion (siblings mutually exclusive)

Click "Business", "Marketing", "Settings" in turn — only one section stays open at a time.
Each click also demos the smooth expand/collapse animation (grid height transition + chevron rotation; degrades automatically under `prefers-reduced-motion`).

Nested submenus (items.children)

“Business” auto-expands because it contains the active child (Users); clicking a parent only toggles expansion, clicking a child fires selection.

Badges and item actions ​

Items support badge count badges (colors via --oas-sidebar-badge-bg/-color variables) and actions hover action buttons (clicking fires oas-action without selecting).

Badges and item actions

Hover “Projects” to reveal action buttons; clicking fires an oas-action event.

No action yet

Custom icons and coloring ​

Item icon defaults to the built-in @oas-ui/icons registry; app-level custom icons go through the official registerIcon(name, svg) path (exported by @oas-ui/ui) — register once, and <oas-icon>, the sidebar and any future consumers all see it (do not mutate the built-in icon data; on-demand mode using built-in names needs a one-time import '@oas-ui/icons/register'); a same-name registration overrides the built-in icon.

iconColor sets a per-item target color: when present it is fixed, taking precedence over the default disabled/active-state colors; when absent it falls back to currentColor and follows the state (active uses the primary color). Registered colored SVGs (paths with their own stroke/fill) keep their own color naturally — the outer stroke never overrides them.

Custom icons & coloring (registerIcon + iconColor)

“Workbench” is fixed to the warning color and “Repositories” to the theme primary (iconColor present → does not follow the active state); “Team feed” has no iconColor so it follows the state, and its custom SVG keeps its own red stroke. Built-in icons take iconColor too: “Built-in star” uses the registry name star + a forced red (the built-in monochrome path’s currentColor is also replaced); “Built-in heart” without one follows the state. The tooltip icon in the collapsed strip shares the same color.

Dividers and skeleton ​

A {type:"divider"} entry renders a divider line; the loading attribute shows a pulsing skeleton (the value sets the row count, default 4).

Divider + loading

Toggle loading
Click the button to toggle the skeleton.

Shortcut and keyboard navigation ​

The shortcut attribute enables Ctrl/Cmd+B collapse toggling (off by default to avoid hijacking a global key); the menu supports ↑/↓ focus movement, Home/End jumps, and Enter/Space activation.

Shortcut + keyboard navigation

Press Ctrl+B to collapse/expand; focus the menu and navigate with arrow keys.

Hover expansion (expand-on-hover) ​

The expand-on-hover attribute temporarily expands the collapsed icon strip on hover (pure visual state — does not change the controlled collapsed attribute).

expand-on-hover

Collapse to icon strip first
Hover the icon strip to temporarily expand and reveal labels; moving away collapses it again.

Variants ​

The variant attribute: sidebar (default, flush) / floating (margin + radius + shadow) / inset (margin + radius + background contrast).

variant: floating / inset

floating
inset

Right side and dual sidebars ​

side="right": the mobile drawer slides in from the right with the trigger on the right; multiple sidebars can coexist (one on each side, with independent state).

side=right + dual sidebars

“Main nav” on the left and “Auxiliary panel” (side=right) on the right — each opens/closes independently (this demo forces the mobile state for observation).

Resizable width (resizable) ​

The resizable attribute shows a drag rail on the host edge for real-time resizing (writes back to the width attribute); resize-min/resize-max clamp the range (default 160–480); the rail supports arrow keys (±8px, Home/End jumps to min/max); oas-resize fires on drag end or key adjustments. Desktop non-collapsed only (hidden when collapsed or on mobile).

resizable edge drag-resize

Drag the rail on the sidebar's right edge to resize in real time; focus the rail and use arrow keys too. Current width 220px

Resizable width (oas-splitter) ​

For resizable width, compose with oas-splitter instead of a built-in rail: place the sidebar in the left pane and drag the splitter handle to resize.

oas-splitter + sidebar

Drag the splitter handle to resize the sidebar (it fills the left pane with `width="100%"`, tracking the pane width in real time).

Custom width ​

The width attribute overrides the expanded width (defaults to the --oas-sidebar-width token); the collapsed state still narrows to an icon strip.

width attribute

180px280pxDefault token
Click the buttons above to switch the expanded width and watch the sidebar change.

Combining with oas-layout ​

oas-sidebar can be used directly as the sider of oas-layout (slot="sider").

sidebar as the sider

Header areaSide content

Main content area.

flex 1
Footer info

Declarative child channel ​

Besides the items JSON, items can be declared with <oas-sidebar-item> / <oas-sidebar-divider> child elements (the items attribute takes priority when explicitly set; otherwise children are parsed and converge to the same rendering path). The default slot text becomes the label; attributes map to the SidebarItem fields: value / icon / group / badge (all-digit strings convert to number, otherwise stay string); nesting child elements directly inside <oas-sidebar-item> recursively becomes the nested children (dividers allowed). Child additions/removals, attribute and text changes re-render automatically (MutationObserver). Boundary: actions (hover action buttons) are an object array that does not fit scalar attribute mapping — use the items JSON when you need them.

Declarative children (group / divider / nested / badge)

DashboardLive TrendsBusiness OrdersUsersInbox
“Overview” is a group title; divider, nested submenu (auto-expanded by the active child) and badge behave identically to the `items` channel.

Dynamic add/remove (MutationObserver auto-refresh)

Add an item
HomeSettings
Click the button to append a menu item; the sidebar refreshes automatically.

API ​

Methods ​

MethodDescription
openDrawer()Opens the mobile drawer programmatically (same as clicking the built-in trigger part="trigger")
closeDrawer()Closes the mobile drawer programmatically (same as mask click / close button / Esc)
  • Usage: const el = document.querySelector('oas-sidebar') as OASSidebar; el.openDrawer().
  • Equivalent to the controlled drawer-open attribute: the methods are sugar for setting/removing it (openDrawer sets, closeDrawer removes) and can be mixed; repeated calls are idempotent.
  • Mobile only (viewport narrower than mobile-breakpoint); on desktop the attribute is reclaimed by update immediately, leaving no floating layer.
  • Drawer open/close emits no event: observe the drawer-open attribute (MutationObserver) or keep host state; for a two-state toggle, call openDrawer() / closeDrawer() based on your own state.

oas-sidebar ​

Attributes ​

AttributeDescriptionTypeDefault
accordionAccordion mode (when present: expanding one parent collapses the other parents at the same level; multiple parents can be open by default)boolean—
activeValue of the currently-highlighted menu item (controlled: set/clear immediately re-renders highlight)string—
collapsedControlled collapse to an icon strip (present means collapsed)boolean—
drawer-openMobile drawer open state (controlled: set opens, clear closes; auto-removed when breakpoint returns to desktop)boolean—
expand-on-hoverTemporarily expand the collapsed icon strip on hover (visual only; does not change controlled collapsed)——
hide-toggleHide the bottom collapse button (a built-in toggle always visible on desktop; presence hides it — host opt-out for static sidebars)boolean—
itemsMenu items JSON [{label, value, icon?, iconColor?, group?, badge?, children?, actions?}] (supports divider entries {type:"divider"}; children for nested submenus)SidebarEntry[] | string[]
loadingSkeleton loading state (shows a pulsing skeleton when present; value sets row count, default 4)string4
mobile-breakpointMobile breakpoint (px); narrower than this becomes an overlay drawer——
resizableEdge drag-resize (shows a drag rail on the host edge; desktop non-collapsed only)boolean—
resize-maxMaximum resize width (px, default 480)string480
resize-minMinimum resize width (px, default 160)string160
shortcutEnable Ctrl/Cmd+B collapse toggling (off by default to avoid hijacking a global key)boolean—
sideDrawer side: left (default) / right (mobile drawer slides from the right, trigger on the right)——
variantVariant: sidebar (default flush) / floating (radius + shadow) / inset (radius + background contrast)——
widthExpanded width; defaults to the --oas-sidebar-width tokenstring0

Events ​

EventDescription
oas-actiondetail: { value: string, action: string, label: string }; When fired: an item hover action button is clicked (does not fire oas-select)
oas-collapsedetail: { collapsed: boolean }; When fired: Desktop collapse button toggled
oas-resizedetail: { width: number }; When fired: drag-resize ends / arrow keys adjust the width
oas-selectdetail: { value: string, label: string }; When fired: A menu item was selected (also collapses the drawer on mobile)

Slots ​

NameDescription
defaultMenu item content (default slot text; direct <oas-sidebar-item> children recursively become nested children)
footerCustom sidebar footer content
headerCustom sidebar header content

CSS Variables ​

CSS VariableDescriptionDefault
--oas-sidebar-badge-bgBadge backgroundvar(--oas-color-primary)
--oas-sidebar-badge-colorBadge text color#fff
--oas-sidebar-bgSidebar background; defaults stay anchored to base tokens (theme / dark / brand flow through automatically), host override recolors the whole barvar(--oas-color-bg-hover)
--oas-sidebar-collapsed-widthCollapsed icon-rail width64px
--oas-sidebar-item-hover-bgItem hover background (defaults to a text-primary 6% mix)color-mix(in srgb, var(--oas-color-text-primary) 6%, transparent)
--oas-sidebar-widthExpanded width (the width attribute takes precedence)220px

oas-sidebar-item ​

Attributes ​

AttributeDescriptionTypeDefault
badgeCount badge (all-digit strings convert to number, otherwise stay string; matches SidebarItem.badge's string | number)——
groupGroup name (consecutive items in the same group get a group title before the group's first item)——
iconLeading icon (@oas-ui/icons registry icon name; shown in the collapsed icon strip)——
icon-colorIcon color: when set, fixes the icon color (replaces the built-in mono path's currentColor, taking precedence over disabled/active state colors); defaults to currentColor so the color follows states——
valueSelection value (data-carrier field of the declarative child channel)——

Slots ​

NameDescription
defaultSidebar item label content (default slot text); direct child <oas-sidebar-item> (and <oas-sidebar-divider>) elements recursively become the nested children

oas-sidebar-divider ​

Slots ​

NameDescription
defaultDivider data carrier (no attributes; the host parses it as {type:"divider"})

Parts ​

root / panel / head / close / nav / body / foot / toggle (desktop collapse) / trigger (mobile trigger) / mask / item; the header and footer content are injected via slot="header", the default slot and slot="footer" respectively.

Released under the MIT OR Apache-2.0 License.