关于 tab-navigation
一项关于标签页选择与实现的设计技能,涵盖布局、溢出处理、无障碍、键盘操作和状态保存,适用于设计相关内容视图的界面。
主要功能
- 01标签页适用场景指南
- 02下划线、盒式与胶囊样式
- 03溢出与垂直标签模式
- 04键盘与 ARIA 无障碍支持
- 05URL 与 localStorage 状态保存
适用场景
- 01为相关内容视图选择标签页
- 02设计窄屏下的标签页行为
- 03检查键盘与屏幕阅读器支持
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dembrandt/ dembrandt-skills --skill tab-navigation安装后在 Claude Code 中输入/tab-navigation确认已加载。
详细介绍
标签页导航
标签页将相关内容组织在共同的上下文中。标签栏展示所有可用视图;切换标签页会更换内容面板,而不会跳转页面。只有当所有视图共享标题、主要操作或共同主题时,标签页才最适用——彼此无关的标签页应放在不同页面中。
何时使用标签页——何时不使用
| 适合使用标签页的情况…… | 适合使用其他方式的情况…… |
|---|---|
| 2–7 个视图共享共同上下文(同一记录、同一设置部分) | 标签页超过 7–8 个——改用侧边栏或分区导航 |
| 用户在一次会话中频繁切换视图 | 内容具有顺序性(改用步骤条/向导) |
| 所有标签页都是同等有效的入口 | 某个视图明显是主要视图——将其余视图放入次级导航或溢出菜单 |
| 各视图共享页眉或一组页面级操作 | 每个“标签页”都需要不同的页眉和布局——使用独立页面 |
标签页类型
选择适合布局上下文的视觉样式。
下划线/指示器标签页
水平标签栏,在活动标签页下方显示滑动下划线或底部边框。视觉强调最轻——适用于标签栏位于内容区域内的页面级标签页。
Overview | Activity | Settings
──────────
包含式/方框式标签页
每个标签页都是一个独立方框;活动标签页看起来与下方面板相连。视觉强调较强——适用于屏幕顶部附近或卡片内较醒目的标签页组。
胶囊式/按钮式标签页
圆角胶囊形按钮,用于在不同状态间切换。强调较弱——适用于某个分区内的次级内容切换(非页面级)。常用于视图切换(例如“列表/网格/地图”)。
结构
┌─────────────────────────────────────────────────┐
│ Tab A │ Tab B │ Tab C │ │ ← Tab strip (role="tablist")
├─────────┴─────────────────────────────────────────┤
│ │
│ Tab panel content │ ← Tab panel (role="tabpanel")
│ │
└───────────────────────────────────────────────────┘
- 标签栏: 高度固定(通常为 40–48px)。不会随页面滚动——页面较长时,可考虑将其设为粘性定位。
- 标签面板: 填满剩余可用高度。内容溢出时滚动的是面板,而不是标签栏。
- 活动指示器: 下划线标签页使用
--color-primary的 2–3px 底部边框;包含式/胶囊式标签页使用填充背景。
状态
| 状态 | 视觉样式 |
|---|---|
| 活动 | 主色指示器;标签文字使用 --color-text-primary 或主色 |
| 非活动 | 弱化标签文字(--color-text-secondary);无指示器 |
| 悬停 | 背景颜色轻微变化(--color-grey-50);标签文字略微加深 |
| 聚焦 | 显示可见的焦点环(outline: 2px solid --color-primary; outline-offset: 2px) |
| 已禁用 | 降低不透明度(0.4);cursor: not-allowed;绝不能是活动标签页 |
绝不要禁用活动标签页。如果内容不可用,应在面板中显示并附上说明,而不是禁用标签页。
标签页溢出
当标签栏宽度超过其容器时,不要将标签页换行到多行;这会破坏标签栏的视觉隐喻。以下是三种解决方式,按优先顺序排列。
回退为选择框
测量标签栏宽度;当其内容宽于容器时,使用相同选项渲染原生 <select>。所有选项都能一目了然地访问,且在任何宽度下都不会隐藏在边缘之外;平台也已为触控用户提供选择器。
可滚动标签栏
标签栏可水平滚动,并在右边缘显示渐隐效果以提示有溢出。这是移动设备上的平台惯例;如果用户熟悉这组标签页,这种方式也很合适;但初次阅读的用户会看不到部分选项。
.tab-strip {
display: flex;
overflow-x: auto;
scrollbar-width: none; /* hide scrollbar visually on desktop */
}
.tab-strip::after {
content: '';
position: absolute; right: 0;
background: linear-gradient(to left, var(--color-surface), transparent);
pointer-events: none;
}
“更多”溢出菜单
显示所有适合的标签页,再将其余标签页折叠到 More ▾ 下拉菜单中。活动标签页位于溢出菜单中时,更新“More”标签:Settings ▾(显示当前活动的隐藏标签页名称)。
无论使用哪种方式,都只能在所选标签页下方显示下划线;在整个标签栏下方显示横线会与其争抢视觉注意力。对于包含许多视图的桌面仪表板,优先使用侧边栏导航,而不是溢出标签页。
垂直选项卡
当有 5 个或更多选项卡且布局包含左侧边栏时使用。垂直选项卡允许使用较长的标签,避免溢出问题,并能更灵活地适应不同布局。
- 固定宽度的侧边栏(通常为 200–240px),选项卡标签垂直排列
- 当前选项卡:左侧边框强调(
3px solid --color-primary)+ 淡色背景 - 主内容区填充剩余宽度
键盘导航
选项卡遵循 ARIA 的“roving tabindex”模式,在选项卡栏内导航。
| 按键 | 操作 |
|---|---|
Tab | 将焦点移至选项卡栏(然后移入当前面板) |
← / → | 在选项卡之间移动(水平选项卡栏);根据内容加载成本,立即激活或按 Enter 激活 |
↑ / ↓ | 在选项卡之间移动(垂直选项卡栏) |
Home | 跳至第一个选项卡 |
End | 跳至最后一个选项卡 |
Enter / Space | 激活获得焦点的选项卡(如果方向键尚未自动激活) |
自动激活与手动激活: 如果切换选项卡会触发网络请求,请使用手动激活(方向键移动焦点,按 Enter 激活),以避免不必要的请求。如果内容已加载或加载成本较低,则可以接受按方向键自动激活。
ARIA
<div role="tablist" aria-label="Product sections">
<button
role="tab"
id="tab-overview"
aria-selected="true"
aria-controls="panel-overview"
tabindex="0"
>Overview</button>
<button
role="tab"
id="tab-activity"
aria-selected="false"
aria-controls="panel-activity"
tabindex="-1"
>Activity</button>
</div>
<div
role="tabpanel"
id="panel-overview"
aria-labelledby="tab-overview"
>…</div>
<div
role="tabpanel"
id="panel-activity"
aria-labelledby="tab-activity"
hidden
>…</div>
- 当前选项卡设为
aria-selected="true",其他选项卡设为false - 当前选项卡设为
tabindex="0",其他选项卡设为-1(roving tabindex) - 非当前面板使用
hidden属性(或display: none)——屏幕阅读器会跳过隐藏的面板 - 如果上方标题不能充分说明选项卡组的内容,请为
tablist设置aria-label
状态持久化
在页面加载和导航之间保留当前选项卡:
- URL 片段或查询参数:
?tab=activity— 最可靠的方式;支持深层链接以及浏览器的前进和后退 - localStorage: 适用于无需分享的偏好设置(例如偏好的仪表盘视图)
- 不要使用
sessionStorage— 关闭选项卡会导致状态意外丢失
当 URL 包含选项卡参数时,即使它不是第一个选项卡,也应在加载时跳转到该选项卡。
嵌套选项卡
避免在选项卡内再嵌套选项卡。嵌套选项卡会让范围变得难以理解——用户在“选项卡 A › 子选项卡 2”中编辑内容时,无法清楚理解外层选项卡的含义。
如果发现自己在嵌套选项卡,请重新考虑信息架构:
- 能否将内层选项卡改为次级导航(胶囊按钮、面板内的侧边栏)?
- 能否将外层选项卡改为顶层页面导航?
主要内容区最多使用一层选项卡。
审核清单
- 是否有 2–7 个选项卡,且每个选项卡都属于同一主题或上下文?
- 在任何目标视口下,选项卡栏是否都不会换成多行?
- 是否通过颜色和/或指示标记清楚区分当前选项卡?
- 悬停和焦点状态是否符合对比度要求?
- 是否避免使用禁用的选项卡(改为在面板内显示不可用状态)?
- 键盘导航是否遵循 roving tabindex(按 ←/→ 在选项卡间移动,按 Tab 移入面板)?
- 是否正确设置了
role="tablist"、role="tab"、role="tabpanel"和aria-selected? - 是否通过
hidden属性将非当前面板从无障碍树中隐藏? - 是否将当前选项卡持久化在 URL(适用于可深层链接的视图)或 localStorage 中?
- 是否避免了嵌套选项卡?
- 选项卡溢出时是否使用下拉选择框、可滚动的选项卡栏或“更多”菜单,绝不换行?
- 是否仅在当前选项卡下方显示下划线作为选中标记,而不在整个选项卡栏下方显示横线?