一文掌握Web可访问性:前端开发者必读的包容性设计指南

137 阅读19分钟

一文了解Web的可访问性

作为前端开发者,我们常常沉浸在 UI 动效、性能优化和新技术栈的研究中,却忽视了一个直接影响 25%用户的重要问题——Web 可访问性。本文将深入解析前端可访问性的核心要点及具体实现。

图片

可访问性是什么?

Web 可访问性是指通过设计和开发技术,确保网页或应用能够被所有人无障碍地访问和使用,包括残障人士、老年用户、临时受伤者(如手部受伤)或使用特殊设备(如屏幕阅读器、语音控制工具)的人群。其核心目标是消除访问障碍,实现信息平等。

Web 可访问性的重要性:

  • 法律合规:许多国家和地区(如欧盟、美国)要求公共网站必须符合可访问性标准(如 WCAG),否则可能面临法律风险。如果你的应用用户人群是这些国家的,就需要特别关注开发中的可访问性。
  • 用户体验:提升所有用户的操作便利性,例如清晰的导航、高对比度配色对普通用户同样友好。
  • SEO 优化:可访问性实践(如语义化 HTML)通常与搜索引擎优化(SEO)兼容,提升搜索排名。

WCAG

WCAG(Web Content Accessibility Guidelines,网页内容可访问性指南)是由 W3C(万维网联盟)制定的国际标准,旨在确保网页内容对所有人均可访问和使用。

根据 Web 内容可访问性指南(WCAG),Web 可访问性可以归纳为以下四个核心原则

  • 可感知: 用户必须能够感知提供的信息。
  • 文本替代:为非文本内容(如图片、图表)提供 alt 文本或描述。
  • 多媒体可访问:为视频提供字幕,为音频提供文本转录。
  • 适配性:内容在不同设备(如屏幕阅读器、移动端)上可正常显示。
  • 可操作: 用户界面组件和导航必须是可操作的。
  • 键盘导航:所有功能可通过键盘(Tab、Enter、方向键)操作。
  • 避免闪烁内容:防止快速闪烁的动画引发光敏性癫痫。
  • 足够时间:用户有充足时间阅读或操作(如表单填写倒计时可延长)。
  • 可理解: 用户必须能够理解和使用信息和用户界面。
  • 一致性:导航、标签、按钮功能保持一致。
  • 错误提示:表单输入错误时提供明确的文字说明和修正建议。
  • 语言明确:避免使用复杂术语,页面语言通过 lang 属性声明。
  • 健壮: 内容必须兼容当前和未来的技术。
  • 语义化 HTML:使用标准标签(如 <button><nav>),避免滥用 <div> 模拟控件。
  • ARIA 支持:为动态内容补充 ARIA 属性(如 rolearia-label)。

WCAG 要求分为三个等级,从低到高依次为:

图片

可访问性的前端实现

Web 可访问性需要贯穿开发全流程,从前端技术实现到设计协作,再到测试验证。

语义化 HTML

根据内容的含义选择合适的 HTML 标签,这有助于屏幕阅读器等辅助技术理解页面结构,方便用户导航。

<!-- 错误:用 div 模拟按钮 -->
<div onclick="submit()">提交</div>

<!-- 正确:语义化按钮 -->
<button onclick="submit()">提交</button>

常见的语义化标签包括:

标签作用
<header>定义文档或节的页眉,包含介绍性内容,如网站标志、标题、导航栏等
<nav>专门定义导航链接部分,包含指向不同页面或同一页面不同部分的主要导航链接
<main>规定文档的主要内容,该内容应独一无二,不包含重复出现的内容
<article>表示文档中独立、完整且可独立分发的内容块,如博客文章、新闻报道等
<section>对文档内容进行分块,将相似主题的内容分组,不一定能独立存在
<aside>定义所处内容之外的内容,常表现为侧边栏或插入内容,与周围内容相关但非主要部分
<footer>定义文档或节的页脚,包含版权信息、作者信息、联系方式等
<h1> - <h6>定义不同级别的标题,构建文档大纲结构,利于搜索引擎理解及用户浏览
<ul>创建无序列表,列表项无特定顺序,以项目符号显示
<ol>创建有序列表,列表项按数字或字母顺序排列,适用于强调顺序的内容
<dl>创建定义列表,包含<dt>(定义术语)和<dd>(定义描述)对,展示术语及其定义
<blockquote>定义从其他来源引用的内容,视觉上常呈现为缩进,可搭配<cite>指定引用来源
<q>定义短的行内引用,浏览器自动在两端加引号
<time>定义日期和时间,方便浏览器和搜索引擎识别,可通过datetime属性指定具体值

语义化 HTML 的好处:

  • 提高可访问性: 语义化 HTML 可以帮助辅助技术(如屏幕阅读器)更好地理解页面结构和内容,从而为残障用户提供更好的体验。
  • 增强 SEO(搜索引擎优化): 搜索引擎爬虫依赖 HTML 标签来理解网页的内容和结构。使用语义化 HTML 可以使搜索引擎更准确地抓取和索引页面,有助于提高搜索排名。
  • 改善代码可读性和维护性: 语义化 HTML 使代码更具结构性和逻辑性,易于理解和维护。
  • 符合 Web 标准和最佳实践: 语义化 HTML 符合 W3C 和其他标准组织推荐的最佳实践,有助于构建高质量的 Web 应用。

键盘导航

键盘导航是 Web 可访问性的重要组成部分,它允许用户仅使用键盘来浏览和操作网页,这对于那些无法使用鼠标或其他指针设备的用户(如肢体残障人士、键盘快捷方式偏好者)来说至关重要。

可聚焦元素

可聚焦元素是指用户可以通过键盘的 Tab 键将焦点移动到这些元素上的元素,并且焦点顺序应符合逻辑。常见的可聚焦元素包括链接(<a>)、按钮(<button>)、输入框(<input><textarea>)、下拉框(<select>)等。

<a href="#">前端充电宝</a>
<button>点击我</button>
<input type="text"placeholder="输入内容">
<select>
<option value="1">选项1</option>
<option value="2">选项2</option>
</select>

在上述代码中,用户可以使用 Tab 键依次将焦点移动到链接、按钮、输入框和下拉框上。

焦点顺序
  • 自然顺序:键盘焦点的移动顺序应该与网页内容的逻辑和视觉顺序一致。通常,焦点会按照 HTML 文档中元素出现的先后顺序进行移动。例如,在一个表单中,焦点会从第一个输入框开始,依次移动到后续的输入框、按钮等元素。
  • 调整顺序:在某些情况下,可能需要调整焦点顺序。可以使用 tabindex 属性来实现。
  • tabindex="0":将元素添加到正常的 Tab 顺序中,即使该元素默认不可聚焦。
  • tabindex="-1":使元素可聚焦,但不包含在正常的 Tab 顺序中,可以通过 JavaScript 等方式将焦点设置到该元素上。
  • tabindex="正数":指定元素的焦点顺序,数值越小越先获得焦点,但不建议过多使用这种方式,因为可能会破坏自然的焦点顺序。

焦点可见性

当元素获得焦点时,应该有明显的视觉指示,以便用户清楚地知道当前焦点所在的位置。可以通过修改元素的样式(如改变边框颜色、添加背景色、显示下划线等)来实现。

button:focus,
 input:focus,
 a:focus {
  outline: 2px solid #007BFF; /* 蓝色边框 */
  box-shadow: 005pxrgba (0, 123, 255, 0.5); /* 添加阴影效果 */
}

注意事项:

  • 避免移除默认的outline样式,除非提供了更好的替代方案。
  • 提供高对比度的颜色,确保视觉上有足够的可见性。

键盘操作支持

  • 常见操作:不同类型的元素应该支持相应的键盘操作。例如:
  • 链接:按下 Enter 键可以激活链接,跳转到相应的页面。
  • 按钮:按下 Enter 键或 Space 键可以触发按钮的点击事件。
  • 输入框:可以使用方向键在输入框内移动光标,按下 Enter 键在某些情况下可以提交表单。
  • 下拉框:使用方向键可以选择下拉框中的选项,按下 Enter 键可以确认选择。
  • 自定义元素的键盘操作:对于自定义的交互元素(如自定义菜单、对话框等),需要通过 JavaScript 来实现相应的键盘操作支持。例如,一个自定义菜单可以通过监听键盘事件,根据用户按下的方向键来切换菜单项的选中状态,按下 Enter 键来执行相应的操作。

跳过导航

对于一些大型网页,可能存在大量的导航链接和重复的内容,用户可能希望快速跳过这些内容,直接访问主要内容。这时可以提供 “跳过导航” 链接。

实现方法:在页面顶部添加一个隐藏的链接,当用户使用键盘将焦点移动到该链接时,链接显示出来,用户按下 Enter 键可以直接跳转到页面的主要内容区域。

<a href="#main-content" class="skip-nav">跳过导航</a>
<header>
  <nav>
    <ul>
      <li><a href="#">首页</a></li>
      <li><a href="#">产品</a></li>
      <li><a href="#">关于我们</a></li>
    </ul>
  </nav>
</header>
<main id="main-content">
  <h1>主要内容标题</h1>

  <p>主要内容文本...</p>
</main>
.skip-nav {
  position: absolute;
  top: -40px;
  left: 0;
  background: #fff;
  padding: 8px;
  border: 1px solid #ccc;
  transition: top 0.3s ease;
}

.skip-nav:focus {
  top: 0;
}

这样,当用户使用键盘将焦点移动到该链接时,链接会显示出来,按下 Enter 键可以直接跳转到页面的主要内容区域。

测试验证

  • 手动测试: 使用键盘在网页上进行导航,检查焦点顺序是否正确、焦点可见性是否明显、元素的键盘操作是否正常等。
  • Tab键测试:通过Tab键逐个测试页面上的交互元素,确保每个元素都可以被聚焦并操作。
  • Shift + Tab:测试反向导航,确保焦点顺序正确。
  • Enter和Space 键:测试按钮、链接等交互元素是否可以通过Enter或Space键激活。
  • Esc键:测试模态对话框、下拉菜单等是否可以通过Esc键关闭。
  • 自动化测试:使用 axe-core 工具库进行检查。

ARIA 属性

ARIA(Accessible Rich Internet Applications)属性是 HTML5 规范的一部分,旨在帮助开发者增强网页的可访问性,特别是在处理动态内容和复杂用户界面时。通过使用ARIA属性,可以为屏幕阅读器和其他辅助技术提供更多的信息,从而提升用户体验。

ARIA 属性主要分为三类:角色、状态、属性。

角色

角色定义了元素在页面中的功能或用途,常见的角色包括buttonmenudialog等。

  • role="button":将一个普通的 <div> 元素定义为按钮,使其具有按钮的语义。
<div role="button"tabindex="0">前端充电宝</div>
  • role="menu"role="menuitem":用于创建自定义菜单,明确菜单和菜单项的角色。
<ul role="menu">
    <li role="menuitem">\<a href="#">选项 1</a></li>
    <li role="menuitem">\<a href="#">选项 2</a></li>
</ul>
  • role="dialog":用于定义一个对话框。
<div id="dialog"role="dialog"aria-modal="true">
    <h2>标题</h2>
    <p>内容</p>
    <button id="closeDialog">关闭</button>
</div>

注意事项:

  • 尽量使用原生HTML元素(如<button><nav>等),它们自带语义化角色。
  • 只有在必要时才使用role属性来增强自定义组件的可访问性。

状态

状态表示元素当前的状态,如是否选中、是否展开等。常见的状态包括aria-checkedaria-expanded等。

  • aria-checked:用于复选框和单选框,指示其选中状态。
<input type="checkbox"id="check-box"aria-checked="false">
<label for="check-box">选择</label>
  • aria - expanded:用于可展开 / 折叠的元素,如菜单、手风琴组件等,指示其展开状态。
<button aria-controls="content"aria-expanded="false">展开内容</button>
<div id="content"hidden>具体内容</div>

属性

属性提供了额外的信息,如标签、描述等。常见的属性包括aria-labelaria-labelledbyaria-describedby等。

  • aria-label:为元素提供一个明确的标签,用于替代元素内部的文本,特别是当元素没有可见文本时,适用于简单的替代文本。
<input type="search"aria-label="搜索框">
<button aria-label="关闭菜单">X</button>
  • aria-labelledby:引用其他元素的ID作为标签来源,适用于引用现有文本作为标签的情况。
<h1 id="page-title">网站</h1>
<nav aria-labelledby="page-title">
  <ul>
    <li><a href="#home">首页</a></li>
    <li><a href="#about">关于我们</a></li>
  </ul>
</nav>
  • aria-describedby用于为元素提供额外的描述信息,通常与表单验证错误消息一起使用。
<label for="email">邮箱地址:</label>
<input type="email"id="email"name="email"aria-invalid="true"aria-describedby="email-error">
<span id="email-error"class="error">请输入有效的邮箱地址</span>

多媒体内容

确保多媒体内容对所有用户都是可访问的,包括视力障碍者、听力障碍者以及其他有特殊需求的用户,是创建包容性网站的重要部分。

图片可访问性

  • 替代文本: 为图像提供替代文本(alt属性),以便屏幕阅读器能够描述图像的内容。需要注意:
  • 提供有意义的alt属性,避免使用空字符串(除非图像是纯粹装饰性的)。
  • 对于装饰性图像,可以使用alt=""来告诉屏幕阅读器忽略该图像。
<img src="logo.png"alt="公司 Logo">
  • 长描述: 对于复杂的图像或图表,简单的alt属性可能不足以描述其全部内容,此时可以使用 <figcaption> 标签结合 <figure> 标签为图像添加说明。
<figure>
  <img src="complex_graph.png" alt="一张复杂的销售数据折线图" />
  <figcaption>
    该折线图展示了过去一年中不同季度的产品销售数据变化趋势。
  </figcaption>
</figure>

视频可访问性

  • 字幕: 为视频添加字幕,帮助听力障碍者理解视频内容,也有助于在嘈杂环境或用户选择静音时理解视频内容。可以在 <video> 标签中使用 <track> 标签添加字幕文件,kind 属性设置为 captions
<video controls>
  <source src="example.mp4" type="video/mp4" />
  <track kind="captions" src="captions.vtt" srclang="zh" label="中文字幕" />
</video>
  • 音频描述: 音频描述是对视频中视觉内容的口头描述,帮助视力障碍者理解视频中的重要视觉元素。同样使用 <track> 标签来实现,kind 属性设置为 descriptions
<video controls>
  <source src="video.mp4" type="video/mp4" />
  <track
    kind="descriptions"
    src="descriptions.vtt"
    srclang="zh"
    label="视频描述"
  />
</video>
  • 控制和交互: 提供易于使用的视频控制功能,让所有用户都能方便地播放、暂停、调整音量、快进等。可以在 <video> 标签中添加 controls 属性,显示默认的视频控制条。也可以通过 JavaScript 自定义控制界面,但要确保其可通过键盘操作。
<video id="myVideo" controls>
  <source src="example.mp4" type="video/mp4" />
</video>

<script>
  const video = document.getElementById("myVideo");
  // 添加键盘事件监听器
  document.addEventListener("keydown", (e) => {
    if ((e.key = " ")) {
      // 空格键播放/暂停
      e.preventDefault();
      if (video.paused) {
        video.play();
      } else {
        video.pause();
      }
    } else if ((e.key = "ArrowLeft")) {
      // 左箭头后退5秒
      video.currentTime -= 5;
    } else if (e.key === "ArrowRight") {
      // 右箭头前进5秒
      video.currentTime += 5;
    }
  });
</script>

音频可访问性

  • 音频转录: 为听障用户或在无法播放音频的情况下提供音频内容的文字版本。
<audio controls>
  <source src="audio.mp3" type="audio/mpeg" />
</audio>

<p>点击 <a href="audio_transcription.txt">这里</a> 查看音频转录文本。</p>
  • 音频控制: 确保音频播放器的控制按钮可以通过键盘操作,并且对屏幕阅读器友好。
<audio id="myAudio" controls>
  <source src="example.mp3" type="audio/mpeg" />
</audio>
<script>
  const audio = document.getElementById("myAudio");
  // 添加键盘事件监听器
  document.addEventListener("keydown", (e) => {
    if ((e.key = " ")) {
      // 空格键播放/暂停
      e.preventDefault();
      if (audio.paused) {
        audio.play();
      } else {
        audio.pause();
      }
    } else if ((e.key = "ArrowLeft")) {
      // 左箭头后退5秒
      audio.currentTime -= 5;
    } else if (e.key === "ArrowRight") {
      // 右箭头前进5秒
      audio.currentTime += 5;
    }
  });
</script>

整体策略

  • 自动播放限制: 避免自动播放多媒体内容,特别是带有声音的视频或音频,以免干扰用户的体验。可以使用preload="none"preload="metadata"来延迟加载视频,直到用户明确请求播放。
<video controlspreload="none">
  <source src="example.mp4" type="video/mp4" />
</video>
  • 焦点管理: 确保当多媒体内容加载或动态更新时,焦点正确管理,不会导致用户失去当前的操作上下文。

表单设计

设计一个易于理解和使用的表单是提升用户体验和可访问性的关键。

结构与布局

  • 清晰的层次结构: 确保表单有一个清晰的层次结构,使用户能够轻松理解每个部分的目的和内容。可以使用<fieldset><legend>来分组相关的表单元素,并且确保每个表单部分都有明确的标题或说明。

<form id="registrationForm">
  <fieldset>
    <legend>注册信息</legend>
    <label for="username">用户名:</label>
    <input type="text" id="username" name="username" required />
    <label for="email">邮箱地址:</label>
    <input type="email" id="email" name="email" required />
    <label for="password">密码:</label>
    <input type="password" id="password" name="password" required />
  </fieldset>

  <fieldset>
    <legend>联系方式</legend>
    <label for="phone">电话号码:</label>
    <input type="tel" id="phone" name="phone" />
    <label for="address">地址:</label>
    <textarea id="address" name="address"></textarea>
  </fieldset>

  <button type="submit">提交</button>
</form>

  • 逻辑顺序: 按照用户的自然填写顺序排列表单字段,避免跳跃式布局。
<label for="firstName">名字:</label>
<input type="text" id="firstName" name="firstName" required />
<label for="lastName">姓氏:</label>
<input type="text" id="lastName" name="lastName" required />
<label for="email">邮箱地址:</label>
<input type="email" id="email" name="email" required />

标签

  • 使用<label>标签: 为每个表单控件提供明确的标签,以便屏幕阅读器能够正确读取,可以使用for属性将标签与相应的输入字段关联起来。
<label for="email">邮箱地址:</label>
<input type="email" id="email" name="email" required />
  • 隐藏标签: 对于某些装饰性或视觉上不必要的标签,可以使用CSS将其隐藏,但仍然对屏幕阅读器可见。
<label for="search" class="sr-only">搜索:</label>
<input type="search" id="search" name="search" placeholder="搜索..." />
<style>
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
  }
</style>

输入字段

  • 类型提示: 使用HTML5的输入类型提示(如emailteldate等),帮助浏览器提供更好的输入体验(如自动完成、键盘布局等)。
<input type="email"id="email"name="email"required>
<input type="tel"id="phone"name="phone">
<input type="date"id="birthday"name="birthday">
  • 占位符: 占位符文本可以帮助用户了解应该输入的内容,但不应替代标签。
<input type="email"id="email"name="email"placeholder="example@example.com"required>

表单验证

  • 客户端验证: 在用户提交表单之前进行客户端验证,以防止无效数据提交,可以使用 HTML5 内置的验证属性(如requiredpattern等)。
<form id="registrationForm">
  <label for="email">邮箱地址:</label>
  <input
    type="email"
    id="email"
    name="email"
    requiredaria-invalid="false"
    aria-describedby="email-error"
  />
  <span id="email-error" class="error"></span>
  <button type="submit">提交</button>
</form>

<script>
  const form = document.getElementById("registrationForm");
  const emailInput = document.getElementById("email");
  const emailError = document.getElementById("email-error");
  form.addEventListener("submit", (e) => {
    if (!emailInput.validity.valid) {
      e.preventDefault();
      emailInput.setAttribute("aria-invalid", "true");
      emailError.textContent = "请输入有效的邮箱地址";
      emailInput.focus();
    }
  });
</script>
  • 服务器端验证: 即使有客户端验证,仍需在服务器端进行验证,以确保数据的安全性和完整性。

即时反馈

  • 实时验证: 在用户输入过程中提供即时反馈,帮助他们在提交表单前纠正错误,可以使用aria-invalid属性指示输入的有效性状态。
<form id="registrationForm">
  <label for="email">邮箱地址:</label>
  <input
    type="email"
    id="email"
    name="email"
    requiredaria-invalid="false"
    aria-describedby="email-error"
  />
  <span id="email-error" class="error"></span>
  <button type="submit">提交</button>
</form>

<script>
  const emailInput = document.getElementById("email");
  const emailError = document.getElementById("email-error");
  emailInput.addEventListener("input", () => {
    if (!emailInput.validity.valid) {
      emailInput.setAttribute("aria-invalid", "true");
      emailError.textContent = "请输入有效的邮箱地址";
    } else {
      emailInput.setAttribute("aria-invalid", "false");
      emailError.textContent = "";
    }
  });
</script>

  • 成功反馈: 在用户成功提交表单后,提供明确的成功确认信息。

辅助功能

  • ARIA属性: 使用ARIA属性增强表单的可访问性,特别是在复杂交互中。
  • 使用aria-required指示必填字段。
  • 使用aria-describedby提供额外描述信息(如错误消息)。
<label for="email">邮箱地址:</label>
<input
  type="email"
  id="email"
  name="email"
  requiredaria-required="true"
  aria-describedby="email-error"
/>
<span id="email-error" class="error"></span>
  • 键盘导航: 确保表单可以通过键盘操作,并且焦点管理合理。
<form id="registrationForm">
  <label for="username">用户名:</label>
  <input type="text" id="username" name="username" required />
  <label for="email">邮箱地址:</label>
  <input type="email" id="email" name="email" required />
  <button type="submit">提交</button>
</form>

<script>
  document.addEventListener("keydown", (e) => {
    if (e.key === "Enter") {
      const focusedElement = document.activeElement;

      if (focusedElement.tagName.toLowerCase() === "button") {
        focusedElement.click();
      }
    }
  });
</script>

模态框

模态框是一种常见的UI组件,用于显示临时信息或要求用户进行特定操作。

HTML结构

