构建无障碍组件之Tabs Pattern

作者:anOnion日期:2026/3/28

标签页(Tabs)是一种分层的内容展示组件,通过标签列表(Tab List)和对应的内容面板(Tab Panel)来组织和展示内容。本文基于 W3C WAI-ARIA Tabs Pattern 规范,详解如何构建无障碍的标签页组件。

一、Tabs 的定义与核心概念

1.1 什么是 Tabs

Tabs 是一种将内容分层展示的界面模式:

  • Tab List(标签列表):包含一组标签元素的容器
  • Tab(标签):作为对应内容面板的标签,激活后显示该面板
  • Tab Panel(标签面板):包含与标签关联的内容的元素
  • 通常一次只显示一个标签面板
  • 标签列表通常排列在当前显示面板的边缘(最常见的是顶部)

1.2 核心术语

术语说明
Tab List包含在 tablist 元素中的一组标签元素
Tab标签列表中的元素,作为对应面板的标签
Tab Panel包含与标签关联内容的元素
1┌─────────────────────────────────────────┐
2  Tab List (role="tablist")              
3  ┌─────────┬─────────┬─────────┐        
4   Tab 1    Tab 2    Tab 3           
5  │(active)                           
6  └────┬────┴─────────┴─────────┘        
7                                        
8                                        
9  ┌─────────────────────────────────┐    
10    Tab Panel 1 (role="tabpanel")      
11    ┌─────────────────────────┐        
12                                     
13          Content Panel 1            
14          (currently shown)          
15                                     
16    └─────────────────────────┘        
17  └─────────────────────────────────┘    
18                                         
19  ┌─────────────────────────────────┐    
20    Tab Panel 2 (role="tabpanel")      
21    ┌─────────────────────────┐        
22          Content Panel 2            
23          (hidden)                   
24    └─────────────────────────┘        
25  └─────────────────────────────────┘    
26└─────────────────────────────────────────┘
27

1.3 自动激活 vs 手动激活

Tabs 有两种激活模式:

自动激活(Automatic Activation)

  • 标签获得焦点时自动激活并显示对应面板
  • 要求面板内容已预加载,避免延迟
  • 用户导航更高效

手动激活(Manual Activation)

  • 用户需要按 Space 或 Enter 激活标签
  • 适用于面板内容加载有延迟的场景
  • 避免焦点移动时的不必要加载

二、WAI-ARIA 角色与属性

2.1 基本角色

1<div
2  role="tablist"
3  aria-label="产品信息">
4  <button
5    role="tab"
6    aria-selected="true"
7    id="tab-1">
8    产品详情
9  </button>
10  <button
11    role="tab"
12    aria-selected="false"
13    id="tab-2">
14    规格参数
15  </button>
16  <button
17    role="tab"
18    aria-selected="false"
19    id="tab-3">
20    用户评价
21  </button>
22</div>
23
24<div
25  role="tabpanel"
26  aria-labelledby="tab-1">
27  <!-- 产品详情内容 -->
28</div>
29

2.2 Tab List 属性

Tab List 容器具有 role="tablist"

1<!-- 水平标签页(默认) -->
2<div
3  role="tablist"
4  aria-label="产品信息">
5  ...
6</div>
7
8<!-- 垂直标签页 -->
9<div
10  role="tablist"
11  aria-label="设置选项"
12  aria-orientation="vertical">
13  ...
14</div>
15

2.3 Tab 属性

每个标签具有 role="tab"

1<button
2  role="tab"
3  id="tab-1"
4  aria-selected="true"
5  aria-controls="panel-1"
6  tabindex="0">
7  产品详情
8</button>
9
10<button
11  role="tab"
12  id="tab-2"
13  aria-selected="false"
14  aria-controls="panel-2"
15  tabindex="-1">
16  规格参数
17</button>
18

2.4 Tab Panel 属性

每个面板具有 role="tabpanel"

  • aria-labelledby:引用对应标签的 ID
  • 未激活的面板通常使用 hidden 属性或 CSS 隐藏
