阅读视图

发现新文章,点击刷新页面。

构建无障碍组件之Accordion Pattern

Accordion Pattern 详解:构建垂直堆叠的展开收起组件

Accordion(手风琴)是一种常见的交互组件,由垂直堆叠的可交互标题组成,每个标题包含一个内容部分的标题、摘要或缩略图。本文基于 W3C WAI-ARIA Accordion Pattern 规范,详解如何构建无障碍的 Accordion 组件。

一、Accordion 的定义与核心概念

Accordion 是一组垂直堆叠的交互式标题,每个标题都包含一个内容部分的标题、摘要或缩略图。标题作为控件,允许用户显示或隐藏其关联的内容部分。

Accordion 常用于在单个页面上呈现多个内容部分时减少滚动需求。

1.1 核心术语

  • Accordion Header(手风琴标题):内容部分的标签或缩略图,同时作为显示(在某些实现中也包括隐藏)内容部分的控件
  • Accordion Panel(手风琴面板):与手风琴标题关联的内容部分

在某些 Accordion 中,手风琴标题旁边始终可见额外的元素。例如,每个手风琴标题可能伴随一个菜单按钮,用于提供适用于该部分的操作访问。

二、WAI-ARIA 角色与属性

2.1 基本角色

每个手风琴标题的内容包含在具有 role="button" 的元素中。

2.2 标题层级

每个手风琴标题按钮包装在具有 role="heading" 的元素中,并设置适合页面信息架构的 aria-level 值:

  • 如果原生宿主语言具有隐式标题和 aria-level 的元素(如 HTML 标题标签),可以使用原生宿主语言元素
  • 按钮元素是标题元素内部的唯一元素
<!-- 手风琴标题 -->
<h3>
  <button aria-expanded="true" aria-controls="panel-1" id="accordion-header-1">
    第一部分标题
  </button>
</h3>

<!-- 手风琴面板 -->
<div id="panel-1" role="region" aria-labelledby="accordion-header-1">
  <p>第一部分的内容...</p>
</div>

2.3 状态属性

  • aria-expanded:如果与手风琴标题关联的面板可见,设置为 true;如果面板不可见,设置为 false
  • aria-controls:设置为包含手风琴面板内容的元素的 ID
  • aria-disabled:如果与手风琴标题关联的面板可见,且手风琴不允许折叠该面板,则设置为 true

2.4 区域角色(可选)

每个作为面板内容容器的元素可以具有 role="region"aria-labelledby,其值引用控制面板显示的按钮:

  • 避免在会创建过多地标区域的情况下使用 region 角色,例如在可以同时展开超过约 6 个面板的手风琴中
  • 当面板包含标题元素或嵌套手风琴时,region 角色对屏幕阅读器用户感知结构特别有帮助
<!-- 手风琴标题按钮 -->
<h3>
  <button aria-expanded="true" aria-controls="panel-1" id="header-1">
    面板标题
  </button>
</h3>

<!-- 手风琴面板内容 -->
<div role="region" aria-labelledby="header-1" id="panel-1">
  <p>面板内容...</p>
</div>

三、键盘交互规范

3.1 基本键盘操作

按键 功能
Enter 或 Space 当焦点位于折叠面板的手风琴标题上时,展开关联面板。如果实现只允许一个面板展开,且另一个面板已展开,则折叠该面板
Tab 将焦点移动到下一个可聚焦元素;手风琴中的所有可聚焦元素都包含在页面 Tab 序列中
Shift + Tab 将焦点移动到上一个可聚焦元素;手风琴中的所有可聚焦元素都包含在页面 Tab 序列中

3.2 可选键盘操作

按键 功能
Down Arrow 如果焦点在手风琴标题上,将焦点移动到下一个手风琴标题。如果焦点在最后一个手风琴标题上,要么不执行任何操作,要么将焦点移动到第一个手风琴标题
Up Arrow 如果焦点在手风琴标题上,将焦点移动到上一个手风琴标题。如果焦点在第一个手风琴标题上,要么不执行任何操作,要么将焦点移动到最后一个手风琴标题
Home 当焦点在手风琴标题上时,将焦点移动到第一个手风琴标题
End 当焦点在手风琴标题上时,将焦点移动到最后一个手风琴标题

四、实现方式

4.1 基础结构

