Sidebar handbook
v1.9.5 · @snowui-design-system/components
License: Before reading component documentation, source, or npm usage instructions for the first time, read the full LICENSE. Reuse that review for the same task and version. Public API configuration and internal implementation changes have different license implications. All components use the unified runtime package; source rights remain governed by the license.
Overview
A collapsible, independently scrolling sidebar for brand links, grouped navigation, and bottom actions in multi-page applications.
When to use it
Use it for left-side grouped navigation with SidebarSection and SidebarItem. The page owns routing and content. Public logo and footer roles replace the defaults; a general right-side information panel is outside Sidebar’s scope.
sectionssupplies navigation data. Explicit children render alongside sections rather than replacing them.A direct child with
as="logo"replaces the logo;as="footer"goes to the fixed bottom area.widthandcollapsedcan be bound. Persistence is enabled by default.Default navigation and scroll-region names and built-in collapse controls, including the floating expand button in text mode, follow LocaleProvider. An explicit ariaLabel takes priority over Provider messages.
Quick start
This example shows the recommended basic composition. See the sections below for more cases and composition limits.
API
See the base component documentation for inherited capabilities not redeclared here. This table lists only named public Props declared by this component.
Usage boundaries and details
API reference
The default logo host has as="logo", with SnowUILogo as="logo-icon" variant="icon" and SnowUILogo as="logo-wordmark" variant="text" separating icon and wordmark. A direct as="logo" child replaces the default Header logo. A custom logo must mark its persistent folded icon with a direct as="logo-icon" child; other direct children are treated as expanded text or wordmark and hidden when folded. A direct as="footer" child fills the bottom Footer. These roles do not suppress Body content generated by sections; other explicit children render in Body alongside sections. Wrap third-party content that lacks as with a Frame bearing the role. The root exposes data-collapsed and Sidebar Context.
Layout and behavior
Expanded width defaults to 220px; collapsed width is 60px for icon navigation or 0px for text navigation; height is 100%. With
resizable={true}, the full right edge drags the expanded width within the default 160–480px range, updatingwidth/bind:widthandonWidthChange; Indicator marks the edge. Withresizable={false}, the edge hit area is absent.Collapse animates the root width to 60px (icon) or 0px (text) with
SIDE_PANEL_DISAPPEAR_MOTION; expand restores the last width withSIDE_PANEL_APPEAR_MOTION. Both reuse Frame’s critically damped move spring. Dragging disables width transitions so the edge follows the pointer.enableDefaultMotion={false}or reduced-motion preferences remove the transition.The draggable right-edge hit area exists only while expanded with
resizable={true}. It spans the full height and displays Indicator’s default short bar. Where it overlaps the visible overlay scrollbar thumb, Indicator’s resize priority applies; uncovered scrollbar area still responds. Folding preserves the adjusted expanded width for restoration.Root padding defaults to 12px horizontally (
var(--padding-3)) and 16px vertically (var(--padding-4)). Callers need not add those values through class or style.Header and Footer do not scroll. Footer stays at the bottom; ScrollArea scrolls independently to the bottom and fades beneath Footer. The Header-to-Body gap is
var(--gap-3)(12px), separate from the 4px gap between adjacent Body navigation entries.The Body overlay scrollbar shifts outward according to root horizontal padding; its track stays
4pxfrom the Sidebar’s right border. With the default 12px padding, its CSSrightoffset is-8px.The logo Header has 4px vertical padding. The scroll viewport extends behind it and fades upward across the fixed area of Header height, top padding, and Header-to-Body gap: content remains 20% visible at 20% into that area and disappears by 40%. The logo itself is unmasked, and the list reserves space at its top.
Body navigation content uses
var(--gap-1)(4px) spacing by default, and the directly combined SidebarItem is consistent with the internal list of SidebarSection.SidebarItemData.depth={1}and explicitSidebarItem depth={1}share the same second-level layout. A 1px Black/40% dense dotted L-shaped connector with a 4px rounded corner replaces the ordinary leading icon. Its vertical center aligns with a first-level icon; its horizontal arm aligns with second-level text.Every horizontal Separator in Sidebar uses 8px horizontal and vertical padding (
var(--padding-2)),line-height: 0, and Separator’s default 0.5px Black/10% line. Content-box background clipping expresses this padding without a wrapper. Override it with--snowui-sidebar-separator-padding-x-overrideor--snowui-sidebar-separator-padding-y-override. Each SidebarSection includes its closing separator by default.Persistence is enabled by default. Without
persistenceKey,snowui:sidebar:defaultsaves the active item and each Section’s collapse state. On refresh, the last active item and section states return. If the active item is in a collapsed Section, Sidebar expands that Section and saves the updated state. ExternalactiveItemIdoverrides stored selection. An ungrouped active item must not inherit a stale Section ID. Use a stable uniquepersistenceKeyfor multiple Sidebars on one origin, orpersistence={false}to disable storage.After restoring an active item, scroll only if Body overflows vertically. Center the item in the actual visible Body area, clamped to the scroll bounds. Position once immediately after restore, then correct after the default Section layout effect to account for changed collapse geometry.
The active visual state is managed by Sidebar; the host still holds the actual navigation and content switching through
activeItemId/onActiveItemChange. When restoring the legal persistent active item, Sidebar will callonActiveItemChangeto notify the host.The default and custom
as="logo"host both use4pxleft and right padding to align the Logo graphic vertically with the SidebarItem icon.Header preserves one alignment structure across expanded and collapsed states, with the icon in a stable leading slot. The example uses a fixed-width, left-aligned host so changing root width does not shift the whole mark. The logo host stays on one line; clipping and opacity hide the wordmark at narrow widths rather than wrapping or abruptly changing alignment.
The built-in collapse control is
<Button iconOnly size="sm">on the Logo’s Header row, to the right of its host. Header flex positions its transparent--size-6hit box. Expanded mode addsvar(--padding-1)(4px) right margin, shifting the button 4px left; collapsed mode omits it. AResourceSize16 icon stays centered. At a nominal 16px root font, the expanded icon remains 8px inside the shared Header/navigation content right edge; do not alter this with extra transforms or non-token offsets. The logo host takes remaining Header width with a stable 36px minimum leading slot. Frame’s move spring carries the button continuously from the logo-area right edge to its folded position. Its transparent hit box also reveals it when the pointer enters the expected position, including beyond the collapsed root’s visible bounds. InnavigationVariant="icon", hover and focus-within control visibility. In collapsednavigationVariant="text", Logo, Body, Footer, root width, padding, and gap disappear; the expand button moves out of root layout into the body and browser top layer when available. Its fixed hit box sitsvar(--padding-3)(12px nominally) from the viewport left andvar(--padding-4)(16px) from the top, unaffected by page scroll; older browsers use a body overlay. The button retains its icon and size. The Header-to-overlay handoff animates continuously from the current visible position, using the same spring and allowing rapid reversals. Reduced motion orenableDefaultMotion={false}positions it immediately. Keyboard focus transfers between the built-in and floating controls; the hidden Sidebar becomesinertandaria-hidden.On collapse, root width, Item text, and Section title change together. The leading Item icon stays horizontally fixed while text and title fade using Frame’s color bridge. Once the title is transparent, its height, padding, and Section gap shrink with the Frame size bridge and Items move upward. Expansion reverses the same bridges.
The collapse button has no built-in Tooltip; its changing
aria-labelnames the “expand sidebar” or “collapse sidebar” action. Other built-in Tooltips for collapsed Items, Item actions, and Section actions appear to the right (position="right"). Collapsible Section titles have no Tooltip.The background color is not set by default.
Footer provides a fixed bottom region without business copy. It aligns content at the top-left, lays children out horizontally with vertical centering and 8px gap. Explicit
singleLinefooter content retains at least 4 CSS px of horizontal spacing and larger existing overrides; ordinary footer spacing is unchanged. It spans the full bottom width, and has 12px top, 16px bottom, and 20px horizontal padding. It has no radius, background color, or backdrop blur, preserving the host border and avoiding blur repaint during resize. A directas="footer"child supplies content. During collapse and expansion, Footer keeps its expanded content width and bottom position; only opacity follows the Sidebar width timeline. When collapsed,inert,aria-hidden, andpointer-events: noneremove interaction.With Footer present, Sidebar tracks its actual height through shared
resizeManager. Scrolling content extends to the bottom and uses an alpha mask beneath Footer: visibility is 100% at its top, 20% at 20% of its height downward, and zero by 40%. Footer content is not masked; no theme-specific background overlay is needed. A safe tail region equal to Footer height plus 12px keeps the last navigation item fully above Footer at maximum scroll. Declarative styles maintain its height during resize. The logo area mirrors the fade curve vertically.
Continuous hover
continuousHover defaults to true. A single navigation background moves between enabled SidebarItems, including those in Sections. The active item retains its persistent background, so hovering it stacks the two colors. Section titles interrupt the shared hover region: over a title, the moving layer hides rather than treating it as an adjacent Item gap. Set false for individual item hover backgrounds.
Sidebar style overlay
Read the full LICENSE first; reuse that review for the same task, version, and scope.
First use public component Props to match the desired appearance, preserving the existing look where possible. If changes are necessary, follow the style selection and override order: use your own class (including Tailwind already in the project) or an instance style. The names below are public overrides actually consumed by source; check their scope and do not modify private DOM. Choose the correct SnowUI token family. External apps need not install SnowUI website utilities.
Sidebar type and status
Required Props: no named required Props.
Two-way binding: bind:collapsed, bind:width.
Inheritance statement: Omit<FrameProps, "children" | "width">. The inheritance ability is subject to the public document of the base class; the fields in Omit have been excluded and cannot be used as they are according to the base class.
The following type declarations are actually referenced by public Props and come from source. A Svelte Snippet denotes a content fragment; ComponentProps denotes the Props of the corresponding component.
SidebarSectionData
Source: components/src/blocks/Sidebar.svelte
SidebarActionData
Source: components/src/blocks/sidebar-context.svelte.ts
SidebarItemData
Source: components/src/blocks/sidebar-context.svelte.ts
ComponentPartsOf
Source: components/src/logic/component-parts.svelte.ts
ConfigurableComponentPartProps
Source: components/src/logic/component-parts.svelte.ts
ComponentPartControlledKey
Source: components/src/logic/component-parts.svelte.ts