1<div
2  role="tabpanel"
3  id="panel-1"
4  aria-labelledby="tab-1">
5  <!-- 激活的面板内容 -->
6</div>
7
8<div
9  role="tabpanel"
10  id="panel-2"
11  aria-labelledby="tab-2"
12  hidden>
13  <!-- 未激活的面板内容 -->
14</div>
15

三、键盘交互规范

3.1 Tab 键导航

场景行为
焦点进入 Tab List焦点置于当前激活的标签上
焦点在 Tab List 中焦点移动到 Tab List 外的下一个元素(通常是 Tab Panel 或其内部第一个可聚焦元素)

3.2 方向键导航(水平标签页)

按键功能
左箭头焦点移到上一个标签;如果在第一个标签,移到最后一个
右箭头焦点移到下一个标签;如果在最后一个标签,移到第一个
Home(可选)焦点移到第一个标签
End(可选)焦点移到最后一个标签

3.3 方向键导航(垂直标签页)

按键功能
上箭头等同于水平标签页的左箭头
下箭头等同于水平标签页的右箭头

3.4 激活操作

按键功能
Space / Enter激活当前聚焦的标签(手动激活模式下)
Shift + F10(Windows)Control + 点击(Mac)如果标签有关联的弹出菜单,打开菜单
Delete(可选)如果允许删除,删除当前标签及其面板

3.5 自动激活说明

  • 推荐在面板内容已预加载时使用自动激活
  • 自动激活时,方向键移动焦点会立即激活对应标签
  • 如果面板加载有延迟,使用手动激活避免阻碍导航

四、实现方式与样式要点

4.1 基础 HTML 结构

1<div class="tabs">
2  <!-- Tab List -->
3  <div
4    role="tablist"
5    aria-label="产品信息">
6    <button
7      role="tab"
8      id="tab-1"
9      aria-selected="true"
10      aria-controls="panel-1"
11      tabindex="0">
12      产品详情
13    </button>
14    <button
15      role="tab"
16      id="tab-2"
17      aria-selected="false"
18      aria-controls="panel-2"
19      tabindex="-1">
20      规格参数
21    </button>
22    <button
23      role="tab"
24      id="tab-3"
25      aria-selected="false"
26      aria-controls="panel-3"
27      tabindex="-1">
28      用户评价
29    </button>
30  </div>
31
32  <!-- Tab Panels -->
33  <div
34    role="tabpanel"
35    id="panel-1"
36    aria-labelledby="tab-1">
37    <h2>产品详情</h2>
38    <p>这是一款高性能的...</p>
39  </div>
40
41  <div
42    role="tabpanel"
43    id="panel-2"
44    aria-labelledby="tab-2"
45    hidden>
46    <h2>规格参数</h2>
47    <table>
48      <tr>
49        <th>尺寸</th>
50        <td>100 x 50 x 20 mm</td>
51      </tr>
52      <tr>
53        <th>重量</th>
54        <td>200g</td>
55      </tr>
56    </table>
57  </div>
58
59  <div
60    role="tabpanel"
61    id="panel-3"
62    aria-labelledby="tab-3"
63    hidden>
64    <h2>用户评价</h2>
65    <p>"非常满意这款产品..."</p>
66  </div>
67</div>
68

4.2 样式实现注意事项

4.2.1 激活状态样式

激活的标签需要有明显的视觉区分:

  • 下划线/边框:使用边框颜色变化指示激活状态
  • 背景色:激活标签使用不同的背景色
  • 文字样式:加粗或改变颜色增强对比

4.2.2 焦点状态样式

确保键盘用户可以清楚看到当前焦点位置:

  • 使用 outlinebox-shadow 创建焦点环
  • 焦点环颜色与背景有足够对比度
  • 避免使用 outline: none 而不提供替代样式

4.2.3 面板显示/隐藏

  • 未激活的面板应使用 hidden 属性或 display: none 完全隐藏
  • 避免使用 visibility: hiddenopacity: 0,这会让内容仍可被屏幕阅读器访问

