WWDC Quick Look 💓 By SwiftGGTeam
What's new in web accessibility

What's new in web accessibility

观看原视频

Highlight

Safari 在 2022 年新增了对 HTML dialog 元素的支持、Web Speech API 中的 SSML 支持,以及更完善的 ARIA 自定义控件支持,让开发者可以用更少的代码构建无障碍的 Web 应用。

核心内容

辅助技术概述

全球约七分之一的人有某种形式的残障,影响他们与设备和 Web 的交互方式。Apple 提供了多种辅助工具:VoiceOver(屏幕阅读器)、Switch Control(切换控制)、Voice Control(语音控制)和 Full Keyboard Access(全键盘访问)。

00:36

这些工具依赖网页的语义信息来工作。Safari 对语义 HTML 元素(button、h1-h6、table、list 等)有内置的无障碍支持,这是首选方案。但当语义 HTML 无法满足需求时,需要用 JavaScript 创建自定义控件,并补充 ARIA 属性。

自定义控件的无障碍化

Session 用一个披萨切片选择器作为示例。这是一个自定义交互控件:用户点击披萨托盘上的不同位置来选择切片数量。

02:42

初始实现只有点击事件,对视觉障碍用户完全不可用。改造步骤:

  1. 添加 role="slider",让辅助技术知道这个控件是一个滑块
  2. 添加 tabindex="0",让键盘用户可以聚焦到这个控件
  3. 添加 ARIA 属性:aria-valueminaria-valuemaxaria-valuenowaria-valuetext
  4. 处理键盘事件:上下左右箭头键调整数值

这里有一个关键细节:在 iOS 上,VoiceOver 用户 swipe up 增加滑块值、swipe down 减少滑块值时,Safari 会自动模拟对应的箭头键事件。所以只需要监听键盘事件,就能同时支持 VoiceOver 手势和键盘操作。

SSML 语音合成

Web Speech API 的 SpeechSynthesis 接口在 Safari 中新增了对 SSML(Speech Synthesis Markup Language)的支持。

07:09

SSML 可以控制语音合成的多个方面:

  • <break>:插入停顿
  • <phoneme>:控制单词发音
  • <prosody>:调整音高、语速和音量
  • <lang>:指定语言区域

Session 的示例是一个语言学习测验:点击”朗读问题”按钮,英文问题用美语发音,西班牙语选项用墨西哥西班牙语发音,每个选项前有短暂停顿。

HTML Dialog 元素

Safari 新增了对 HTML <dialog> 元素的支持,让模态框的无障碍实现变得简单。

10:50

<dialog> 自动处理:

  • 焦点管理
  • Escape 键关闭
  • iOS 的 scrub 手势关闭(快速左右滑动)
  • 模态外部的元素对辅助技术不可见

配合 aria-labelledbyautofocus 属性,可以进一步优化屏幕阅读器用户的体验。

详细内容

自定义滑块控件

HTML 结构:

<div id="pizza-input"
     role="slider" tabindex="0"
     aria-valuemin="0" aria-valuemax="8"
     aria-valuenow="4" aria-valuetext="4 slices">
</div>

关键点:

  • role="slider" 告诉辅助技术这是一个滑块控件
  • tabindex="0" 让键盘和辅助技术可以聚焦到该元素
  • aria-valueminaria-valuemax 定义取值范围
  • aria-valuenow 表示当前数值
  • aria-valuetext 提供比数字更友好的描述(“4 slices” 比 “4” 更易懂)

JavaScript 实现:

class PizzaControl {
  constructor(id) {
    this.control = document.getElementById(id);
    this.sliceCount = 4;

    // 点击事件:根据点击位置计算切片数量
    this.control.addEventListener("click", (event) => {
      const newSliceCount = this.computeSliceCount(event);
      this.update(newSliceCount);
    });

    // 键盘事件:支持方向键调整
    this.control.addEventListener("keydown", (event) => {
      const key = event.key;
      if (key === "ArrowRight" || key === "ArrowUp")
        this.update(this.sliceCount + 1);
      else if (key === "ArrowLeft" || key === "ArrowDown")
        this.update(this.sliceCount - 1);
    });
  }

  update(newSliceCount) {
    // 限制在 0-8 范围内
    this.sliceCount = Math.max(0, Math.min(newSliceCount, 8));

    // 视觉更新
    this.renderSlices(this.sliceCount);

    // 同步更新 ARIA 属性
    this.control.setAttribute("aria-valuenow", this.sliceCount);
    const sliceModifier = this.sliceCount === 1 ? "slice" : "slices";
    this.control.setAttribute("aria-valuetext",
      `${this.sliceCount} ${sliceModifier}`);
  }
}

关键点:

  • 点击事件处理鼠标/触摸交互
  • 键盘事件同时服务键盘用户和 VoiceOver 用户
  • update() 方法同时更新视觉表现和 ARIA 属性
  • 规则:更新视觉时,必须同步更新 ARIA 表示

SSML 语音合成

function wrapWithSSML(phrase, locale) {
  return `
    <break time="100ms"/>
    <prosody rate="80%">
      <lang xml:lang="${locale}">
        ${phrase}
      </lang>
    </prosody>
  `;
}

const readQuestionButton = document.getElementById("read-question-btn");

readQuestionButton.addEventListener("click", () => {
  const ssml = `
    <speak>
      How do you say
        ${wrapWithSSML("the water", "en-US")}
      in Spanish?
      ${wrapWithSSML("El agua", "es-MX")}
      ${wrapWithSSML("La abuela", "es-MX")}
      ${wrapWithSSML("La abeja", "es-MX")}
      ${wrapWithSSML("El árbol", "es-MX")}
    </speak>
  `;

  const utterance = new SpeechSynthesisUtterance(ssml);
  window.speechSynthesis.speak(utterance);
});

关键点:

  • 整个 SSML 字符串包裹在 <speak> 元素中
  • <break> 插入 100ms 停顿,在选项之间制造节奏
  • <prosody rate="80%"> 将语速降低到 80%,更容易听清
  • <lang xml:lang="..."> 切换发音语言区域
  • SpeechSynthesisUtterance 接收 SSML 字符串,传给 speechSynthesis.speak() 播放

HTML 中的按钮:

<button id="read-question-btn">
  Read question<span aria-hidden="true">🔊</span>
</button>

关键点:

  • 扬声器 emoji 用 aria-hidden="true" 隐藏,避免屏幕阅读器读出无意义的 emoji 描述

Dialog 模态框

基础实现:

<dialog id="show-score-modal">
  <form method="dialog">
    You got all six questions correct. Great work!
    <button type="submit">Close</button>
  </form>
</dialog>
const showScoreButton = document.getElementById("show-score-btn");

showScoreButton.addEventListener("click", () => {
  document.getElementById("show-score-modal").showModal();
});

关键点:

  • <dialog> 元素原生支持模态行为
  • method="dialog" 让表单内的提交按钮自动关闭对话框
  • showModal() 显示模态框,自动管理焦点和背景遮挡

优化后的无障碍版本:

<dialog id="show-score-modal" aria-labelledby="modal-content">
  <form method="dialog">
    <span id="modal-content">
      You got all six questions correct. Great work!
    </span>
    <button type="submit" autofocus>Close</button>
  </form>
</dialog>

关键点:

  • aria-labelledby 指向内容元素的 ID,让屏幕阅读器打开时先读出内容
  • autofocus 将初始焦点放在关闭按钮上,用户可以直接操作
  • 系统自动处理 Escape 键和 iOS scrub 手势关闭

核心启发

做一个无障碍优先的在线测验平台

  • 做什么:一个支持多种题型的在线考试/测验系统,所有交互都对屏幕阅读器友好
  • 为什么值得做:自定义控件(如滑块、拖拽排序)在测验中很常见,用 ARIA + 键盘事件可以做出既好看又无障碍的体验
  • 怎么开始:用 rolearia-* 属性和键盘事件构建自定义控件,用 <dialog> 显示结果和确认框,用 SSML 朗读题目

做一个语音辅助的烹饪 App

  • 做什么:一个 Web 烹饪教程,用户可以用语音控制步骤切换,同时听到带语调的朗读
  • 为什么值得做:厨房场景下手是湿的/油腻的,语音交互和清晰的语音反馈很实用
  • 怎么开始:用 Web Speech API 的 SpeechRecognition 接收语音指令,用 SSML 控制步骤朗读的语速和停顿

做一个全键盘可操作的图片编辑器

  • 做什么:一个轻量级 Web 图片编辑器,所有功能都可以通过键盘完成
  • 为什么值得做:很多创意工具只支持鼠标,对运动障碍用户不友好
  • 怎么开始:为每个工具设置 tabindex 和键盘快捷键,用 ARIA live region 播报操作结果,用 <dialog> 处理确认弹窗

做一个多语言学习卡片 App

  • 做什么:一个语言学习工具,卡片正面是单词,背面是释义和例句,支持语音朗读
  • 为什么值得做:SSML 的 <lang> 标签让不同语言的单词可以用正确的发音读出,<phoneme> 可以处理特殊发音
  • 怎么开始:用 SSML 包装每个单词和例句,用 <prosody> 调整语速,用 aria-live 区域播报当前卡片状态

关联 Session

评论

GitHub Issues · utterances