<div class="accordion">
  <!-- 第一部分 -->
  <h3>
    <button 
      aria-expanded="true" 
      aria-controls="section1"
      id="accordion-header-1">
      第一部分标题
    </button>
  </h3>
  <div 
    id="section1" 
    role="region" 
    aria-labelledby="accordion-header-1">
    <p>第一部分的内容...</p>
  </div>

  <!-- 第二部分 -->
  <h3>
    <button 
      aria-expanded="false" 
      aria-controls="section2"
      id="accordion-header-2">
      第二部分标题
    </button>
  </h3>
  <div 
    id="section2" 
    role="region" 
    aria-labelledby="accordion-header-2"
    hidden>
    <p>第二部分的内容...</p>
  </div>
</div>

4.2 单展开模式

在单展开模式下,一次只能展开一个面板:

<div class="accordion" data-accordion-single>
  <h3>
    <button 
      aria-expanded="true" 
      aria-controls="panel-1"
      aria-disabled="true">
      始终展开的面板
    </button>
  </h3>
  <div id="panel-1" role="region">
    <p>此面板无法折叠...</p>
  </div>
  
  <h3>
    <button 
      aria-expanded="false" 
      aria-controls="panel-2">
      可切换的面板
    </button>
  </h3>
  <div id="panel-2" role="region" hidden>
    <p>点击上方标题可展开此面板...</p>
  </div>
</div>

4.3 多展开模式

在多展开模式下,可以同时展开多个面板:

<div class="accordion" data-accordion-multiple>
  <h3>
    <button aria-expanded="true" aria-controls="multi-1">
      第一个面板
    </button>
  </h3>
  <div id="multi-1" role="region">
    <p>第一个面板内容...</p>
  </div>
  
  <h3>
    <button aria-expanded="true" aria-controls="multi-2">
      第二个面板(也可同时展开)
    </button>
  </h3>
  <div id="multi-2" role="region">
    <p>第二个面板内容...</p>
  </div>
</div>

4.4 使用原生 HTML <details> + name 实现

HTML5.2 起,<details> 元素支持 name 属性,可以实现原生的单展开模式(Accordion 效果),无需 JavaScript:

<details name="accordion-group" open>
  <summary>第一部分标题</summary>
  <p>第一部分的内容...</p>
</details>

<details name="accordion-group">
  <summary>第二部分标题</summary>
  <p>第二部分的内容...</p>
</details>

<details name="accordion-group">
  <summary>第三部分标题</summary>
  <p>第三部分的内容...</p>
</details>
关键点说明
特性 说明
name 属性 相同 name 值的 <details> 元素会互斥,实现单展开
open 属性 指定默认展开的面板
浏览器支持 Chrome 120+, Firefox, Safari 17.1+
增强版实现(添加 heading 结构)

⚠️ 注意<details> 元素的实现方式与 W3C Accordion Pattern 的 DOM 结构要求不完全一致。W3C 标准要求按钮元素必须是 heading 元素内部的唯一子元素(<h3><button>...</button></h3>),而 <details> 使用 <summary> 作为交互元素。

如果需要更好的无障碍支持,可以在 <summary> 内添加标题:

<details name="accordion-group" open>
  <summary>
    <h3 style="display: inline; font-size: inherit;">第一部分标题</h3>
  </summary>
  <p>第一部分的内容...</p>
</details>

重要提示:这种结构虽然添加了 heading,但仍然是 heading 在 summary 内部,与 W3C 要求的 button 在 heading 内部 的结构相反。因此,这种方式:

  • ✅ 提供了基本的标题层级信息
  • ❌ 不完全符合 W3C Accordion Pattern 的 DOM 结构规范
  • ❌ 可能不被某些屏幕阅读器正确识别为手风琴组件
适用场景

推荐使用 <details name>

  • 简单的 FAQ 页面
  • 不需要复杂样式的场景
  • 追求原生、轻量实现
  • 现代浏览器环境

推荐使用 W3C 模式:

  • 需要多展开模式
  • 需要箭头键导航
  • 需要精确的标题层级(SEO/屏幕阅读器)
  • 需要复杂的自定义样式

五、常见应用场景

5.1 表单分步填写

将长表单分成多个部分,用户逐步填写:

<div class="accordion">
  <h3>
    <button aria-expanded="true" aria-controls="step-1">
      步骤 1:个人信息
    </button>
  </h3>
  <div id="step-1" role="region">
    <label>姓名 <input type="text" /></label>
    <label>邮箱 <input type="email" /></label>
  </div>
  
  <h3>
    <button aria-expanded="false" aria-controls="step-2">
      步骤 2:地址信息
    </button>
  </h3>
  <div id="step-2" role="region" hidden>
    <label>城市 <input type="text" /></label>
    <label>邮编 <input type="text" /></label>
  </div>
</div>

5.2 FAQ 页面

常见问题解答页面,每个问题作为一个可展开的部分:

<div class="accordion">
  <h3>
    <button aria-expanded="false" aria-controls="faq-1">
      如何注册账户?
    </button>
  </h3>
  <div id="faq-1" role="region" hidden>
    <p>点击页面右上角的"注册"按钮,填写必要信息...</p>
  </div>
  
  <h3>
    <button aria-expanded="false" aria-controls="faq-2">
      如何重置密码?
    </button>
  </h3>
  <div id="faq-2" role="region" hidden>
    <p>点击登录页面的"忘记密码"链接...</p>
  </div>
</div>

5.3 设置面板

应用程序的设置页面,将相关设置分组:

<div class="accordion">
  <h3>
    <button aria-expanded="true" aria-controls="settings-general">
      通用设置
    </button>
  </h3>
  <div id="settings-general" role="region">
    <label><input type="checkbox" /> 启用通知</label>
    <label><input type="checkbox" /> 自动保存</label>
  </div>
  
  <h3>
    <button aria-expanded="false" aria-controls="settings-privacy">
      隐私设置
    </button>
  </h3>
  <div id="settings-privacy" role="region" hidden>
    <label><input type="checkbox" /> 公开个人资料</label>
    <label><input type="checkbox" /> 允许搜索</label>
  </div>
</div>

六、最佳实践

6.1 语义化标记

  • 使用适当的标题层级(h1-h6)包装手风琴标题按钮
  • 为每个面板添加 role="region" 以增强结构感知(面板数量较少时)
  • 确保按钮元素是标题元素内部的唯一元素

6.2 键盘导航

  • 实现基本的 Enter/Space 和 Tab 导航
  • 可选实现箭头键导航以提升用户体验
  • 确保所有手风琴标题都包含在 Tab 序列中

6.3 视觉指示

  • 使用清晰的视觉指示器表示展开/折叠状态
  • 为当前聚焦的标题提供明显的焦点样式
  • 考虑使用动画过渡提升用户体验

6.4 状态管理

  • 明确区分单展开和多展开模式
  • 在单展开模式中,考虑是否允许所有面板同时折叠
  • 使用 aria-disabled 表示不允许折叠的面板

6.5 嵌套考虑

  • 避免过深的嵌套层级
  • 嵌套手风琴时,确保每个层级有清晰的视觉区分
  • 考虑使用不同的标题层级表示嵌套关系

七、Accordion 与 Disclosure 的区别

特性 Accordion Disclosure
内容组织 多个垂直堆叠的面板 单个内容块
展开模式 支持单展开或多展开 独立控制
标题结构 使用 heading + button 结构 简单按钮或 summary
导航支持 支持箭头键导航 基本 Tab 导航
用途 表单分步、设置面板、FAQ 详细信息展示

八、总结

构建无障碍的 Accordion 组件需要关注三个核心:正确的语义化标记(heading + button 结构)、完整的键盘交互支持(包括可选的箭头键导航)、清晰的状态管理(aria-expanded、aria-controls、aria-disabled)。与简单的 Disclosure 不同,Accordion 强调多个面板的组织和管理,适用于更复杂的内容展示场景。

遵循 W3C Accordion Pattern 规范,我们能够创建既美观又包容的手风琴组件,为不同能力的用户提供一致的体验。

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

构建无障碍组件之Disclosure Pattern

Disclosure (Show/Hide) Pattern 详解:构建无障碍展开收起

展开收起(Disclosure)是一种常见的交互组件,也被称为 Collapse(折叠),允许内容在折叠(隐藏)和展开(可见)状态之间切换。本文基于 W3C WAI-ARIA Disclosure Pattern 规范,详解如何构建无障碍的展开收起组件。

一、Disclosure 的定义与核心功能

Disclosure(展开收起)是一种控件,允许内容在折叠(隐藏)和展开(可见)状态之间切换。它包含两个基本元素:控制展开收起的按钮和其控制可见性的内容区域。

