无障碍访问 {#accessibility}

Web 无障碍访问 (也称为 a11y) 是指创建可供任何人使用的网站的做法——无论是身患某种障碍、通过慢速的网络连接访问、使用老旧或损坏的硬件,还是仅处于某种不方便的环境。例如,在视频中添加字幕可以帮助失聪、有听力障碍或身处嘈杂环境而听不到手机的用户。同样地,确保文字样式没有处于太低的对比度,可以对低视力用户和在明亮的强光下使用手机的用户都有所帮助。

你是否已经准备开始却又无从下手?

请先阅读由万维网联盟 (W3C) 提供的 Web 无障碍访问的规划和管理

跳过链接 {#skip-link}

你应该在每个页面的顶部添加一个直接指向主内容区域的链接,这样用户就可以跳过在多个网页上重复的内容。

通常这个链接会放在 App.vue 的顶部,这样它就会是所有页面上的第一个可聚焦元素:

  1. <ul class="skip-links">
  2. <li>
  3. <a href="#main" ref="skipLink">Skip to main content</a>
  4. </li>
  5. </ul>

若想在非聚焦状态下隐藏该链接,可以添加以下样式:

  1. .skipLink {
  2. white-space: nowrap;
  3. margin: 1em auto;
  4. top: 0;
  5. position: fixed;
  6. left: 50%;
  7. margin-left: -72px;
  8. opacity: 0;
  9. }
  10. .skipLink:focus {
  11. opacity: 1;
  12. background-color: white;
  13. padding: 0.5em;
  14. border: 1px solid black;
  15. }

一旦用户改变路由,请将焦点放回到这个“跳过”链接。通过如下方式聚焦“跳过”链接的模板 ref (假设使用了 vue-router)即可实现:

vue <script> export default { watch: { $route() { this.$refs.skipLink.focus() } } } </script>
vue <script setup> import { ref, watch } from 'vue' import { useRoute } from 'vue-router' const route = useRoute() const skipLink = ref() watch( () => route.path, () => { skipLink.value.focus() } ) </script>

阅读关于跳过链接到主要内容的文档

内容结构 {#content-structure}

确保设计可以支持易于访问的实现是无障碍访问最重要的部分之一。设计不仅要考虑颜色对比度、字体选择、文本大小和语言,还要考虑应用程序中的内容是如何组织的。

标题 {#headings}

用户可以通过标题在应用程序中进行导航。为应用程序的每个部分设置描述性标题,这可以让用户更容易地预测每个部分的内容。说到标题,有几个推荐的无障碍访问实践:

  • 按级别顺序嵌套标题:<h1> - <h6>
  • 不要在一个章节内跳跃标题的级别
  • 使用实际的标题标记,而不是通过对文本设置样式以提供视觉上的标题

阅读更多有关标题的信息

  1. <main role="main" aria-labelledby="main-title">
  2. <h1 id="main-title">Main title</h1>
  3. <section aria-labelledby="section-title">
  4. <h2 id="section-title"> Section Title </h2>
  5. <h3>Section Subtitle</h3>
  6. <!-- 内容 -->
  7. </section>
  8. <section aria-labelledby="section-title">
  9. <h2 id="section-title"> Section Title </h2>
  10. <h3>Section Subtitle</h3>
  11. <!-- 内容 -->
  12. <h3>Section Subtitle</h3>
  13. <!-- 内容 -->
  14. </section>
  15. </main>

Landmark {#landmarks}

Landmark 会为应用中的章节提供访问规划。依赖辅助技术的用户可以跳过内容直接导航到应用程序的每个部分。你可以使用 ARIA role 帮助你实现这个目标。

HTML ARIA Role 地标的目的
header role=”banner” 主标题:页面的标题
nav role=”navigation” 适合用作文档或相关文档导航的链接集合
main role=”main” 文档的主体或中心内容
footer role=”contentinfo” 关于父级文档的信息:脚注/版权/隐私声明链接
aside role=”complementary” 用来支持主内容,同时其自身的内容是相对独立且有意义的
无对应元素 role=”search” 该章节包含整个应用的搜索功能
form role=”form” 表单相关元素的集合
section role=”region” 相关的且用户可能会导航至此的内容。必须为该元素提供 label

:::tip 提示: 在使用地标 HTML 元素时,建议加上冗余的地标 role attribute,以最大限度地与传统不支持 HTML5 语义元素的浏览器兼容。 :::

阅读更多有关标题的细节

语义化表单 {#semantic-forms}

当创建一个表单,你可能使用到以下几个元素:<form><label><input><textarea><button>

标签通常放置在表格字段的顶部或左侧:

  1. <form action="/dataCollectionLocation" method="post" autocomplete="on">
  2. <div v-for="item in formItems" :key="item.id" class="form-item">
  3. <label :for="item.id">{{ item.label }}: </label>
  4. <input
  5. :type="item.type"
  6. :id="item.id"
  7. :name="item.id"
  8. v-model="item.value"
  9. />
  10. </div>
  11. <button type="submit">Submit</button>
  12. </form>

请注意这里我们是如何在表单元素中引入 autocomplete='on' 的,它将应用于表单中的所有 input 框。你也可以为每个 input 框都设置不同的 autocomplete attribute 的值

标签 {#labels}

提供标签来描述所有表单控件的用途;使 forid 链接起来:

  1. <label for="name">Name</label>
  2. <input type="text" name="name" id="name" v-model="name" />

如果你在 chrome 开发工具中检查这个元素,并打开 Elements 选项卡中的 Accessibility 选项卡,你将看到输入是如何从标签中获取其名称的:

Chrome 开发者工具正在通过标签展示无障碍访问的 input 框的名字

:::warning 警告: 你可能还见过这样的包装 input 框的标签:

  1. <label>
  2. Name:
  3. <input type="text" name="name" id="name" v-model="name" />
  4. </label>

但我们仍建议你显式地为 input 元素设置 id 相匹配的标签,以更好地实现无障碍访问。 :::

aria-label {#aria-label}

你也可以为 input 框配置一个带有 aria-label 的无障碍访问名。

  1. <label for="name">Name</label>
  2. <input
  3. type="text"
  4. name="name"
  5. id="name"
  6. v-model="name"
  7. :aria-label="nameLabel"
  8. />

在 Chrome DevTools 中审查此元素,查看无障碍名称是如何更改的:

Chrome 开发者工具正在通过 aria-label 展示无障碍访问的 input 框名字

aria-labelledby {#aria-labelledby}

使用 aria-labelledby 类似于 aria-label,除非标签文本在屏幕上可见。它通过 id 与其他元素配对,你可以链接多个 id

  1. <form
  2. class="demo"
  3. action="/dataCollectionLocation"
  4. method="post"
  5. autocomplete="on"
  6. >
  7. <h1 id="billing">Billing</h1>
  8. <div class="form-item">
  9. <label for="name">Name:</label>
  10. <input
  11. type="text"
  12. name="name"
  13. id="name"
  14. v-model="name"
  15. aria-labelledby="billing name"
  16. />
  17. </div>
  18. <button type="submit">Submit</button>
  19. </form>

Chrome 开发者工具通过 aria-labelledby 展示 input 的无障碍访问名称

aria-describedby {#aria-describedby}

aria-describedby 的用法与 aria-labelledby 相同,它提供了一条用户可能需要的附加描述信息。这可用于描述任何输入的标准:

  1. <form
  2. class="demo"
  3. action="/dataCollectionLocation"
  4. method="post"
  5. autocomplete="on"
  6. >
  7. <h1 id="billing">Billing</h1>
  8. <div class="form-item">
  9. <label for="name">Full Name:</label>
  10. <input
  11. type="text"
  12. name="name"
  13. id="name"
  14. v-model="name"
  15. aria-labelledby="billing name"
  16. aria-describedby="nameDescription"
  17. />
  18. <p id="nameDescription">Please provide first and last name.</p>
  19. </div>
  20. <button type="submit">Submit</button>
  21. </form>

你可以通过使用 Chrome 开发工具来查看说明:

Chrome 开发者工具正在根据 aria-labelledby 和 aria-describedby 展示 input 的无障碍访问名和无障碍访问描述信息

占位符 {#placeholder}

避免使用占位符,因为它们可能会使许多用户感到困惑。

占位符的缺陷之一是默认情况下它们不符合颜色对比度标准;应当修改其颜色,让它看起来像是预先填入 input 框中的数据一样。查看以下示例,可以看到满足颜色对比度条件的姓氏占位符看起来像预填充的数据:

  1. <form
  2. class="demo"
  3. action="/dataCollectionLocation"
  4. method="post"
  5. autocomplete="on"
  6. >
  7. <div v-for="item in formItems" :key="item.id" class="form-item">
  8. <label :for="item.id">{{ item.label }}: </label>
  9. <input
  10. type="text"
  11. :id="item.id"
  12. :name="item.id"
  13. v-model="item.value"
  14. :placeholder="item.placeholder"
  15. />
  16. </div>
  17. <button type="submit">Submit</button>
  18. </form>

最好在表单外提供所有用户需要填写输入的信息。

用法说明 {#instructions}

添加用法说明时,请确保将其正确链接到目标 input 框。你可以提供附加用法说明并在 aria-labelledby 内绑定多个 id。这可以使设计更加灵活。

  1. <fieldset>
  2. <legend>Using aria-labelledby</legend>
  3. <label id="date-label" for="date">Current Date:</label>
  4. <input
  5. type="date"
  6. name="date"
  7. id="date"
  8. aria-labelledby="date-label date-instructions"
  9. />
  10. <p id="date-instructions">MM/DD/YYYY</p>
  11. </fieldset>

或者,你可以通过 aria-describedby 将用法说明附加到 input 框上。

  1. <fieldset>
  2. <legend>Using aria-describedby</legend>
  3. <label id="dob" for="dob">Date of Birth:</label>
  4. <input type="date" name="dob" id="dob" aria-describedby="dob-instructions" />
  5. <p id="dob-instructions">MM/DD/YYYY</p>
  6. </fieldset>

隐藏内容 {#hiding-content}

通常,即使 input 框具有无障碍的名称,也不建议在视觉上隐藏标签。但是,如果可以借助周围的内容来理解输入的功能,那么我们也可以隐藏视觉标签。

让我们看看这个搜索框:

  1. <form role="search">
  2. <label for="search" class="hidden-visually">Search: </label>
  3. <input type="text" name="search" id="search" v-model="search" />
  4. <button type="submit">Search</button>
  5. </form>

现在,只要视力情况良好,用户可以就能通过按钮的内容识别出该 input 框的目的。

此时我们可以使用 CSS 从视觉上隐藏元素,同时也不会影响到无障碍访问:

  1. .hidden-visually {
  2. position: absolute;
  3. overflow: hidden;
  4. white-space: nowrap;
  5. margin: 0;
  6. padding: 0;
  7. height: 1px;
  8. width: 1px;
  9. clip: rect(0 0 0 0);
  10. clip-path: inset(100%);
  11. }

aria-hidden="true" {#aria-hiddentrue}

添加 aria-hidden="true" 在无障碍访问时被隐藏,但对其他可视用户仍然是可见的。不要在可聚焦的元素上使用它,请只在装饰性的、重复的的或屏幕外的内容上使用它。

  1. <p>This is not hidden from screen readers.</p>
  2. <p aria-hidden="true">This is hidden from screen readers.</p>

按钮 {#buttons}

在表单中使用按钮时,必须设置类型以防止提交表单。 你也可以使用一个 input 元素来创建按钮:

  1. <form action="/dataCollectionLocation" method="post" autocomplete="on">
  2. <!-- 按钮 -->
  3. <button type="button">Cancel</button>
  4. <button type="submit">Submit</button>
  5. <!-- 输入按钮 -->
  6. <input type="button" value="Cancel" />
  7. <input type="submit" value="Submit" />
  8. </form>

功能图片 {#functional-images}

你可以使用这种方式来创建一个带有功能的图片。

  • input 框

    • 这些图片会像一个类型为 submit 的表单按钮一样
    1. <form role="search">
    2. <label for="search" class="hidden-visually">Search: </label>
    3. <input type="text" name="search" id="search" v-model="search" />
    4. <input
    5. type="image"
    6. class="btnImg"
    7. src="https://img.icons8.com/search"
    8. alt="Search"
    9. />
    10. </form>
  • 图标

  1. <form role="search">
  2. <label for="searchIcon" class="hidden-visually">Search: </label>
  3. <input type="text" name="searchIcon" id="searchIcon" v-model="searchIcon" />
  4. <button type="submit">
  5. <i class="fas fa-search" aria-hidden="true"></i>
  6. <span class="hidden-visually">Search</span>
  7. </button>
  8. </form>

规范 {#standards}

万维网联盟 (W3C) Web 无障碍访问倡议 (WAI) 为不同的组件制定了 Web 无障碍性标准:

网络内容无障碍指南 (WCAG) {#web-content-accessibility-guidelines-wcag}

WCAG 2.1 继承自 WCAG 2.0,接纳 Web 演进过程中的新技术。W3C 鼓励在开发或更新 Web 无障碍访问策略时使用 WCAG 的最新版本。

WCAG 2.1 2.1 四大指导原则 (缩写 POUR):{#wcag-21-four-main-guiding-principles-abbreviated-as-pour}

  • 可感知性
    • 用户必须能够感知所渲染的信息
  • 可操作性
    • 表单界面,控件和导航是可操作的
  • 可理解性
    • 信息和用户界面的操作必须为所有用户所理解
  • 健壮性
    • 随着技术的进步,用户必须能够访问内容

Web 无障碍倡议 – 无障碍访问丰富的互联网应用程序 (WAI-ARIA) {#web-accessibility-initiative-accessible-rich-internet-applications-wai-aria}

W3C 的 WAI-ARIA 为如何构建动态内容和高阶用户界面控件提供了指导。

资源 {#resources}

文档 {#documentation}

辅助技术 {#assistive-technologies}

测试 {#testing}

用户 {#users}

世界卫生组织估计,全世界 15% 的人口患有某种形式的残疾,其中约 2 - 4% 的人严重残疾。估计全世界有 10 亿残障人士,它们是世界上最大的少数群体。

残疾的种类繁多,大致可分为以下四类:

  • 视觉 - 可以为这些用户提供屏幕助读器、屏幕缩放、控制屏幕对比度或盲文显示等帮助。
  • 听觉 - 可以为这些用户提供视频字幕、文字记录或手语视频。
  • 运动能力 - 可以为这些用户提供一系列运动障碍辅助技术中:比如语音识别软件、眼球跟踪、单刀式开关、超大轨迹球鼠标、自适应键盘等等。
  • 认知能力 - 可以为这些用户提供补充媒体、更清晰和简单、更结构化的内容。

你可以查看以下来自 WebAim 的链接,更深入地了解这些用户的需求: