定价

Sidebar 使用手册

v1.9.5 · @snowui-design-system/components

许可阅读:首次阅读组件文档、源码或 npm 使用流程,先读完整 LICENSE;同任务同版本去重。公开 API 配置与内部实现修改分别按许可处理;全部组件使用统一运行包,源码权益以许可为准。

组件介绍

组织应用的品牌入口、分组导航与底部操作,支持折叠和独立滚动,适合多页面应用。

使用选择

用于应用左侧分组导航,组合 SidebarSection 与 SidebarItem。页面负责路由和内容;公开 logo / footer 角色可替换内置内容,普通右侧信息列不使用 Sidebar。

  • sections 提供导航数据;children 与 sections 同时渲染,并非替换关系。

  • 直属 as="logo" 替换标识,as="footer" 进入固定底部;宽度和折叠状态可绑定,默认启用持久化。

  • 导航及滚动区默认名称、内置折叠按钮(含text浮层展开按钮)随LocaleProvider更新;显式ariaLabel优先于Provider消息。

快速开始

以下示例展示推荐的基本组合。更多场景与组合限制见后文。

<script lang="ts">	import { Avatar, Button, Icon, Separator } from '@snowui-design-system/components';	import { AvatarByewind, Github } from '@snowui-design-system/resource-svelte';	import { Sidebar, SidebarItem, SidebarSection } from '@snowui-design-system/components';	let collapsed = $state(false);	let activeItemId = $state('components');</script> <div style="display: flex; align-items: flex-start; justify-content: center; width: min(100%, 24rem); height: 24rem; min-width: 0;">    <Sidebar bind:collapsed activeItemId={activeItemId} onActiveItemChange={(id: string) => activeItemId = id} persistence={false} resizable={false} ariaLabel="SnowUI documentation" navigationVariant="icon">        <SidebarItem id="introduction" label="Introduction" icon="book" iconCollection="phosphor" />        <SidebarItem id="quick-start" label="Quick Start" icon="rocket" iconCollection="phosphor" updated />        <Separator />        <SidebarSection id="library" title="Library">            <SidebarItem id="components" label="Components" icon="squares-four" iconCollection="phosphor" />            <SidebarItem id="charts" label="Charts" icon="stack" iconCollection="phosphor" />        </SidebarSection>        <div data-as="footer" style="display: flex; align-items: center; gap: var(--gap-3);">            <Button bare iconOnly href="https://byewind.com" target="_blank" rel="noopener noreferrer" aria-label="SnowUI maintainer"><Avatar src={AvatarByewind} size={24} dataName="SnowUI maintainer" /></Button>            <Button bare iconOnly href="https://github.com/SnowUI" target="_blank" rel="noopener noreferrer" aria-label="SnowUI on GitHub"><Icon size={24}><Github /></Icon></Button>        </div>    </Sidebar></div>

API

关键字 类型 默认值 描述
sections SidebarSectionData[] [] 分类数组,每项必填唯一 id,可选 title、collapsible、defaultCollapsed、actions、items、content;content 优先于 items。items 使用 SidebarItemData 的 id、label、icon、href 等字段。
navigationVariant "icon" | "text" "icon" 导航结构变体;text 表示无图标导航,折叠后根宽度与 padding 为零,只保留位于当前页面可视区域左上方的展开按钮;不自动扫描内容推断图标
collapsed boolean — 受控折叠状态,支持 bind:collapsed;未传时从 defaultCollapsed 初始化内部状态。
defaultCollapsed boolean false 非受控初始折叠状态
width number 220 展开宽度,支持 bind:width;拖拽右侧边框时实时更新
minWidth number 160 可拖拽展开宽度下限
maxWidth number 480 可拖拽展开宽度上限
resizable boolean true 是否启用右侧边框调宽命中层
activeItemId string — 外部控制的选中项 id;未传时使用内部选择或恢复的持久化值。仅选择状态,不自动替宿主进行路由。
persistence boolean true 是否记忆 active item 与各 Section 折叠状态
persistenceKey string "default" localStorage 实例命名空间;同一页面存在多个 Sidebar 时必须分别设置
ariaLabel string locale 文案 导航及其滚动区域的可访问名称;省略时随 LocaleProvider 更新,显式值优先,多实例应提供可区分的名称。
continuousHover boolean true 是否在可用导航项之间移动共享 hover 背景;关闭后恢复逐项 hover,选中状态不受影响。
onCollapsedChange (collapsed: boolean) => void — 用户点击内置按钮切换后调用,参数为新折叠状态,不可取消。
onWidthChange (width: number) => void — 用户调宽时调用,参数为夹取到 minWidth/maxWidth 的整数像素宽度。
onActiveItemChange (itemId: string) => void — 导航选择或恢复合法持久化选中项时调用,参数为选中项 id;宿主处理内容与路由。
children Snippet — 无参数显式内容,与 sections 同时渲染;直属 as="logo" 替换 Header Logo,as="footer" 放入固定 Footer,其余内容进入导航区。
parts ComponentPartsOf<{ Indicator: typeof Indicator; SnowUILogo: typeof SnowUILogo; SidebarSection: typeof SidebarSection; SidebarItem: typeof SidebarItem; }> — 配置调宽交互层使用的 Indicator,以及默认生成的 SnowUILogo / SidebarSection / SidebarItem;Indicator 复用默认状态视觉

