Accessible, unstyled collapsible application sidebar with rail and mobile-sheet modes.
|
|
| Package |
@vizejs/ui/sidebar |
| Maturity |
stable |
| Own the source |
vize lib pull sidebar |
| Requires |
context, controllable-state, dialog, id, primitive, shortcut |
| Aliases |
sidebar, app sidebar, context panel, navigation rail, off-canvas nav |
| Covers |
shadcn/ui Sidebar, HTML aside landmark, WAI-ARIA dialog pattern |
Usage
import { SidebarProvider, Sidebar, SidebarRoot, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarRail, SidebarTrigger } from "@vizejs/ui/sidebar";
Or copy the source into your project with vize lib pull sidebar (see Source Distribution).
API
Source: src/families/layout/sidebar/sidebar-provider.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
open |
boolean |
undefined |
Controlled desktop open state. undefined selects uncontrolled behavior. |
defaultOpen |
boolean |
true |
Initial desktop open state for uncontrolled use. |
openMobile |
boolean |
undefined |
Controlled mobile sheet open state. undefined selects uncontrolled behavior. |
defaultOpenMobile |
boolean |
false |
Initial mobile sheet open state for uncontrolled use. |
collapsible |
SidebarCollapsible |
"offcanvas" |
Desktop collapse mode: off-canvas, icon rail, or never. |
side |
SidebarSide |
"left" |
Screen edge the sidebar is attached to. |
variant |
SidebarVariant |
"sidebar" |
Consumer styling variant mirrored to data-variant. |
storage |
SidebarStorage |
undefined |
Adapter that persists the desktop open state. It is read only after mount, so server and hydration markup always start from open/defaultOpen. |
storageKey |
string |
"vize-sidebar-open" |
Key used with storage. |
keyboardShortcut |
string | null |
"Mod+B" |
Document-level shortcut that toggles the sidebar. null disables it. |
mobileQuery |
string | null |
"(max-width: 767px)" |
Media query that switches to the mobile sheet. Evaluated only on the client; server rendering assumes desktop. null disables the sheet. |
width |
string |
"16rem" |
Expanded width published as --vize-sidebar-width. |
widthIcon |
string |
"3rem" |
Rail width published as --vize-sidebar-width-icon. |
widthMobile |
string |
"18rem" |
Mobile sheet width published as --vize-sidebar-width-mobile. |
Events
| Event |
Payload |
Description |
update:open |
[value: boolean] |
Fired when the desktop open state requests a controlled value. |
update:openMobile |
[value: boolean] |
Fired when the mobile sheet requests a controlled value. |
open-change |
[value: boolean, previous: boolean, nativeEvent: Event | null] |
Fired after any distinct desktop open request. |
Slots
| Slot |
Slot props |
Description |
default |
SidebarSlotState |
Sidebar layout: SidebarRoot, SidebarInset, and triggers. |
Exposed
| Member |
Type |
Description |
open |
boolean |
Desktop open state. |
state |
SidebarState |
Desktop state token. |
isMobile |
boolean |
Whether the mobile query currently matches. Always false during SSR. |
openMobile |
boolean |
Whether the mobile sheet is open. |
collapsible |
SidebarCollapsible |
Desktop collapse mode. |
side |
SidebarSide |
Attached screen edge. |
variant |
SidebarVariant |
Styling variant. |
sidebarId |
string |
Id of the sidebar landmark, wired to trigger aria-controls. |
setOpen |
(value: boolean, event?: Event | null) => boolean |
Request a desktop open value and report whether it differs. |
setOpenMobile |
(value: boolean, event?: Event | null) => boolean |
Request a mobile sheet open value and report whether it differs. |
toggle |
(event?: Event | null) => boolean |
Toggle the mobile sheet on mobile, otherwise the desktop sidebar. |
Source: src/families/layout/sidebar/sidebar-root.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
"Sidebar" |
Accessible name of the complementary landmark and the mobile sheet. |
to |
string | HTMLElement |
"body" |
CSS selector or element the mobile sheet is moved into. |
portalDisabled |
boolean |
false |
Render the mobile sheet in place instead of teleporting it. |
Slots
| Slot |
Slot props |
Description |
default |
SidebarSlotState |
Sidebar contents. Receives the current Sidebar state. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/layout/sidebar/sidebar-root.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
"Sidebar" |
Accessible name of the complementary landmark and the mobile sheet. |
to |
string | HTMLElement |
"body" |
CSS selector or element the mobile sheet is moved into. |
portalDisabled |
boolean |
false |
Render the mobile sheet in place instead of teleporting it. |
Slots
| Slot |
Slot props |
Description |
default |
SidebarSlotState |
Sidebar contents. Receives the current Sidebar state. |
Exposed
| Member |
Type |
Description |
element |
HTMLElement | null |
Rendered desktop landmark, or null while rendered as a mobile sheet. |
Source: src/families/layout/sidebar/sidebar-content.vue
Props
| Prop |
Type |
Default |
Description |
as |
PrimitiveAs |
"div" |
Element or component to render. |
Slots
| Slot |
Slot props |
Description |
default |
— |
Scrollable main area of the sidebar. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/layout/sidebar/sidebar-footer.vue
Props
| Prop |
Type |
Default |
Description |
as |
PrimitiveAs |
"div" |
Element or component to render. |
Slots
| Slot |
Slot props |
Description |
default |
— |
Sticky bottom area of the sidebar. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/layout/sidebar/sidebar-group.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned group id. null and undefined select a deterministic fallback. |
Slots
| Slot |
Slot props |
Description |
default |
— |
SidebarGroupLabel and group contents. |
Exposed
| Member |
Type |
Description |
element |
Element | null |
Rendered element or component instance. |
labelId |
string |
Id consumed by SidebarGroupLabel and wired to aria-labelledby. |
Source: src/families/layout/sidebar/sidebar-group-label.vue
Props
| Prop |
Type |
Default |
Description |
as |
PrimitiveAs |
"div" |
Element or component to render, for example a heading. |
Slots
| Slot |
Slot props |
Description |
default |
— |
Visible group label. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/layout/sidebar/sidebar-header.vue
Props
| Prop |
Type |
Default |
Description |
as |
PrimitiveAs |
"div" |
Element or component to render. |
Slots
| Slot |
Slot props |
Description |
default |
— |
Sticky top area of the sidebar. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/layout/sidebar/sidebar-inset.vue
Props
| Prop |
Type |
Default |
Description |
as |
PrimitiveAs |
"main" |
Element or component rendered for the main content beside the sidebar. |
Slots
| Slot |
Slot props |
Description |
default |
— |
Main application content. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/layout/sidebar/sidebar-rail.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
"Toggle sidebar" |
Accessible name when no visible label supplies one. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before toggling. Call preventDefault() to keep state unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
SidebarSlotState |
Button contents. Receives the current Sidebar state. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
|
Source: src/families/layout/sidebar/sidebar-trigger.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
"Toggle sidebar" |
Accessible name when no visible label supplies one. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before toggling. Call preventDefault() to keep state unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
SidebarSlotState |
Button contents. Receives the current Sidebar state. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
|
Behavior
Normative behavior for the @vizejs/ui/sidebar compound primitive (application sidebar /
context panel). Every row is proven by the named mounted-DOM, SSR, or compile-time test.
| State x input |
Observable outcome |
Proven by |
| any desktop render |
Root is a labelled <aside> with a deterministic id, data-state, data-side, data-variant; groups are role="group" labelled by their label; inset is <main>; the provider publishes --vize-sidebar-width, --vize-sidebar-width-icon, --vize-sidebar-width-mobile. |
renders a labelled complementary landmark, sections, and CSS width variables |
collapsible="offcanvas", trigger click |
Toggles open; the trigger mirrors aria-expanded/aria-controls; the collapsed landmark gets inert and data-collapsible="offcanvas"; update:open and open-change report the request. |
trigger toggles an off-canvas sidebar and makes the collapsed landmark inert |
collapsible="icon", rail click |
Collapses to a rail that stays interactive (no inert); the rail is a tabindex="-1" button because the trigger is the keyboard path. |
icon collapse keeps the rail interactive and the rail toggles the sidebar |
collapsible="none" |
The sidebar is always expanded, desktop toggles are disabled, and nothing is emitted. |
collapsible none keeps the sidebar expanded and disables toggles |
controlled open; trigger click prevented |
Requests are emitted while state follows the prop; a prevented click does not toggle. |
controlled open waits for the parent and click is preventable |
storage adapter |
The persisted value is read only after mount and each desktop change is written back under storageKey. |
persisted state is restored after mount and written on change |
keyboardShortcut (default Mod+B) |
The document shortcut toggles the sidebar and consumes the key; null disables it. |
the Mod+B shortcut toggles the sidebar and can be disabled |
mobileQuery matches after mount |
The root renders as a modal Dialog sheet labelled like the landmark; the trigger drives openMobile (update:openMobile) and Escape closes it; leaving the query restores the <aside> and listeners are released on unmount. |
mobile query renders the sidebar as a modal sheet toggled by the trigger |
| provider expose |
open, openMobile, isMobile, state, side, variant, sidebarId, setOpen, setOpenMobile, and toggle are available. |
provider exposes typed state and programmatic controls |
| parts outside a provider |
Mounting throws VIZE_UI_CONTEXT_MISSING; a group label outside a group names the missing SidebarGroup provider. |
sidebar parts require a provider |
| SSR |
Isolated requests render byte-identical desktop markup (SSR assumes desktop and ignores storage), including inert for a collapsed off-canvas sidebar. |
renders byte-identical desktop markup across isolated SSR requests |
| hydration with storage |
Hydration reuses server nodes with zero warnings, then applies the persisted state. |
hydrates without mismatches before applying storage and the mobile query |
| public types |
Collapse mode, side, variant, and storage adapter are closed, typed contracts. |
src/families/layout/sidebar/sidebar.types.test-d.ts |
| DOM/SSR/Vapor |
Every part compiles in each renderer lane. |
scripts/check-renderers.ts |
Components
| Component |
Contract |
sidebar-provider.vue |
Owns desktop and mobile state, persistence, the shortcut, the mobile query, and width variables. |
sidebar-root.vue |
Renders the <aside> landmark on desktop or a modal Dialog sheet on mobile. |
sidebar-trigger.vue |
Native button that toggles the sidebar (mobile sheet on mobile) with aria-expanded/aria-controls. |
sidebar-rail.vue |
Pointer hit area on the sidebar edge that toggles it; removed from the tab order. |
sidebar-header.vue |
Header section with data-state. |
sidebar-content.vue |
Scrollable content section with data-state. |
sidebar-footer.vue |
Footer section with data-state. |
sidebar-group.vue |
role="group" wrapper labelled by its SidebarGroupLabel. |
sidebar-group-label.vue |
Label whose id labels the enclosing group. |
sidebar-inset.vue |
Main content region (<main> by default) beside the sidebar. |
Sidebar ships no stylesheet. Consumers style data-state, data-collapsible, data-side,
data-variant, data-mobile, and the width variables.