当内容被隐藏时,按钮通常设计为带有右指箭头或三角形的按钮,暗示激活按钮将显示更多内容。当内容可见时,箭头或三角形通常向下指向。

二、WAI-ARIA 角色与属性

2.1 基本角色

role="button" 用于标识控制展开收起的按钮元素。

2.2 状态属性

aria-expanded 属性表示内容的展开状态:

  • 当内容可见时,按钮的 aria-expanded 设置为 true
  • 当内容隐藏时,按钮的 aria-expanded 设置为 false

2.3 控制关系

对于手动实现的 Disclosure(例如使用按钮),可选地使用 aria-controls 属性来引用包含所有展开/收起内容的元素:

<button
  role="button"
  aria-expanded="false"
  aria-controls="disclosure-content">
  展开更多信息
</button>

<div
  id="disclosure-content"
  class="hidden">
  <p>这里是被控制的展开内容...</p>
</div>

三、键盘交互规范

当展开收起控件获得焦点时:

按键 功能
Enter 激活展开收起控件,切换内容可见性
Space 激活展开收起控件,切换内容可见性

四、实现方式

4.1 原生 details/summary 元素

HTML5 的 <details><summary> 元素是推荐的实现方式,内置无障碍支持:

  • 自动状态管理:浏览器自动处理展开/收起状态
  • 内置键盘支持:自动支持 Enter 和 Space 键
  • 语义化标签:提供原生的无障碍语义
<details>
  <summary>点击展开/收起</summary>
  <p>这里是展开的内容...</p>
</details>

注意:当使用原生 <details><summary> 元素时,不需要添加 aria-controlsrole="button",因为浏览器会自动处理这些属性和语义。

4.2 按钮 + ARIA 实现

使用按钮和 ARIA 属性的手动实现方式(当不能使用原生 <details> 元素时):

<button
  role="button"
  aria-expanded="false"
  aria-controls="faq-content"
  onclick="toggleDisclosure('faq-content', this)">
  常见问题解答
</button>

<div
  id="faq-content"
  class="disclosure-content hidden">
  <p>FAQ 内容...</p>
</div>

五、常见应用场景

5.1 图片描述展开 (Image Description)

用于显示图片的详细描述信息:

<details>
  <summary>查看图片描述</summary>
  <img
    src="image.jpg"
    alt="图片描述" />
  <p>这是对图片的详细描述...</p>
</details>

5.2 FAQ 展开收起 (Answers to Frequently Asked Questions)

用于常见问题解答的逐条展开:

<details>
  <summary>问题一:如何注册账户?</summary>
  <p>回答:点击注册按钮...</p>
</details>

<details>
  <summary>问题二:如何重置密码?</summary>
  <p>回答:点击忘记密码...</p>
</details>

5.3 导航菜单展开 (Navigation Menu)

用于移动端导航菜单的展开收起:

<nav>
  <details>
    <summary>菜单</summary>
    <ul>
      <li><a href="#home">首页</a></li>
      <li><a href="#about">关于我们</a></li>
      <li><a href="#contact">联系我们</a></li>
    </ul>
  </details>
</nav>

5.4 带顶级链接的导航菜单 (Navigation Menu with Top-Level Links)

在导航菜单中同时包含展开子项和直接链接:

<nav>
  <details>
    <summary>产品</summary>
    <ul>
      <li><a href="#product-a">产品 A</a></li>
      <li><a href="#product-b">产品 B</a></li>
    </ul>
  </details>
  <a href="#services">服务</a>
  <a href="#about">关于我们</a>
</nav>

5.5 展开卡片 (Disclosure Card)

将展开收起功能集成到卡片组件中:

<details class="card">
  <summary class="card-header">
    <h3>项目信息</h3>
  </summary>
  <div class="card-content">
    <p>这里是项目的详细信息...</p>
    <ul>
      <li>开始日期:2023年1月1日</li>
      <li>结束日期:2023年12月31日</li>
      <li>负责人:张三</li>
    </ul>
  </div>
</details>

六、最佳实践

6.1 语义化标记

优先使用原生的 <details><summary> 元素,它们提供完整的语义和无障碍支持。

6.2 组件命名