未重新声明的继承能力请查所属基础组件文档;本表只列本组件源码具名公开属性。

使用边界与详细说明

API 参考

默认 Logo 的 host 为 as="logo",内部以 SnowUILogo as="logo-icon" variant="icon" 与 SnowUILogo as="logo-wordmark" variant="text" 明确区分图形和字标。直接子节点 as="logo" 会被安置到 Header 并完整替换默认 Logo;自定义 Logo 同样必须用它的直接子节点 as="logo-icon" 标记折叠后保留的图形,其余直接子节点视为展开态文字或字标并在折叠时隐藏。直接子节点 as="footer" 会被安置到底部覆盖 Footer。它们不抑制 sections 生成的 Body 内容,其余显式子节点会与 sections 依次渲染在 Body。第三方内容不支持 as 时,用对应角色的 Frame 包裹。Sidebar 根输出 data-collapsed,并提供 Sidebar Context。

<Sidebar activeItemId="dashboard" persistenceKey="workspace-primary">  <Frame as="logo" singleLine>    <BrandIcon as="logo-icon" />    <Text lines={1}>Brand</Text>  </Frame>  <SidebarItem id="dashboard" label="Dashboard" icon="dashboard" updated />  <Separator />  <SidebarSection id="page" title="Page">    <SidebarItem id="social" label="Social media" icon="chat" />  </SidebarSection>  <Text as="footer">© 2026 SnowUI</Text></Sidebar>