4.2.4 垂直标签页样式

垂直布局时需要注意:

  • 标签列表使用 flex-direction: column
  • 激活指示器从底部边框改为右侧边框
  • 确保足够的点击区域(最小 44x44px)

4.2.5 响应式设计

移动端适配建议:

  • 小屏幕下标签可以换行或使用水平滚动
  • 考虑将水平标签页切换为垂直布局
  • 调整标签内边距和字体大小

五、常见应用场景

5.1 产品详情页

1<div
2  role="tablist"
3  aria-label="产品信息">
4  <button
5    role="tab"
6    aria-selected="true"
7    aria-controls="panel-overview">
8    概览
9  </button>
10  <button
11    role="tab"
12    aria-selected="false"
13    aria-controls="panel-features">
14    功能
15  </button>
16  <button
17    role="tab"
18    aria-selected="false"
19    aria-controls="panel-reviews">
20    评价
21  </button>
22</div>
23

5.2 设置面板

1<div
2  role="tablist"
3  aria-label="设置选项"
4  aria-orientation="vertical">
5  <button
6    role="tab"
7    aria-selected="true"
8    aria-controls="panel-account">
9    账户
10  </button>
11  <button
12    role="tab"
13    aria-selected="false"
14    aria-controls="panel-privacy">
15    隐私
16  </button>
17  <button
18    role="tab"
19    aria-selected="false"
20    aria-controls="panel-notifications">
21    通知
22  </button>
23</div>
24

5.3 代码示例展示

1<div
2  role="tablist"
3  aria-label="代码示例">
4  <button
5    role="tab"
6    aria-selected="true"
7    aria-controls="panel-html">
8    HTML
9  </button>
10  <button
11    role="tab"
12    aria-selected="false"
13    aria-controls="panel-css">
14    CSS
15  </button>
16  <button
17    role="tab"
18    aria-selected="false"
19    aria-controls="panel-js">
20    JavaScript
21  </button>
22</div>
23

六、最佳实践

6.1 选择合适的激活模式

  • 自动激活:面板内容已预加载,无明显延迟
  • 手动激活:面板内容需要异步加载,或加载时间较长

6.2 确保键盘可访问

  • 所有标签都必须可以通过键盘聚焦
  • 方向键在标签之间循环导航
  • Tab 键从标签列表移动到面板内容

6.3 提供清晰的视觉反馈

  • 激活的标签使用不同的样式(颜色、边框)
  • 焦点状态清晰可见
  • 未激活的面板完全隐藏

6.4 避免嵌套 Tabs

不要在 Tab Panel 内部嵌套另一个 Tabs,这会造成:

  • 键盘导航复杂且容易迷失
  • 屏幕阅读器用户难以理解层级关系
  • 视觉上的混乱

6.5 处理大量标签

如果标签数量过多:

  • 考虑使用垂直方向节省水平空间
  • 或者重新组织内容结构
  • 避免标签需要水平滚动

6.6 移动端适配

1@media (max-width: 640px) {
2  [role='tablist'] {
3    flex-wrap: wrap;
4  }
5
6  [role='tab'] {
7    flex: 1;
8    min-width: 80px;
9    padding: 10px;
10    font-size: 14px;
11  }
12}
13

七、Tabs 与其他组件的选择

7.1 Tabs 与 Accordion 的选择

场景TabsAccordion
需要同时对比多个内容❌ 一次只能看一个✅ 可同时展开多个
水平空间有限❌ 需要水平排列标签✅ 垂直展开更省空间
内容有明确顺序✅ 标签顺序暗示优先级❌ 各面板相对独立
需要频繁切换内容✅ 点击标签即可切换❌ 需要展开/折叠操作
移动端优先❌ 水平空间受限✅ 垂直空间更充裕
键盘导航效率✅ 方向键快速循环⚠️ Tab 键逐个遍历
内容关联性✅ 同一主题不同方面✅ 相对独立的内容块
典型应用场景产品详情页、设置面板FAQ、内容列表、过滤器