在实际开发中,Disclosure 模式可能以不同名称出现:

  • Collapse:在许多 UI 库(如 Bootstrap、Ant Design、Element UI)中的常见名称
  • Accordion:当多个 Disclosure 组件垂直堆叠时的特例
  • Expand/Collapse:更直白的功能描述
  • Show/Hide:强调内容可见性的变化

尽管名称不同,其核心行为和无障碍要求保持一致。

6.3 状态指示

使用视觉指示器(如箭头方向)来表明当前展开状态:

  • 收起状态:右指箭头或向右三角形
  • 展开状态:下指箭头或向下三角形

6.4 平滑过渡

添加 CSS 过渡效果提升用户体验:

.disclosure-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
}

.disclosure-content.expanded {
  max-height: 500px; /* 或适当的最大高度 */
}

6.5 可访问性考虑

  • 确保按钮具有清晰的焦点指示
  • 提供足够的点击区域(至少 44x44px)
  • 为屏幕阅读器用户提供明确的状态反馈

七、与类似模式的区别

特性 Disclosure Accordion Tabs
内容组织 单个内容块 多个面板垂直排列 多个面板水平排列
展开方式 单击切换 单击展开,其他收起 单击切换标签
用途 详细信息展示 FAQ、设置面板 页面内容分组

八、总结

构建无障碍的展开收起组件需要关注三个核心:正确的 ARIA 属性声明、合理的键盘交互支持、清晰的视觉状态指示。原生 <details><summary> 元素简化了实现,但开发者仍需理解无障碍原理,确保所有用户都能顺利使用。

遵循 W3C Disclosure Pattern 规范,我们能够创建既美观又包容的展开收起组件,为不同能力的用户提供一致的体验。

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

构建无障碍组件之Dialog Pattern

Dialog (Modal) Pattern 详解:构建无障碍模态对话框

模态对话框是 Web 应用中常见的交互组件,用于在不离开当前页面的情况下展示重要信息或获取用户输入。本文基于 W3C WAI-ARIA Dialog Pattern 规范,详解如何构建无障碍的模态对话框。

一、Dialog 的定义与核心功能

Dialog(对话框)是覆盖在主窗口或其他对话框之上的窗口。模态对话框会阻断用户与底层内容的交互,直到对话框关闭。底层内容通常会被视觉遮挡或变暗,以明确当前焦点在对话框内。

与 Alert Dialog 不同,普通 Dialog 适用于各种需要用户交互的场景,如表单填写、信息展示、设置配置等。它不强调紧急性,用户可以自主决定是否与之交互。

二、Dialog 的关键特性

模态对话框具有以下核心特性:

焦点限制:对话框包含独立的 Tab 序列,Tab 和 Shift+Tab 仅在对话框内循环,不会移出对话框外部。

背景禁用:对话框背后的内容处于 inert 状态,用户无法与之交互。尝试与背景交互通常会导致对话框关闭。

层级管理:对话框可以嵌套,新的对话框覆盖在旧对话框之上,形成层级结构。

三、WAI-ARIA 角色与属性

3.1 基本角色

role="dialog" 是对话框的基础角色,用于标识模态或非模态对话框元素。

aria-modal="true" 明确告知辅助技术这是一个模态对话框,背景内容当前不可用。

3.2 标签与描述

aria-labelledby 引用对话框标题元素,为对话框提供可访问名称。

aria-describedby 引用包含对话框主要内容的元素,帮助屏幕阅读器用户理解对话框目的。

<dialog
  role="dialog"
  aria-modal="true"
  aria-labelledby="dialog-title"
  aria-describedby="dialog-desc">
  <h2 id="dialog-title">用户设置</h2>
  <p id="dialog-desc">请配置您的个人偏好设置。</p>
  <!-- 对话框内容 -->
</dialog>

四、键盘交互规范

4.1 焦点管理

打开对话框时:焦点应移动到对话框内的某个元素。通常移动到第一个可聚焦元素,但根据内容不同可能有不同策略:

  • 内容包含复杂结构(列表、表格)时,可将焦点设置在内容的静态元素上,便于用户理解
  • 内容较长时,将焦点设置在标题或顶部段落,避免内容滚动出视野
  • 简单确认对话框,焦点可设置在主要操作按钮

关闭对话框时:焦点应返回到触发对话框的元素,除非该元素已不存在。

4.2 键盘操作