布局与行为

  • 展开默认宽度 220px,有图标模式折叠宽度 60px,text 模式折叠宽度 0px,高度 100%。resizable={true} 时,右侧边框全高均可在默认 160px~480px 范围内拖拽调整展开宽度,并通过 width / bind:width 与 onWidthChange 对外同步;边缘显示 Indicator 默认宽度调整指示器。设为 false 时不渲染整边调宽命中层。

  • 折叠与展开以 Sidebar 根宽度为主时间轴:展开宽度 → 折叠宽度(icon 为 60px,text 为 0px)使用 SIDE_PANEL_DISAPPEAR_MOTION 的临界阻尼 spring timing,折叠宽度 → 上次展开宽度使用 SIDE_PANEL_APPEAR_MOTION 的同系 timing;该 timing 复用 Frame 默认 move spring。拖拽期间临时关闭宽度 transition,使边缘与指针同步;enableDefaultMotion={false} 与系统 reduced motion 均关闭该过渡。

  • 右侧整边调宽命中层仅在 resizable={true} 且展开态存在,沿 Sidebar 右侧边框全高铺开,渲染 Indicator 默认短条。命中层与 Sidebar 的可见 overlay scrollbar Thumb 重叠时遵守 Indicator 的 Resize 优先规则,滚动条未被覆盖的区域继续响应。折叠不会覆盖已调整的展开宽度,再次展开时恢复该宽度。

  • 根容器默认 padding 为左右 12px(var(--padding-3))、上下 16px(var(--padding-4));这是 Sidebar 组件默认值,不由使用方重复补 class 或 style。

  • Header 与 Footer 均为非滚动区;Footer 全宽贴底,ScrollArea 独立滚动并延伸至底部,在 Footer 区域内按设计渐隐。Sidebar 根的 Header / Body 默认 gap 为 var(--gap-3)(12px),即 Logo 区域与下方导航区域之间的默认间距;它与 Body 内相邻导航内容的 4px gap 是两个独立层级。

  • Body 纵向 overlay scrollbar 根据根横向 padding 自动外移,轨道右边缘始终距 Sidebar 右边框 4px;默认 12px 横向 padding 时其 right 偏移为 -8px。

  • Logo 所在 Header 区域使用 4px 上下 padding;滚动视口延伸到 Logo 区域背后,并以 Header 实测高度、顶部 padding 和 Header/Body gap 组成的固定区高度实现反向渐隐:从下往上进入区域 20% 时保留 20% 可见度,到 40% 时完全消失。Logo 本身不参与遮罩,列表顶部保留对应空间。

  • Body 导航内容默认使用 var(--gap-1)(4px)间距,直接组合的 SidebarItem 与 SidebarSection 内部列表保持一致。

  • SidebarItemData.depth={1} 与显式 SidebarItem depth={1} 使用同一二级导航视觉:组件在 leading 槽绘制等长双臂、1px Black/40% 密集点线、4px 圆角的 L 形层级连接线,并以该连接线替换普通 Icon。竖线中心与一级 Item Icon 中心同列,横线中心与二级文字中心同轴。

  • Sidebar 内所有横向 Separator 使用同一默认样式,不区分 SidebarSection 自动生成或调用方显式组合:上下与左右 padding 均为 8px(var(--padding-2))、line-height: 0、分割线本体保持 Separator 默认 0.5px 与 Black/10%。节点自身使用 content-box 背景裁切表达 padding,不要求外部 wrapper 或 class;横向与纵向 padding 分别通过 --snowui-sidebar-separator-padding-x-override、--snowui-sidebar-separator-padding-y-override 覆盖。每个 SidebarSection 的分类内容末尾内置该 Separator,调用方无需为分类手动补分割线。

  • 状态记忆默认开启,未传 persistenceKey 时使用 snowui:sidebar:default 保存 active item 与各 Section 的折叠状态。刷新后自动恢复标题展开 / 折叠状态与最后 active item;恢复时若当前 active item 位于已折叠 Section,Sidebar 强制展开该 Section,并将修正后的展开状态重新保存。外部传入的 activeItemId 优先于旧存储记录;可见的无分类 active item 不得继承旧分类 ID 并误展开该分类。同一 origin 存在多个 Sidebar 时,宿主必须提供稳定且唯一的 persistenceKey;不需要状态记忆时显式设置 persistence={false}。

  • 恢复 active item 后,仅当 Body 确实存在纵向溢出时才定位:以“active item 中心 = Body 实际可视区域中心”为目标,首尾位置按可滚动边界夹紧。恢复后立即定位一次,并在默认 Section 布局动效结束后再次校正,避免折叠布局变化造成偏移。

  • active 视觉状态由 Sidebar 管理;宿主仍通过 activeItemId / onActiveItemChange 持有实际导航与内容切换。恢复合法的持久化 active item 时,Sidebar 会调用 onActiveItemChange 通知宿主。

  • 默认与自定义 as="logo" host 均使用 4px 左右 padding,使 Logo 图形与 SidebarItem 图标垂直对齐。

  • Header 在展开 / 折叠状态使用同一套起始对齐结构,Logo Icon 保持固定 leading slot;Test 示例以固定宽度左对齐宿主承载 Sidebar,避免居中宿主在根宽度变化时带动整个 Logo 位移。Logo host 强制保持单行,wordmark 通过 host 裁切与透明度收起,不会在窄宽度下换行并撑高 Logo,也不在动效起点直接切换整体 Header 对齐。

  • 折叠按钮是内置默认样式 <Button iconOnly size="sm">,与 Logo 位于同一 Header 水平线上,并始终位于 Logo host 分配区域的右边。透明命中 host 使用 --size-6 并由 Header flex 安置;展开态增加 var(--padding-1)(4px)右侧 margin,使按钮相对原位置左移 4px,折叠态不增加该间距。ResourceSize 16 图标在 host 内居中;展开态图标不得越过 Header 与导航项共用的内容右边界,在 16px 名义根字号下与该边界保持 8px 间距。不得再用额外 transform 或非 Token 偏移改变此位置。Logo host 填充按钮之前的剩余 Header 宽度,只设置稳定的 36px 最小 leading slot;按钮会跟随 Sidebar 宽度以 Frame 默认 move spring 从 Logo 区域右侧平移到折叠位置,不发生瞬移。按钮外层保留同尺寸透明命中 host,因此鼠标进入 Sidebar,或直接进入按钮应出现的位置(包括折叠态超出根视觉边界的部分)时都能显示按钮。默认 navigationVariant="icon" 时按上述 hover / focus-within 规则显形;navigationVariant="text" 的折叠态隐藏 Logo、Body 和 Footer,根宽度、padding 与 gap 收到零;展开按钮脱离根布局,挂载到 body 并在支持时进入浏览器 top layer,保持可见、可点击。按钮命中盒距当前可视区域左边 var(--padding-3)(名义 12px),上边 var(--padding-4)(名义 16px);采用 fixed 定位保证页面滚动不改变边距。旧浏览器回退为 body 浮层。按钮保留原有图形与尺寸;Header 与顶层按钮交接时从当前可见位置连续移动,复用侧栏展开/收起的 spring timing,快速反向操作从当前动画位置接续;reduced motion 或 enableDefaultMotion={false} 时直接定位;键盘操作后焦点在内置按钮与浮层按钮间恢复,隐藏侧栏 inert 且 aria-hidden。

  • 折叠开始时,Sidebar 宽度、Item 文字与 Section 标题同时开始变化:宽度收窄,Item 文字和标题消费 Frame color bridge 渐隐;Item leading Icon 保持原横向位置。标题透明度归零后,标题高度、padding 与 Section gap 才消费 Frame size bridge 收起,Item 随后上移。展开时按同一组 bridge 平滑恢复。

  • 折叠按钮不内置 Tooltip,通过随状态更新的 aria-label 保留“展开 / 折叠侧边栏”可访问名称。其余内置 Tooltip(折叠态 Item label、Item action、Section action)统一位于对应元素右侧(position="right")。Section 标题默认可折叠但不显示 Tooltip。

  • 默认不设置背景色。

  • Footer 只内置底部覆盖区域,不内置业务文案;区域默认使用 Figma Auto Layout 的 Align top left:内容整体位于容器左上方,内部横向排列并在同一水平线上垂直居中,内容 gap 为 8px;显式singleLine的Footer内容组合保留至少4 CSS px横向间距,较大现有覆盖保持;普通Footer间距不改。区域全宽贴底,内边距上 12px、下 16px、左右 20px。区域无圆角,不绘制背景色,也不使用背景模糊,避免覆盖宿主边框及拖宽时的模糊重绘。直接子节点 as="footer" 提供业务内容。折叠 / 展开期间 Footer 固定使用展开态内容宽度与底部位置,不跟随根宽度收缩或位移,只使用与 Sidebar 宽度时间轴一致的 opacity 渐隐 / 渐显;折叠态通过 inert、aria-hidden 与 pointer-events: none 退出交互。

  • 存在 Footer 时,Sidebar 通过共享 resizeManager 跟踪 Footer 实际高度;滚动内容延伸至 Sidebar 底部,使用 alpha 遮罩还原 Footer 覆盖渐变:Footer 顶部内容保留 100%,向下 20% 高度时保留 20%,到 40% 时完全不可见。Footer 自身内容不参与遮罩,深浅主题均不需要叠加背景色。导航尾部保留 Footer 高度 + 12px 安全区,保证滚到底时最后一项完整位于 Footer 上方。高度由声明式样式维护,拖宽时不中断;Logo 区域使用同一曲线的垂直镜像。