模态框的基本结构应包含一个对话框容器和关闭按钮,并且使用适当的ARIA属性来定义其角色和状态。

  • 使用role="dialog"来标识模态框。
  • 使用aria-modal="true"来告知屏幕阅读器这是一个模态对话框,阻止背景内容的交互。
  • 提供明确的标题(aria-labelledby)和描述(aria-describedby),帮助用户理解对话框的内容。
<div
  id="myModal"
  role="dialog"
  aria-modal="true"
  aria-labelledby="modalTitle"
  aria-describedby="modalDescription"
  style="display: none"
>
  <h2 id="modalTitle">模态标题</h2>
  <p id="modalDescription">这是模态内容。</p>
  <button id="closeModal">关闭</button>
</div>

焦点管理

  • 初始焦点: 当模态框打开时,应将焦点移动到模态框内的第一个可聚焦元素上。如果模态框有多个可聚焦元素,选择最相关的元素作为初始焦点。
function openModal() {
  const modal = document.getElementById("myModal");
  modal.style.display = "block";
  // 将焦点移到关闭按钮上
  document.getElementById("closeModal").focus();
}
  • 模态框内焦点循环:在模态框打开时,焦点应限制在模态框内部,防止用户通过 Tab 键将焦点移出模态框,与页面其他部分交互。可以通过第三方库如 react-focus-lock来实现,也可以通过监听keydown事件,处理Tab键导航,确保焦点始终停留在模态框内。
function trapFocus(event) {
  const modal = document.getElementById("myModal");
  const focusableElements = modal.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  const firstFocusableElement = focusableElements[0];
  const lastFocusableElement = focusableElements[focusableElements.length - 1];
  if (event.shiftKey && document.activeElement === firstFocusableElement) {
    // Shift + Tab 从第一个元素移出
    lastFocusableElement.focus();
    event.preventDefault();
  } else if (
    !event.shiftKey &&
    document.activeElement === lastFocusableElement
  ) {
    // Tab 从最后一个元素移出
    firstFocusableElement.focus();
    event.preventDefault();
  }
}
document.addEventListener("keydown", trapFocus);
  • 关闭模态框时恢复焦点:当模态框关闭时,应将焦点恢复到打开模态框的触发元素上,以便用户可以继续之前的操作。
const closeModalButton = document.getElementById("close-modal");
closeModalButton.addEventListener("click", () => {
  modal.hidden = true;
  openModalButton.focus();
});

样式与视觉提示

  • 视觉焦点指示: 为可聚焦元素提供清晰的视觉焦点指示,帮助视力障碍者识别当前聚焦的位置。
button:focus,
input:focus,
select:focus,
textarea:focus {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}
  • 背景遮罩: 使用背景遮罩来模糊或隐藏背景内容,使模态框更加突出,背景遮罩应覆盖整个视口,并具有一定的透明度。
<div id="overlay" style="display: none"></div>

<div
  id="myModal"
  role="dialog"
  aria-modal="true"
  aria-labelledby="modalTitle"
  aria-describedby="modalDescription"
  style="display: none"
>
  <!-- 模态框内容 -->
</div>

<style>
  #overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 9998;
  }

  #myModal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: white;
    padding: 20px;
    z-index: 9999;
  }
</style>

<script>
  function openModal() {
    const overlay = document.getElementById("overlay");
    const modal = document.getElementById("myModal");
    overlay.style.display = "block";
    modal.style.display = "block";
    // 将焦点移到关闭按钮上
    document.getElementById("closeModal").focus();
  }

  function closeModal() {
    const overlay = document.getElementById("overlay");
    const modal = document.getElementById("myModal");
    overlay.style.display = "none";
    modal.style.display = "none";
    // 恢复之前的焦点
    if (previousActiveElement) {
      previousActiveElement.focus();
    }
  }
</script>

小结:可访问性不是功能,而是责任。通过实现这些技术细节,我们不仅满足 WCAG 标准,更是为所有用户构建真正的包容性网络。从下一个功能需求开始,将可访问性刻进你的开发 DNA。