其他
当多个共享上下文的同级视图需要频繁切换时,用 Tab…
适用于 2–7 个共享标题、主操作或共同主题的同级视图,如详情页、设置页、仪表盘多数据视图。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 56/100 ×0.15
适用场景
当多个共享上下文的同级视图需要频繁切换时通用
解决思路
- 选择 Underline / Indicator Tabs:使用水平 strip,活动 tab 有滑动下划线或底边框;这是最轻量的处理,适合页面级 tabs,且 tab strip 位于内容区域内。
- 选择 Contained / Boxed Tabs:每个 tab 是独立盒子,活动 tab 看起来与下方 panel 相连;视觉权重更高,适合屏幕顶部或卡片内的重要 tab 组。
- 选择 Pill / Button Tabs:使用圆角胶囊切换状态;强调较低,适合 section 内的二级内容切换(非页面级),常用于视图切换(如 “List / Grid / Map”)。
- 构建 tab strip:设置 role="tablist",固定高度通常 40–48px,不随页面滚动;页面很长时考虑 sticky。
- 构建 tab panel:设置 role="tabpanel",填充剩余可用高度;内容溢出时滚动 panel,而不是 tab strip。
- 设置活动指示器:underline tabs 使用 2–3px 底边框,颜色 --color-primary;contained / pill tabs 使用填充背景。
- Active 状态:使用主色指示器;label 使用 --color-text-primary 或 primary。
- Inactive 状态:label 使用弱化色 --color-text-secondary;不显示指示器。
- Hover 状态:使用轻微背景变化 --color-grey-50;label 稍微加深。
- Focus 状态:使用可见焦点环 outline: 2px solid --color-primary; outline-offset: 2px。
- Disabled 状态:降低不透明度 0.4;cursor: not-allowed;永远不要把活动 tab 设为 disabled。
- 如果内容不可用,不要禁用 tab;在 panel 内显示内容并解释原因。
- 当 tab strip 宽于视口或容器时,不要换行到多行;会破坏 strip 隐喻。
- 溢出时使用可滚动 strip:水平滚动;右边缘显示 fade/gradient 提示溢出;触摸设备首选。CSS 示例:.tab-strip { display: flex; overflow-x: auto; scrollbar-width: none; } .tab-strip::after { content: ''; position: absolute; right: 0; background: linear-gradient(to left, var(--color-surface), transparent); pointer-events: none; }
- 溢出时使用 “More” overflow menu:显示尽可能多的 tabs,其余折叠为 More ▾ 下拉;当被隐藏的 tab 处于活动状态时更新 “More” 标签为 Settings ▾(显示活动隐藏 tab 名称)。
- 桌面仪表盘有很多视图时,优先使用 sidebar nav,而不是 overflow tabs。
- 当有 5+ tabs 且布局有左侧边栏时,使用 Vertical Tabs;它允许更长标签且不溢出,扩展更优雅。
- Vertical Tabs 使用固定宽度侧边栏,通常 200–240px;tab 标签垂直堆叠。
- Vertical Tabs 的 Active tab:使用左侧边框强调 3px solid --color-primary,并加轻微背景。
- Vertical Tabs 的主内容区填充剩余宽度。
- 键盘导航遵循 ARIA “roving tabindex” 模式,在 strip 内导航。
- 按键 Tab:将焦点移到 tab strip,然后进入活动 panel。
- 按键 ← / →:在水平 strip 的 tabs 之间移动;根据内容成本决定立即激活或在 Enter 时激活。
- 按键 ↑ / ↓:在垂直 strip 的 tabs 之间移动。
- 按键 Home:跳到第一个 tab。
- 按键 End:跳到最后一个 tab。
- 按键 Enter / Space:激活焦点 tab(如果尚未在方向键上自动激活)。
- Auto-activate vs manual activate:如果切换 tabs 会触发网络请求,使用 manual activation(方向键移动焦点,Enter 激活)以避免不必要 fetch;如果内容已加载或成本低,方向键自动激活可接受。
- ARIA 结构:使用 div role="tablist" aria-label="Product sections";button role="tab" id="tab-overview" aria-selected="true" aria-controls="panel-overview" tabindex="0";另一个 button 使用 aria-selected="false" tabindex="-1";div role="tabpanel" id="panel-overview" aria-labelledby="tab-overview";非活动 panel 加 hidden。
- 活动 tab 设置 aria-selected="true",其他所有 tab 设置 false。
- 活动 tab 设置 tabindex="0",其他所有 tab 设置 -1(roving tabindex)。
- 非活动 panel 使用 hidden 属性(或 display: none),让屏幕阅读器跳过隐藏 panel。
- 当上方标题不足以描述该组时,在 tablist 上设置 aria-label。
- 跨页面加载和导航记住活动 tab。
- 使用 URL fragment 或 query param:?tab=activity;这是最稳健的方法,支持 deep linking 和浏览器前进/后退。
- 使用 localStorage:可接受用于偏好(例如首选 dashboard 视图),但不要求可分享。
- 不要使用 sessionStorage;关闭 tabs 会意外丢失状态。
- 当 URL 包含 tab param 时,加载时跳转到该 tab,即使它不是第一个 tab。
- 避免 tabs 套 tabs;嵌套 tabbing 会让 scope 混乱,用户不知道外层 tab 的含义。
- 如果发现嵌套 tabs,重新考虑信息架构:内层 tabs 能否变成 secondary nav(pills、panel 内 sidebar)?外层 tabs 能否变成顶级页面导航?
- 主内容区最多只放一层 tabs。
- 检查:是否有 2–7 个 tabs,且每个共享共同主题或上下文?
- 检查:tab strip 是否在任何目标视口都不换行到多行?
- 检查:tab overflow 是否使用可滚动 strip 或 “More” 菜单,而不是换行?
- 检查:活动 tab 是否通过颜色和/或指示器清晰区分?
- 检查:hover 和 focus 状态是否满足对比度要求?
- 检查:是否避免 disabled tabs(改为在 panel 内显示不可用)?
- 检查:键盘导航是否遵循 roving tabindex(←/→ 在 tabs 间,Tab 进入 panel)?
- 检查:是否正确设置 role="tablist"、role="tab"、role="tabpanel" 和 aria-selected?
- 检查:非活动 panels 是否从无障碍树隐藏(hidden 属性)?
- 检查:活动 tab 是否持久化到 URL(用于 deep-linkable 视图)或 localStorage?
- 检查:是否避免嵌套 tabs?
适用边界 · 注意事项
- 超过 7–8 个 tabs,或视图彼此无关、其中一个明显是主入口、每个 tab 需要不同 header/layout 时,不要用 tabs;改用 sidebar / section nav、secondary nav / overflow 或独立页面。
- 内容具有顺序性时,不要用 tabs;改用 stepper / wizard。
- 不要嵌套 tabs;如果必须,改为 secondary nav(pills、panel 内 sidebar)或外层顶级页面导航。