聊聊前端无障碍实践

avatar
@北京抖音信息服务有限公司

出品:西瓜视频前端技术团队

作者:赵帅

许多人认为残障人群不使用互联网。事实上,情况完全相反。很多残障人群和健全人一样经常使用网站,在网上购物和进行社交活动。甚至对网络的依赖更大。健全人和残障人士的区别,仅仅在于我们感知、接触世界的方式不一样

信息无障碍

普遍共识

国内对信息无障碍的普遍共识是指通过信息化手段弥补身体机能、所处环境等存在的差异,使任何人(无论是健全人还是残疾人,无论是年轻人还是老年人)都能平等、方便、安全地获取、交互、使用信息。

——《工业和信息化部-中国残疾人联合会关于推进信息无障碍的指导意见

无障碍人群

视障人群

视觉障碍者简称视障者,如果个人需透过辅助器具如眼镜、放大镜等才能看清楚东西, 就称为视障者

在中国残疾人实用评定标准(试用)条例中,视障者的残疾分为四个等级。分别是一级盲(一级视力残疾)、二级盲(二级视力残疾)、一级低视力(三级视力残疾)、二级低视力(四级视力残疾)。

在信息无障碍领域,还需要考虑色觉识别障碍人群,其中包括色盲和色弱。

听障人群

听觉障碍是指由于先天或后天原因,导致听觉器官构造缺损,或机能发生部分或全部障碍,导致对声音听取或辨识有困难

全球将近 50%的 12 至 35 岁人群,即大约 11 亿年轻人由于长时间或过度娱乐噪声暴露,正面临着听力受损的风险。听力损失流行率随着年龄增长而增加。60 岁以上人群中,超过 25%的人受到残疾性听力损失的影响。

肢体障碍

肢体障碍指的是人体运动系统的结构、功能损伤造成四肢残缺、躯干麻痹(瘫痪)、畸形等,导致人体的运动功能丧失或者受限。肢体障碍既有先天性的,也可能因年龄增长而导致

同时,我们所有人都可能出现暂时性或者情景性的肢体障碍,比如:手腕骨折的人一段时间无法使用手机,或者一手提重物,只能单手操作设备等等。

认知障碍

认知障碍是因大脑神经细胞病变而导致大脑功能衰退的疾病,患者的记忆、理解、语言、学习、计算和判断能力都会受影响,部分会有情绪、行为及感觉等方面的变化。大多数认知障碍植根于生物学或生理学。有严重认知障碍的人几乎在日常生活的每个方面都需要帮助。

老年人

根据联合国《人口老龄化及其社会经济后果》划分标准,将 65 岁及以上人口定义为老年人口。我国是世界上老年人口最多的国家,全国 60 岁及以上老年人口达到 2.64 亿,占总人口的 18.7% 。尽管整体的社会人口结构是在急剧变“老”的,但科技发展的脚步还在持续更“新”,智能医疗、远程辅诊,在线教育、无接触配送等新的应用场景在各行各业里面持续涌现,也让更多人认识到老年人在信息时代的生存困境

规模概览

image.png

【焦点】解释

什么是焦点

对于盲人和部分低视力人群,他们需要使用读屏软件来了解页面中的内容。

一般情况下,读屏软件会将屏幕上焦点部分的视觉元素/控件朗读出来,用户可以通过在屏幕上移动焦点来“浏览”内容。正确的焦点标注和焦点顺序能够让读屏软件用户获得更好的体验。

焦点顺序很重要

在移动应用中,如果界面可以进行顺序导航,且导航顺序影响含义和操作,则可聚焦元素应以保持其含义和可操作的顺序获取焦点。默认情况下,焦点顺序为从上到下,从左到右。

相关创新

使用无障碍设计思维设计产品逻辑和交互方式,提高残障人群和非残障人群的产品用户体验。这种包容性设计为用户提供多种交互体验,促进了行业对新型交互方式(如自然语言处理、语音识别等)的探索

不论是生活中的设施还是电子产品的性能,信息无障碍的普及和应用为所有人带了便利,不仅能帮助残障群体便捷生活,也渗透在普通人的生活中。