连续 Hover

continuousHover 默认 true。导航容器拥有唯一背景层,在所有可用 SidebarItem(包括 Section 内项目)之间连续移动。active 项的持久背景不被替换, hover 到 active 项时两个背景叠加形成更深颜色。Section 标题属于明确阻断区域: pointer 位于标题时共享层隐藏,不把标题当作相邻 Item 间隙。设为 false 恢复逐项 hover。

先阅读 完整 LICENSE;同任务、同版本、同范围去重。

优先用组件公开 Props 匹配合适样式,尽可能保留原有视觉。确需改写时按 样式选择与改写顺序 使用自己的 class(含项目已有 Tailwind)或实例 style。以下名称来自源码实际消费的公开 override;先核对目标作用域,不修改私有 DOM。采用 SnowUI Token 时选择正确类别,外部应用不被要求安装一方 utilities。

变量 用途与默认参考
--snowui-separator-thickness-override 分割线粗细
--snowui-sidebar-collapsed-header-gap-override 折叠态 Logo host 与折叠按钮间距(默认 var(--gap-1))
--snowui-sidebar-collapsed-logo-slot-width-override icon 变体折叠态 Logo leading slot 宽度(默认 36px)
--snowui-sidebar-collapsed-width-override 折叠宽度(默认 60px)
--snowui-sidebar-default-logo-gap-override 默认 Logo icon / wordmark 间距(默认 calc(var(--gap-1) * 1.5))
--snowui-sidebar-footer-gap-override Footer 内容间距(默认 var(--gap-2) = 8px)
--snowui-sidebar-footer-padding-x-override 覆盖 Footer 横向 padding(默认 var(--padding-5) = 20px)
--snowui-sidebar-footer-padding-y-override 覆盖 Footer 纵向 padding(默认上 12px、下 16px)
--snowui-sidebar-gap-override 根级 Header / Body 间距(默认 var(--gap-3) = 12px)
--snowui-sidebar-item-background-override hover / active 背景(默认 var(--black-4))
--snowui-sidebar-item-radius-override 导航项圆角(默认 var(--radius-3))
--snowui-sidebar-logo-padding-x-override 默认/自定义 Logo host 横向 padding(默认 var(--padding-1))
--snowui-sidebar-logo-padding-y-override Logo 所在 Header 区域纵向 padding(默认 var(--padding-1) = 4px)
--snowui-sidebar-logo-scroll-mask-size-override Logo 区域反向渐隐高度(默认 Header 实测高度 + 顶部 padding + Header/Body gap)
--snowui-sidebar-navigation-gap-override Body 相邻导航内容间距(默认 var(--gap-1))
--snowui-sidebar-padding-x-override 根横向 padding(默认 var(--padding-3) = 12px;Body 滚动条据此保持距 Sidebar 右边框 4px)
--snowui-sidebar-padding-y-override 根纵向 padding(默认 var(--padding-4) = 16px)
--snowui-sidebar-resize-indicator-offset-override 展开态右侧宽度 Indicator 视觉中心距右边缘(默认 var(--padding-1) = 4px)
--snowui-sidebar-separator-padding-x-override Sidebar 内全部横向 Separator 的横向 padding(默认 var(--padding-2) = 8px)
--snowui-sidebar-separator-padding-y-override Sidebar 内全部横向 Separator 的纵向 padding(默认 var(--padding-2) = 8px)
--snowui-sidebar-width-override 展开宽度(默认 220px)