选择建议:

  • 用 Tabs:内容关联紧密、空间充足、需要快速切换
  • 用 Accordion:需要对比多个内容、移动端、垂直空间充裕

7.2 Tabs 与 Menu 的选择

场景TabsMenu
页面内内容切换✅ 显示对应面板,不跳转❌ 预期会跳转页面
导航到不同页面❌ 不适合页面导航✅ 专为导航设计
执行操作(如下载)❌ 不适用于操作✅ 可触发各种操作
内容关联性✅ 同一上下文相关内容❌ 通常相互独立
视觉反馈✅ 始终显示激活状态✅ 显示当前页面
键盘导航✅ 方向键循环,Tab 离开✅ 方向键导航,Enter 激活
ARIA 角色tablist + tab + tabpanelmenubar + menuitem 或 navigation + link
典型应用场景产品详情、代码示例展示主导航、用户操作菜单

选择建议:

  • 用 Tabs:同一页面内切换相关内容
  • 用 Menu:页面导航或执行操作

常见误区:

  • ❌ 使用 Tabs 作为主导航菜单(应使用 <nav> + <a>
  • ❌ 使用 Menu 展示同一页面内容(应使用 Tabs)
  • ❌ Tabs 标签使用 <a> 链接(应使用 <button>
  • ❌ 在移动端使用过多 Tabs(应考虑 Accordion)

八、总结

构建无障碍的 Tabs 组件需要关注:

  1. 正确的 ARIA 角色tablisttabtabpanel
  2. 完整的状态管理aria-selectedaria-controlstabindex
  3. 键盘导航支持:方向键循环、Home/End 快捷键
  4. 合适的激活模式:根据内容加载情况选择自动或手动激活
  5. 清晰的视觉反馈:激活状态、焦点状态明确可辨

遵循 W3C Tabs Pattern 规范,我们能够创建既美观又包容的标签页组件,为所有用户提供一致的体验。

文章同步于 an-Onion 的 Github。码字不易,欢迎点赞。


构建无障碍组件之Tabs Pattern》 是转载文章,点击查看原文


相关推荐


GitHub Copilot SDK 入门:五分钟构建你的第一个 AI Agent
乱世不浮生2026/3/20

TL;DR The core value of the GitHub Copilot SDK is not the convenience of "calling an LLM" (that's already been solved by the OpenAI SDK, LangChain, etc.), but rather providing a production-proven Agent runtime. The problems it actually solves are: O


【浏览器MCP组件】 chrome-devtools的快捷方式和MCP配置
伊玛目的门徒2026/3/11

创建Chrome调试快捷方式 右键点击桌面空白处,选择"新建"-"快捷方式"。在目标位置输入以下命令: C:\Users\luke\AppData\Local\ms-playwright\chromium-1208\chrome-win64\chrome.exe --remote-debugging-port=9222 --user-data-dir="%USERPROFILE%\ChromeDebugProfile" 为快捷方式命名,例如"Chrome调试模式"。双击此快捷方式将启动指定版


从零开始的Web3学习 10| Solidity 视图和纯函数 (View and Pure Functions)
庭前云落2026/3/3

在 Solidity 中,view 和 pure 是用于修饰函数的关键字,它们描述了函数对区块链状态的读写行为。正确使用这两个修饰符可以提高代码的可读性,并帮助编译器进行静态检查。 1. view 函数 承诺:不修改状态,但可以读取状态变量。允许的操作: 读取状态变量(如 uint public data)。调用其他 view 或 pure 函数。访问 address(this).balance 或 block.number 等区块链数据(这些不属于“状态修改”)。 禁止的操作:任何会改变状态的


再论自然数全加和 - 质数螺旋
铸人2026/2/23

下面考虑质数螺旋 曾经以1开始绘制螺旋图,但是计算质数坐标的时候就出现困难。所以我们用0开始,并把它放在螺旋的中心。 观察如下图像, 最中心的数字0,不算大小。圈数为 ,对应的数的个数,也就是面积为, 这些圈的最小值是0,最大值是, 相邻两项的差为, 这是一个二阶等差数列,对应的数值的和为, 这些数值,并不关心旋转的起点。仔细观察我们发现这些质数构成的线都几乎都是对角线,相当于旋转了45°的结果,既然如此,我们把起点旋转45°,看看能不能把斜线变成横竖的直线。


字节发力,豆包大模型2.0 震撼来袭(附 Trae 实测)
苍何2026/2/15

这是苍何的第 496 篇原创! 大家好,我是苍何。 其实在早些时候,我就深度参与了豆包大模型2.0 的内测。 今天,终于,豆包大模型 2.0 正式发布了。 说实话,这次的升级幅度,属实把我整不会了。 先说结论:「豆包 2.0 Pro 全面对标 GPT 5.2 和 Gemini 3 Pro」。 「人类最后的考试」HLE-Text 拿下 54.2 分最高分,ICPC 编程竞赛金牌,IMO 数学奥赛也是金牌。 好家伙,字节这是要掀桌子啊。 豆包 2.0,到底升级了啥 这次发布的是一整个系列,包含 P


2026 AI Agent 风口必看|四大技术变革+多Agent实战
User_芊芊君子2026/2/6

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:AI 文章目录: 一、先破后立:2026年AI Agent的核心变革(新颖切入点)1.1 变革1:架构升级——从“四段式”到“PDA+记忆+反思”闭环1.2 变革2:协同升级——A2A协议主导,多Agent协作常态化1.3 变革3:工具升级——MCP协议统一,工具调用标准化1.4 变革4:能力升级——Skills模块化,Agent能力可复用 二、实战落地:2026年多Agent协作项目(


Settings,变量保存
cfqq19892026/1/28

作用: 变量在exe文件内。比txt操作方便。 步骤: 就这么简单: Settings.Default.Save();  // 放到窗口关闭事件中。 private void Form1_Load(object sender, EventArgs e) { fa = new FA(); //【4】订阅委托广播 fa.wt_get += wt_get; //


ooder-agent v0.6.2 升级实测:SDK 封装 + Skill 化 VFS,AI 一键生成分布式存储应用
OneCodeCN2026/1/19

作为一名深耕分布式Agent框架的开发者,我踩过最多的坑,就是分布式存储的配置复杂、断网数据丢失、自定义应用开发成本高这三大难题。 直到上手 ooder-agent v0.6.2 版本,我才发现原来分布式存储应用可以这么简单——这次升级直接把两个核心痛点连根拔起:agent-sdk 深度封装降低开发门槛,skill-vfs 变身完整Skill程序适配复杂网络场景,更关键的是,AI一句话就能生成存储应用,零代码自动部署。 今天就从技术角度,聊聊这次升级的两大核心亮点和实际使用价值。 一、核心升级1


JNI是什么?
自由生长20242026/1/11

JNI是什么? JNI(Java Native Interface,Java本地接口)是Java平台自1.1版本起提供的标准编程接口,它是一套强大的编程框架,允许运行在Java虚拟机(JVM)中的Java代码与用C、C++等其他编程语言编写的本地代码进行交互。 核心特点 功能扩展:允许Java程序调用本地代码,实现标准Java类库无法支持的功能 性能优化:对于性能敏感的计算密集型任务(如图像处理、音视频编解码、复杂数学运算),本地代码通常比Java实现更高效 代码复用:可以重用已有的C/C++


fmtlib/fmt仓库熟悉
LumiTiger2026/1/2

一、仓库(fmtlib/fmt)依赖/用到的开源库 fmt 核心设计为无外部运行时依赖(self-contained),仅在特定功能/实现中引用少量开源算法/工具(非链接依赖): Dragonbox: 内嵌该开源算法(https://github.com/jk-jeon/dragonbox),用于实现 IEEE 754 浮点数的高性能格式化(保证正确舍入、短长度、往返一致性),是 fmt 浮点格式化的核心实现基础。构建/测试类工具(非业务依赖): CMake:跨平台构建系统;oss-f

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客