应用障别内容
屏幕对比度视障手机屏幕对比度调整功能既可以帮助视障人群看清楚手机上的信息,也能帮助非视障者在阳光下看清楚手机屏幕
深色模式视障浅色文本有了深色背景的衬托,即使在弱光条件下也能轻松阅读
搜索功能自动填充认知障碍帮助有认知障碍(记忆力低下)的人完成搜索
文本输入预测认知障碍只要开始打字,系统就会根据你的谈话对象和之前的谈话内容来给出建议的文本,轻点即可选择合适的字词。
Siri 等人工智能助理软件残障人群帮助残障人群进行手机操作,也能帮助普通人群操作,比如双手沾水/开车时使用 siri 接电话
勿扰模式认知障碍帮助认知障碍的人集中注意力
账号密码自动储存功能认知障碍帮助认知障碍的人快速登陆
手写备忘录转文本听障、认知障碍可以在显示屏上手写文字来回复信息,手写字会自动转换为键入文本
智能识图视障帮助视障群体进行物体识别,也帮助普通人在淘宝拍照识别商品
实时字幕听障帮助听障人群,也帮助普通人更好了解视频/音频内容
无障碍坡道肢体障碍本意是方便轮椅人士出入,而实际使用中,多数老人、推自行车的人,甚至普通人都更愿意走更省力的坡道而非楼梯
无障碍电梯肢体障碍适合乘轮椅者、残疾人或担架床可进入和使用的电梯,实际使用中便捷更多人,比如在地铁站、火车站带有大重量行李的人
自动门肢体障碍便捷肢体障碍群体的同时也让普通人的开关门过程更方便
电梯按钮肢体障碍电梯轿厢内除了一侧的普通楼层按钮,另一侧的按钮高度为 1 米左右,满足轮椅人士的按键需求也为更多乘客提供按键机会

以信息无障碍理念主导的包容性设计目标促进了技术的进步、衍生出更多新产品和创新功能。

键盘、邮件、电动牙刷最初都是为了残障人士更好的使用,但现在都为大部分人提供更好的体验
语音助手听写功能最初是帮助视障者,使用听写功能,可以口述文字和标点来替代输入
谷歌图像识别软件识别分析图像中的物体和文本的软件,最初是帮助视障者感知周围的世界的工具。
文本输入预测只要开始打字,系统就会根据你的谈话对象和之前的谈话内容来给出建议的文本,轻点即可选择合适的字词
降噪功能耳机与“降噪功能”类似,“对话增强功能”通过计算音频技术和波束成形麦克风,使耳机专注于你面前谈话对象的声音,让语音更易辨识,面对面交流更顺畅
屏幕辅助功能键盘可以自定义设置自动大写和更出色的字词建议功能,也支持头部跟踪硬件,以便肢体活动能力受限的用户移动指针,选择或拖动屏幕上的项目
屏幕色彩补偿校准OPPO“千人千屏”工程最初针对视障人群,长期计划为让全球跨领域多设备都能使用,让更多的用户在各种场景中使用到 OPPO 专业的视觉检测及获得属于自己的一套屏幕色彩能力,同时融入更多的色彩偏好维度,将千人千屏工程走的更加专业化
无障碍人脸识别微众银行设计开发 1、识别过程:光线活体人脸识别,不需要视障用户进行点头、眨眼、读数字或者其他的辅助动作即可完成验证;2、 人脸对齐:结合了图像处理、人脸检测、AI 语音合成,以震动传感器和加速度传感器为辅助增加引导,提示用户对准人脸。识别过程中,手机通过振动频率,来告知用户偏离程度,振动强 度大表示严重偏离,振动强度小表示轻微偏离,同时通过语音告诉用户移动手机的方向。
智能输入法讯飞输入法 1、信息无障碍模式:首个盲协认证的输入法:集拼音、语音、手写、笔画于一体的手机输入软件;Android、iOS 系统均实现无障碍,菜单面板适配双击操作;语音面板震动提示语音输入,支持多方言多语种等;2、长辈模式:视觉交互上调大字体, 默认半屏手写,而不是打字键盘,还精简菜单面板图标降低操作成本,调低键盘颜色对比度。语音输入上,针对老年说话慢,经常按下语音输入之后忽然会忘了说什么,导致语音交互退出等问题,语音输入自动默认是长文本识别。

辅助技术

辅助技术 是用于帮助残障人士使用不同设备、软件或产品的技术。一些辅助工具可能只会被特定的、长期残疾的人使用,包括屏幕阅读器、屏幕放大器等。也有我们生活中的常会使用的辅助工具,包括手机上的语音控制、符合人体工程学的键盘、或浏览器自带的页面缩放功能。下表是**「常用辅助工具汇总」。**