设计指南 · Token · 组合边界

必填属性:无具名必填属性。

可双向绑定:bind:collapsed、bind:width。

继承声明:Omit<FrameProps, "children" | "width">。继承能力以基类公开文档为准;Omit 中的字段已排除,不能按基类原样使用。

共享 Frame API · 返回 API

以下为公开属性实际引用的类型声明,来自源码;Svelte Snippet 表示内容片段,ComponentProps 表示对应组件的 Props。

SidebarSectionData

来源:components/src/blocks/Sidebar.svelte

export interface SidebarSectionData {        id: string;        title?: string;        collapsible?: boolean;        defaultCollapsed?: boolean;        actions?: SidebarActionData[];        items?: SidebarItemData[];        content?: Snippet;    }

SidebarActionData

来源:components/src/blocks/sidebar-context.svelte.ts

export interface SidebarActionData {    id: string;    icon: string;    iconCollection?: string;    label: string;    disabled?: boolean;    onSelect?: (ownerId: string) => void;}

SidebarItemData

来源:components/src/blocks/sidebar-context.svelte.ts

export interface SidebarItemData {    id: string;    label: string;    icon?: string | Snippet;    iconCollection?: string;    depth?: 0 | 1;    href?: string;    disabled?: boolean;    updated?: boolean;    tags?: Snippet;    actions?: SidebarActionData[];}

ComponentPartsOf

来源:components/src/logic/component-parts.svelte.ts

export type ComponentPartsOf<    T extends Record<string, Component<any>>,> = {    [K in keyof T]?: ConfigurableComponentPartProps<T[K]>;};

ConfigurableComponentPartProps

来源:components/src/logic/component-parts.svelte.ts

export type ConfigurableComponentPartProps<C extends Component<any>> = Partial<    Omit<ComponentProps<C>, ComponentPartControlledKey>>;

ComponentPartControlledKey

来源:components/src/logic/component-parts.svelte.ts

export type ComponentPartControlledKey =    | "children"    | "as"    | "value"    | "defaultValue"    | "open"    | "selected"    | "checked"    | "disabled"    | "readonly"    | "id"    | `on${string}`    | `aria-${string}`;