HySidebar
Usage examples on this page are written for Lit / plain HTML (
<hy-sidebar>). The same component ships asHySidebarin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
Child components: <hy-sidebar-group>, <hy-sidebar-item> — used only inside this one.
A persistent navigation rail with collapse-to-icon-only state.
hy-sidebar is a single <nav> landmark — it does not wrap in <aside>, because <aside> is itself a complementary landmark and nesting the two would expose two landmarks for one purpose.
Pair with hy-sidebar-group for titled sections and hy-sidebar-item for rows. On mobile, wrap the sidebar inside hy-drawer — the drawer provides overlay/focus-trap semantics, the sidebar provides the content.
When to use
- The main destinations of an application with more than a few sections.
- Destinations that fall into groups, with
hy-sidebar-group.
Do
- Make it
collapsiblewhen the work needs the width.
When not to use
- Two or three destinations: navigation in an
hy-topbar, orhy-tab-nav. - A phone-first screen:
hy-bottombarfor the top-level destinations, or a temporaryhy-drawerfor a menu opened on demand. - Switching between views inside one page:
hy-tab-group.
Accessibility: your part
- Mark exactly one
hy-sidebar-itemactive: the current page. - With
collapsibleorcollapsed, give everyhy-sidebar-itemaleadingicon. A collapsed item without one shows as an empty row.
Examples
Navigation sidebar with grouped items
<hy-sidebar label="Main navigation">
<div slot="header">Edelweiss</div>
<hy-sidebar-group label="Workspace">
<hy-sidebar-item label="Dashboard" value="dashboard" href="/dashboard" active>
<hy-icon slot="leading" name="home" decorative></hy-icon>
</hy-sidebar-item>
<hy-sidebar-item label="Projects" value="projects" href="/projects">
<hy-icon slot="leading" name="folder" decorative></hy-icon>
</hy-sidebar-item>
</hy-sidebar-group>
<hy-sidebar-group label="Settings">
<hy-sidebar-item label="Preferences" value="preferences" href="/preferences">
<hy-icon slot="leading" name="settings" decorative></hy-icon>
</hy-sidebar-item>
</hy-sidebar-group>
</hy-sidebar>Collapsible rail with the built-in toggle
<hy-sidebar collapsible toggle-placement="header-end" width-expanded="280px">
<div slot="header">Edelweiss</div>
<hy-sidebar-item label="Home" value="home" href="/">
<hy-icon slot="leading" name="home" decorative></hy-icon>
</hy-sidebar-item>
</hy-sidebar>
<script>
document.querySelector('hy-sidebar').addEventListener('collapse', (e) => {
console.log(e.detail.collapsed);
});
</script>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
collapsed | collapsed | boolean | false | Whether the sidebar is collapsed to icon-only mode. |
collapsible | collapsible | boolean | false | Whether to render a built-in collapse toggle at the top of the sidebar. The toggle is an hy-icon-button so its hit-area, hover/focus styling and theming follow the same scale as every other icon-button in the system (32px at size="small"). The icon name is hard-coded to arrow-collapse so consumers can't accidentally pass an undefined value — the historical foot-gun this slot was designed to avoid. |
embedded | embedded | boolean | false | Set inside a panel that already draws the frame (an app pane, a splitter panel): the sidebar fills the panel's width, paints no surface of its own (the panel's fill shows through), draws no edge of its own (the stroke stays, transparent, so nothing moves; under forced colors too, where the panel's own divider stands) and its header, scroller and footer sit flush — no block padding, no gap between them — so the content sets its own rhythm. The inline padding stays. The shape of a navigation list inside a layout pane (Primer's NavList in a PageLayout pane, Fluent's Nav). |
placement | placement | 'start' | 'end' | 'start' | Which edge of the parent the sidebar sits on. |
togglePlacement | toggle-placement | 'top-corner' | 'header-end' | 'top-corner' | Where the built-in collapse toggle is placed. See SidebarTogglePlacement for the visual contract of each value. Default top-corner preserves pre-existing behavior; pick header-end when the brand mark is the visual anchor and the toggle should sit below it (Material 3 / modern SaaS pattern). |
label | label | string | 'Main navigation' | Accessible label for the <nav> landmark. |
collapseLabel | collapse-label | string | 'Collapse' | Label on the collapse toggle when the sidebar is expanded. |
expandLabel | expand-label | string | 'Expand' | Label on the collapse toggle when the sidebar is collapsed. |
widthExpanded | width-expanded | string | '' | Width when expanded. Escape hatch; writes --hy-sidebar-width-expanded inline. |
widthCollapsed | width-collapsed | string | '' | Width when collapsed. Escape hatch; writes --hy-sidebar-width-collapsed inline. |
Events
| Event | Detail | Description |
|---|---|---|
collapse | — | Fired on every collapse state change. Detail: { collapsed: boolean } |
Slots
| Slot | Description |
|---|---|
header | Logo, brand, or title above the nav. Stays visible when collapsed. Slot icon-only content (≈32px) that reads in the narrow rail. Position relative to the collapse toggle depends on togglePlacement — see that property for the visual contract. |
default | Items and groups. |
footer | Account, settings, version badge. Hidden chrome when empty. |
CSS Parts
| Part | Description |
|---|---|
base | The <nav> landmark. |
header | Header region. |
content | Scrollable middle region containing items/groups. |
footer | Footer region. |
collapse-toggle | The built-in collapse toggle button (only rendered when collapsible). |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-sidebar-width-expanded | — | Width when expanded (default: 260px) |
--hy-sidebar-width-collapsed | — | Width when collapsed (default: 64px) |
--hy-sidebar-background-surface-base | var(--hy-background-surface-base) | Background color (default: surface-base) |
--hy-sidebar-border-default-rest | var(--hy-border-default-rest) | Border color (default: border-default-rest) |
--hy-sidebar-padding-shell-block | var(--hy-padding-shell-block) | Block padding for header/content/footer |
--hy-sidebar-padding-shell-inline | var(--hy-padding-shell-inline) | Inline padding for header/content/footer |
--hy-sidebar-gap-shell-block | var(--hy-gap-shell-block) | Gap between items at the root |
--hy-sidebar-padding-layout-none | var(--hy-padding-layout-none) | Block padding of header, content and footer when embedded |
--hy-sidebar-gap-layout-none | var(--hy-gap-layout-none) | Gap between header, content and footer when embedded |
--hy-sidebar-motion-transition-expand-duration | var(--hy-motion-transition-expand-duration) | Collapse transition duration |
--hy-sidebar-padding-layout-sm | var(--hy-padding-layout-sm) | Distance from the top + inline-end corner to the absolute-positioned collapse toggle (default: padding-sm) |
--hy-sidebar-collapsed | — | Host-scope variable exposing 1 (collapsed) or 0 (expanded) for descendant / consumer styling (read it via var(); nothing in Harmony consumes it yet) |
--hy-sidebar-motion-transition-expand-easing | var(--hy-motion-transition-expand-easing) | Easing for the collapse and expand transition |
Methods
expand()
Expand the sidebar. No-op if already expanded.
collapse()
Collapse the sidebar. No-op if already collapsed.
toggle()
Toggle the collapsed state.