人群/辅助技术读屏软件屏幕放大功能语音转文字文字转语音字幕对比度增强等显示设置
视障人群全盲---
低视力
色彩识别障碍-----
听障人群---
肢体障碍人群---
认知障碍人群-
老年人-

*注:✔为必须;〇为可能需要(根据个体需求);-为非必须

读屏软件

读屏软件并不仅是为视障者服务的,就如同坡道不仅仅是为轮椅使用者服务一样。但凡是无障碍的通用设计,都可以服务于所有人。比如视力下降的老年人,或当你在不想看长文章的时候,都可以开启辅助功能里的读屏软件,让它帮你朗读出屏幕上的文本信息。

人群使用场景
盲人盲人使用手机主要依靠读屏软件
低视力视力障碍严重者会优先用读屏软件 (视力障碍不严重者会搭配使用屏幕放大功能等)
肢体障碍上肢损伤导致无法正常使用鼠标或触屏,或使用的误触率较高(电脑端常见)
老人视力低下,或更加偏向于听信息时
所有人不想看长文章,或更加倾向于听内容时

iOS-VoiceOver 旁白

iOS 系统中内置读屏软件称为 VoiceOver(旁白)。官方链接:VoiceOver 手势

在 iOS 系统中打开设置 - 辅助功能快捷键-勾选旁白,就可通过连续按三次侧边按钮开启和关闭旁白。

操作手势

image.png

Android - Talkback

Android 中内置屏幕阅读器称为 TalkBack。 官方链接:Talkback 手势

Talkback 的快捷方式: 同时按压音量上下键 3 秒,即可打开/关闭屏幕朗读。

操作手势

image.png

无障碍研发

对于 Web 开发者来说,可以通过调整 HTML 的结构和标签,增加 HTML 属性,配合 CSS 和 JavaScript 等手段来提高页面的可访问性和无障碍性。

无障碍树(Accessibility tree)

浏览器获取 DOM 树,并将其修改成适用于辅助技术的形式。我们将这个修改后的树称为无障碍树

Devtools 查看

其实开发过程中离 无障碍树 相当近,只不过大家一直都没有注意,打开控制台选择 Accessibility (无障碍功能)即可看到:

勾选【启动整页模式的无障碍功能树】,即可通过右上角的小人查看无障碍树

语义化的元素

无障碍树的构建便是通过语义化来实现的,当选中一段 DOM 的时候,在 Devtools 里面都可以看到,浏览器实际呈现给屏幕阅读器的就是这个结构 (浏览器获取 DOM 树,并将其修改成适用于辅助技术的形式) 将 DOM 树变成无障碍树,良好的使用语义化标签,能让辅助设备更合理地将你网站的内容转化成 Accessibility tree,从而解读给用户。

语义化的 HTML 标签

例如 <header> <footer> <nav> <section> <main> <aside> <button>,使用语义化的标签,主要影响两个方面:

  • 选中元素时是否会整块选中

  • 朗读时结尾会加上怎样的修饰词

其中默认设置下,目前仅 <button> 标签可以使得选中元素时会整块选中,而不单独选中子元素。至于修饰词这里列举具体的情况:

  • <header> 读作"xxx 横幅 标志性内容"。

  • <footer> 读作"xxx 页脚 标志性内容"。

  • <nav> 读作"xxx 导航 标志性内容"。

  • <section> 仅读作"xxx",没有结尾修饰词。

  • <main> 读作"xxx 主要 标志性内容"。

  • <aside> 读作"xxx 补充 标志性内容"。

  • <button> 读作"xxx 按钮"。

  • <a> 读作"xxx 链接"。

实际上,在浏览器内部,使用语义化标签会隐式加上特定的 role 属性,最后朗读时的结尾修饰词也正是这些 role 属性的值以及分类,其他 role 的值朗读时也可以以此类推,而以上标签与 role 属性具体对应的关系如下:

HTML 标签role 属性值
headerbanner
footercontentinfo
navnavigation
sectionregion
mainmain
asidecomplementary
buttonbutton
alink

语义化并不只是 HTML5 中新增加的<header><main> 等标签,它们更多的算是结构语义化

  • 图片语义化 依靠着 img 标签中的 alt title 属性,其中 alt 用于图片描述,这个描述是给搜索引擎和屏幕阅读器使用。并且当图片无法显示时,页面会显示alt中的文字。
  • 标题语义化 包括了从 h1h6 的标题,在没有 HTML5 新增加的结构标签时,更多的是由 heading 来表示页面的结构。
  • 表格语义化 包括 table、caption、thead、tbody、tfoot、th 标签等等。