按键 功能
Tab 移动到对话框内下一个可聚焦元素,到达末尾时循环到第一个
Shift + Tab 移动到对话框内上一个可聚焦元素,到达开头时循环到最后一个
Escape 关闭对话框
// 焦点管理示例
function openDialog(dialog, triggerElement) {
  dialog.triggerElement = triggerElement;
  dialog.showModal();

  // 将焦点设置到第一个可聚焦元素或标题
  const focusable = dialog.querySelector(
    'button, [href], input, select, textarea',
  );
  if (focusable) {
    focusable.focus();
  }
}

function closeDialog(dialog) {
  dialog.close();
  // 恢复焦点到触发元素
  if (dialog.triggerElement) {
    dialog.triggerElement.focus();
  }
}

五、实现方式

5.1 原生 dialog 元素

HTML5 <dialog> 元素是推荐实现方式,内置模态行为和无障碍支持:

  • 自动焦点管理showModal() 自动将焦点移动到对话框内第一个可聚焦元素
  • 内置 ESC 关闭:用户按 ESC 键自动关闭对话框
  • 自动模态背景:自动创建背景遮罩,阻止与底层内容交互
  • 焦点循环:Tab 键在对话框内自动循环,不会移出对话框
  • 内置 ARIA 属性:浏览器自动处理 aria-modal 等属性
  • Top Layer 支持:模态对话框显示在浏览器顶层,不受 z-index 限制
<dialog
  id="settings-dialog"
  aria-labelledby="dialog-title">
  <div class="dialog-header">
    <h2 id="dialog-title">设置</h2>
    <button
      onclick="this.closest('dialog').close()"
      aria-label="关闭"></button>
  </div>
  <div class="dialog-content">
    <label>
      用户名
      <input type="text" />
    </label>
  </div>
  <div class="dialog-footer">
    <button onclick="this.closest('dialog').close()">取消</button>
    <button onclick="saveSettings()">保存</button>
  </div>
</dialog>

<button onclick="document.getElementById('settings-dialog').showModal()">
  打开设置
</button>

5.2 div + ARIA 实现

需要手动处理焦点管理和背景交互。这种方式适用于需要自定义动画、复杂布局或旧浏览器兼容的场景:

<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="dialog-title"
  class="modal-overlay">
  <div class="modal-content">
    <h2 id="dialog-title">确认操作</h2>
    <p>确定要执行此操作吗?</p>
    <button>取消</button>
    <button>确认</button>
  </div>
</div>

六、最佳实践

初始焦点策略和键盘交互的详细规范请参考 4.1 焦点管理。在实际应用中,建议遵循以下策略:

  • 信息展示:焦点设置在标题或内容开头,便于屏幕阅读器顺序阅读
  • 表单输入:焦点设置在第一个输入框
  • 确认操作:焦点设置在主操作按钮或取消按钮(视风险而定)

6.1 关闭方式

提供多种关闭方式提升用户体验:

  • ESC 键关闭(原生 dialog 自动支持)
  • 关闭按钮
  • 点击背景遮罩关闭(可选)
  • 明确的取消/确认按钮

6.2 嵌套对话框

支持多层对话框嵌套,每层新对话框覆盖在上层:

<dialog id="layer1">
  <button onclick="document.getElementById('layer2').showModal()">
    打开第二层
  </button>
</dialog>

<dialog id="layer2">
  <p>第二层对话框</p>
</dialog>

6.3 避免滥用

对话框会中断用户流程,应谨慎使用:

  • 优先使用非模态方式展示非关键信息
  • 避免对话框内再嵌套复杂导航
  • 保持对话框内容简洁,避免过多滚动

七、Dialog 与 Alert Dialog 的区别

特性 Dialog Alert Dialog
用途 一般交互、表单、配置 紧急确认、警告、错误
紧急性 非紧急 紧急,需立即响应
关闭方式 多种方式 通常只有确认/取消
角色 role="dialog" role="alertdialog"
系统提示音 可能有

八、总结

构建无障碍的模态对话框需要关注三个核心:正确的 ARIA 属性声明、合理的焦点管理、完整的键盘交互支持。原生 <dialog> 元素简化了实现,但开发者仍需理解无障碍原理,确保所有用户都能顺利使用。

遵循 W3C Dialog Pattern 规范,我们能够创建既美观又包容的对话框组件,为不同能力的用户提供一致的体验。

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

❌