跳到主要内容

AppShell应用骨架

整页的骨架:顶栏、侧栏、主区、辅助栏、底栏。整体占满一屏,各区各自滚动。侧栏和辅助栏是和边缘留缝的圆角卡片:宽屏停靠在两侧、占据布局空间;窄屏时同一份内容改由悬浮卡片从对应一侧滑入。你现在看的这个文档站,外壳就是它。

三种形态

侧栏有三种形态:停靠(完整宽度)、停靠(收成图标窄条)、抽屉。默认按宽度自动在停靠和抽屉之间切换;collapsed 可以强制。AppShell.Trigger 在抽屉形态下开关抽屉,停靠时在完整宽度和图标窄条之间切换。

跳到主要内容
Oink 邮箱

收件箱

停靠 · 完整宽度
收件箱里的第 1 条
收件箱里的第 2 条
收件箱里的第 3 条
收件箱里的第 4 条
收件箱里的第 5 条
收件箱里的第 6 条
已同步 · 刚刚
  • contained。演示里的骨架嵌在一个 480px 高的框里:高度取 100% 而不是 100dvh,宽窄按自身宽度判断(@container),抽屉和遮罩也只盖住这个框。把浏览器拉窄到框不足 768px,“自动”会变成抽屉形态。
  • 同一份 children。侧栏的内容只写一次。抽屉形态下它被渲染进 悬浮卡片(模态:圈焦点、Esc 关闭),点了里面的导航项后自动关上(closeOnNavigate)。
  • 图标窄条。停靠的侧栏收窄后,里面的 Nav 自动折叠成只剩图标,文字改由 title / aria-label 提供。所以导航项要配图标。
  • 各部件要作为 AppShell 的直接子节点,不要在中间包别的元素。

最小用法

真实应用里的写法(不加 contained)没法在文档页里嵌一份,看代码,或者直接看示例应用。drawerOnly 的开关按钮只在抽屉形态下出现。

API

AppShell 属性

属性类型默认值说明
containedbooleanfalse嵌在固定高度的容器里:高度取 100%,按自身宽度判断形态,抽屉只盖住这个容器。
collapsed'auto' | boolean'auto'侧栏 / 辅助栏是否收成抽屉。auto 按断点判断;true / false 强制。
collapseBelow'sm' | 'md' | 'lg' | 'xl''md'auto 时,宽度小于该断点侧栏变抽屉。
asideCollapseBelow'sm' | 'md' | 'lg' | 'xl''lg'auto 时,宽度小于该断点辅助栏变抽屉。
sidebarOpen / defaultSidebarOpen / onSidebarOpenChangeboolean / boolean / (open) => void—侧栏抽屉的开关(受控 / 非受控)。
asideOpen / defaultAsideOpen / onAsideOpenChangeboolean / boolean / (open) => void—辅助栏的显示状态(受控 / 非受控)。停靠时控制是否显示(默认显示),抽屉形态下控制抽屉开合(默认关闭)。
rail / defaultRail / onRailChangeboolean / boolean / (rail) => void—停靠的侧栏是否收成图标窄条(受控 / 非受控)。
gapSpace3(窄屏 2)卡片之间、卡片到边缘的缝。
skipLinkLabelstring'跳到主要内容'跳转链接的文字。
mainIdstring—<main> 的 id;不传自动生成。
…restHTMLAttributes—ref、className、style 和其余原生属性透传到根节点。

AppShell.Sidebar / AppShell.Aside 属性

属性类型默认值说明
labelstring'侧栏' / '辅助栏'无障碍名称。
widthnumber | string260 / 320停靠时的宽度(数字按 px)。
drawerWidthnumber | string300 / 360抽屉形态下的宽度,不会超过可用宽度。
paddingSpace5卡片内边距(20px:外圆角 32 − 导航项圆角 12)。
closeOnNavigatebooleantrue / false抽屉形态下,点了里面的导航项或链接后自动关上。

AppShell.Header / AppShell.Footer / AppShell.Trigger 属性

属性类型默认值说明
Header / Footer · variant'plain' | 'card''plain'plain 直接坐在画布上;card 和侧栏一样是一张卡片。
Trigger · target'sidebar' | 'aside''sidebar'控制哪一栏。辅助栏的开关在停靠和抽屉两种形态下都可用。
Trigger · labelstring'切换导航' / '切换辅助栏'按钮的无障碍名称。
Trigger · drawerOnlybooleanfalse只在抽屉形态下渲染(不需要图标窄条时用)。
Trigger · childrenReactNode—自定义图标,默认三条横线。

useAppShell() 的返回值

属性类型默认值说明
sidebarCollapsed / asideCollapsedboolean—当前是不是抽屉形态。
sidebarOpen / setSidebarOpen / openSidebar / closeSidebarboolean / 函数—侧栏抽屉的状态与控制。
toggleSidebar() => void—抽屉形态:开关抽屉;停靠形态:展开 / 收成图标窄条。
rail / setRail / toggleRailboolean / 函数—图标窄条的状态与控制。
asideOpen / setAsideOpen / toggleAsideboolean / 函数—辅助栏的显示状态与控制,停靠和抽屉形态通用。
containedboolean—是否嵌在容器里。
mainIdstring—<main> 的 id。

无障碍

  • 地标齐全:Header 是 <header>,Sidebar / Aside 是带名字的 <aside>,Main 是 <main>,Footer 是 <footer>。
  • 骨架的第一个可聚焦元素是“跳到主要内容”链接:平时看不见,Tab 聚焦时滑下来,回车后焦点直接进入主区。
  • 抽屉形态下侧栏是模态对话框:打开时焦点移进去并圈住,Esc 或点遮罩关闭,关闭后焦点回到开关按钮。
  • 开关按钮带 aria-expanded;抽屉形态下还有 aria-haspopup="dialog"。自定义图标时名字仍由 label 提供。
  • 主区可以被聚焦(tabIndex=-1)并独立滚动;给 Sidebar、Aside 起能区分的 label。