WAI-ARIA

www.w3.org/TR/wai-aria…

在日趋复杂的 web 应用中,语义化标签,远远不足以帮助浏览器理解页面。Web Accessibility Initiative – Accessible Rich Internet Applications (WAI-ARIA)就是为了解决这一问题。它是由 W3C 制定的技术规范,通过增加属性,赋予组件更强的语义化,提供面向无障碍场景的说明信息,使更多设备、用户能感知我们的页面。

tabindex 属性

在正常情况,只有 a 标签、button 以及 input 标签可以获得焦点,其他元素例如 div、span 等等是无法用是 tab 键获得焦点的。通过为标签属性中加入 tabindex,让 div 元素获得焦点,例如:

<div tabindex="0">这是能获得焦点的div</div>

它根据 tabindex 从小到大来控制 tab 的跳动顺序,虽然可以控制整个页面的浏览顺序,但是最好只使用 0 来指定 tabindex 属性。在上面也提到过打开控制面板查看页面结构 会发现 tab 默认的跳转顺序标签顺序是一致的,如果破坏掉了这个顺序对于一些不兼容 tabindex 的盲人辅助工具、浏览器会造成无法兼容的情况,tabindex的值有三个状态:

  1. tabindex=0时,这种情况下会按正常的顺序遍历到这个可获得焦点的元素,不会有顺序上的差异

  2. tabindex>0时,这种情况下会在遍历完tabindex="0"的元素后,再去遍历等于 1、等于 2 的……以此类推。

  3. tab index= -1,这种情况使用tab键不会获得这个元素的焦点,但是可以通过 js 获得,对于需要脚本控制的元素,通常会把值设置为-1

role 属性

屏幕阅读器会识别 role 属性,告诉用户当前元素的类型。常用的类型有:button、link、checkbox、radio、switch、slider、progressbar、alert、dialog 等,role 属性应该针对场景使用合适的值,让用户感知当前的 UI 组件的类型。

aria-*属性

列举些常用的属性

  • aria-label:阅读的内容

  • aria-labelledby=#id:阅读指定元素的内容,值为指定元素的 id。优先级高于 aria-label

  • aria-hidden="true":对屏幕阅读器不可见,即跳过。默认为 false

  • aria-checked="true":按钮已选中,与 role="checkbox/raido"搭配使用

  • aria-selected="true":选项已选中

  • aria-disabled="true":按钮不可用

  • aria-haspopup="true":当前元素会触发弹出式 UI 组件,与 aria-expanded 搭配使用。默认 false

  • aria-expanded="true":当前元素的弹出式 UI 组已弹出,默认 false

  • 进度条/滑块相关

    • aria-valuemin:最小值

    • aria-valuemax:最大值

    • aria-valuenow:当前值

    • aria-valuetext:阅读内容

更多属性值可以查看 Using Aria

看一个进度条例子:

<div 
    id="percent-loaded" 
    role="progressbar" 
    aria-valuenow="75"
    aria-valuemin="0" 
    aria-valuemax="100">
</div>

这个进度条是使用 <div> 构建的,它没有任何意义。在这个例子中, role="progressbar" 属性通知浏览器这个元素实际上是一个 JavaScript 驱动的进度条小部件。 aria-valueminaria-valuemax 属性指定进度条的最小值和最大值,aria-valuenow 描述进度条的当前状态和因此必须使用 JavaScript 保持更新。

除了将它们直接放置在元素中之外,还可以将 ARIA 属性添加到元素中,并使用 JavaScript 代码动态更新,如下所示:

var progressBar = document.getElementById("percent-loaded");

    progressBar.setAttribute("role", "progressbar");
    progressBar.setAttribute("aria-valuemin", 0);
    progressBar.setAttribute("aria-valuemax", 100);

function updateProgress(percentComplete) {
  progressBar.setAttribute("aria-valuenow", percentComplete);
}

实践推荐

  1. DOM 的顺序很重要

读屏软件在读屏时默认按照 DOM 的顺序朗读,因此如果 DOM 的顺序与内容的语义顺序不一致,例如使用了 flex-direction: row-reverse; 使得内容的顺序倒序显示,会使得内容难以理解。因此尽量避免使用会影响到 DOM 视觉顺序的样式,如果无法避免,需要手动设置 tabIndex 属性,告知读屏软件正确的内容顺序。

  1. 图像使用 alt 属性

<img> 标签需要加上 alt 属性,读屏软件会自动读出 alt 的内容,例如 alt 内容为"一只目光汹汹凝视远方的猫",那么会被读作"一只目光汹汹凝视远方的猫 图像"。如果没有添加 alt 属性,那么仅会读作"图像",视障用户会完全无法理解其实际含义。

但是,当 <img> 标签出现在 <a> 标签内部,作为一个图像链接时,应在 <a> 上使用 title 属性,<img> 标签可不加 alt 属性。

  1. 视频使用 title 属性

与上面的 <img> 标签相似,<video> 标签需要加上 title 属性,例如 title 内容为"一只正在奔跑的猫",那么会被读作"一只正在奔跑的猫 视频"。

  1. 禁用状态使用 disabled 属性

使用特定的 class 来增加禁用态样式是常见的手法,但由于 class 语义并不能被读屏软件识别,因此读屏时无法知道当前处于禁用态。可以改为使用 disabled 属性实现禁用态,例如:

<input type="search" name="q" placeholder="请输入用户名" aria-label="搜索用户" disabled/>
/* 禁用态样式 */
input[disabled] {
    opacity: .5;
}

而对于没有 disabled 属性的标签,例如 a 标签,可以使用 aria-disabled 属性达到同样的效果。

  1. a 标签作为按钮需要指明 role="button"

在 H5 中,为了避免一些浏览器默认样式的干扰,以及制作点击效果(具体原因),目前采用 a 标签实现。但从无障碍的角度考虑,a 标签默认会被当做链接处理,读屏时会读作"链接内的文字 链接"。

<a class="test_btn" role="button" href="javascript:;">文字</a>

读屏时会读出"文字 按钮"。

  1. 按钮可能会用图片实现,增加描述文字

如果 a 标签内本身没有文字,例如以图片、背景色和边框制作的按钮,还需要加上 aria-label="描述文字",读屏时会读作"描述文字 按钮"的形式

<a class="test_btn" role="button" href="javascript:;" aria-label="更完整的描述"></a>
  1. 多重标签嵌套

另外 a 标签内容如果有嵌套的标签,并不会影响文字被读出,例如:

<a class="test_btn" role="button" href="javascript:;">
    <span class="test_btn_inner">
        <span class="test_btn_inner_text">文字</span>
    </span>
</a>

读屏时仍会读出"文字 按钮"。

  1. 用 background-image 实现的 icon 无法被无障碍识别,需要改成 img 实现,用 alt 写文案
  2. 一些数字读出来是英文的,显式指定 lang 为 zh
<html lang="zh">
...
</html>
  1. 无障碍聚焦穿透弹窗蒙层,如图,弹窗后的按钮仍可被无障碍聚焦

把非弹窗内容区域和弹窗区域抽离,让内容和弹窗分别有自己的根节点,用一个变量记录是否有弹窗展示,并绑定 aria-hidden 在内容区域的根节点(火苗管理页用一个变量控制当前展示的弹窗名,这样可以保证弹窗单例)

<div class="root">
    <div class="content" :aria-hidden="!!visibleModal" />

    <!-- visibleModal 为空串时无弹窗展示,这时把aria-hidden设置为true禁用朗读 -->
    <div class="modals">
        <Modal1 :visible="visibleModal === modal1" />
        <Modal2 :visible="visibleModal === modal2" />
    </div>
</div>
  1. 弹窗弹出时聚焦,弹窗弹出时手动对标题元素调用 focus
if (this.visivle) {
    this.$refs.modal.focus()
}

测试工具

axe - Web Accessibility Testing

说明:chrome 插件,能对交互后的页面,进行评估,并给出指引。适合重交互的场景。

Lighthouse

说明:chrome 调试工具,无需安装,生成报告时页面需要刷新。

关于我们

我们来自字节跳动,是旗下西瓜视频前端部门,负责西瓜视频的产品研发工作。

我们致力于分享产品内的业务实践,为业界提供经验价值。包括但不限于营销搭建、互动玩法、工程能力、稳定性、Nodejs、中后台等方向。

欢迎关注我们的公众号:xiguafe,阅读更多精品文章。

我们在招的岗位:job.toutiao.com/s/k28ExPp。招聘的城市:北京/上海/厦门。

欢迎大家加入我们,一起做有挑战的事情!

谢谢你的阅读,希望能对你有所帮助,欢迎关注、点赞~