30年前,Tim Berners-Lee 在欧洲核子研究中心创建了第一个 Web 网页,宣告了万维网的诞生。自此,万维网就承载着开放平等的愿景。
Accessibility——无障碍设计&信息无障碍(也简称为 A11y),虽然常常会被解释为”为残障人士服务“,但其无障碍设计的核心在于为所有人提供同等的体验。我们每个人都有可能在某些时刻成为失能者,这称为场景性残疾(situational disability & temporary disability),比如受伤骨折后,暂时失去了部分活动能力。又比如被强光照射时,看不清楚事物,在嘈杂地铁中的听力产生障碍等等。
根据 W3C 组织的定义,Web accessibility 意味着每个人都可以感知、理解并与 Web 交互,甚至为 Web 做出贡献。中国工信部也指出,信息无障碍是指通过信息化手段弥补身体机能、所处环境等存在的差异,使任何人(无论是健全人还是残疾人,无论是年轻人还是老年人)都能平等、方便、安全地获取、交互、使用信息。
但在万物互联的当下,尽管我们的衣食住行早已与网络世界息息相关,互联网并未成一个平等的,人人都可以访问的世界。根据2022 年 The WebAIM Million 统计报告,在对 100万 个网站首页进行无障碍分析后,得到的结果却差强人意:
图源:2022 年 The WebAIM Million 报告
在这些页面无障碍错误中,96.5%的错误归属于以下五类:
在前端开发的视角中,每一个 Web 应用都可以拆解为 HTML、css 和 JAVAScript。HTML 会经过 HTML Parser 将 HTML 结构转换成 DOM Tree;CSS 会经过 CSS Parser 将 CSS 转换成 CSSOM Tree。最终,浏览器根据 DOM Tree 和 CSSOM Tree 构建出最终的 Render Tree。
对于无障碍 Web 应用,除了包含 DOM 和 CSSOM 之外,将包含 AOM (Accessibility Tree,可访问性树)。AOM 可访问性树和 DOM 树平行存在。简单来说,可访问性树是 DOM 树的一个子集。每个需要暴露给 ATs 辅助技术的 DOM 元素都对应一个在可访问树中存在的无障碍对象。定义 AOM 实现的标准是 WAI-ARIA(Web Accessibility Initiative – Accessible Rich Inte.NET Application),即可访问的互联网富应用标准,致力于解决应用的可访问性问题,它与HTML5 标准同属于 W3C 组织。Web 应用的 AOM 也并非遥不可及,打开 Chrome 浏览器的 Devtools,我们即可查看页面的 AOM 结构。
在了解了无障碍的基本概念后,我们分别从 HTML、开发框架以及 CSS等角度,一起来看看无障碍页面的实现方式吧。
就像浏览器引擎依赖 HTML 结构以构建页面 UI 骨架,ATs 设备也依赖 HTML 结构来构建页面的 AOM 可访问性树。所以语义化的 HTML 对于实现 Web 应用无障碍至关重要,因为原生的 HTML 标签中包含了构建 AOM 的必要元数据。
参考上图,ATs 设备完全可以正确地渲染滑动输入框,即便我们没有在HTML 标签上添加 WAI-ARIA 属性。但我们在开发时往往会忽略 HTML 元素的实际语意,而更多采用无语意的 <div> 和 <span> 标签 (<div> 和 <span> 之外的近 104 个 HTML 标签都具有语义信息)。因为这两个标签没有默认样式,足够简单,就像白纸一样可以随意画上 CSS 样式。但这样的标签,对于 ATs 设备来说,就是灾难。
以上图为例,对于 <button> FOO </button> 标签,读屏软件将读出 “Button, foo",告知用户当前元素是按钮,包含文字 foo。但对于 <div class="button"> FOO </div> 标签,读屏软件只能读出 “foo”,并不能提示当前元素是一个可交互的按钮。虽然我们也可以通过设置 WAI-ARIA 属性为 HTML 标签增添无障碍语意,比如 <div class="button" role="button"> FOO </div>,但这样会平添许多额外的工作,也增加了出错的机率:根据 The WebAIM Million 统计报告,包含 ARIA 的页面比不使用 ARIA 的页面,检测出无障碍性错误的可能高 70%。
在社区中一直都有人在提倡 CSS裸奔日(CSS Naked Day),编写 HTML 时不要基于 UI 视觉效果(CSS 样式),而是基于 UI 的页面结构,可以确保 HTML 的语义完善,增强页面可访问性。
相关浏览器插件:HeadingsMap - Chrome Web Store
WAI-ARIA 的全称是 Accessible Rich Internet Applications,简称 ARIA,是 W3C规范之一。ARIA 允许 Web 开发者创建只有 ATs 技术(比如屏幕阅读器)可以看到的内容(属性),用以实现 HTML 无法达成的无障碍功能,比如:
ARIA 表现为 HTML 的属性,确定了元素的 ARIA 角色、状态和属性。这些信息帮助 ATs 技术更好地理解 Web 页面,确保用户与页面元素的交互。一般情况下,ARIA 不会影响 Web 页面的渲染,也不会影响鼠标或键盘用户的行为,只有使用辅助技术的用户才能感知到 ARIA。开发人员随意使用 ARIA 所导致的问题,对于页面无障碍功能往往是致命的,而且难以察觉。
但 ARIA 永远无法替代语义化 HTML 标签,NO ARIA is better than bad ARIA。请优先考虑语义最贴近的 HTML 标签,只在必要时使用 ARIA。
相关浏览器插件:
(1) 采用 <fieldset> 为表单项分类
当表单分为不同板块时,我们可能会使用 <div> 元素实现表单项的样式板块划分,但这样的划分并不利于无障碍设备获得表单项信息,可以使用<fileset>进行替换。
(2) 正确使用 label,为 <input /> 标签设置对应的 label
在实现表单时,我们往往会通过 placeholder 来提示当前表单项的填写内容。这样的设计会导致当 input 得到焦点时,placeholder 自动消失,造成用户无法感知当前表单项的内容。我们可以使用 label 充当 placeholder,这样的交互方式也称为 Float Label Pattern
(3) 尽可能使用原生的表单元素
在制作表单组件时,我们往往会出于实现 UI 样式的要求,采用 <div> 替代原生的表单元素。尽管这些表单组件在视觉和功能上满足了 UI 要求,但它们并未实现原生表单元素的无障碍功能。
(4) 为表单元素设置原生的校验属性
required、minlength、pattern 等表单的原生校验属性,不但可以满足正常的表单校验需求,也具有更好的无障碍支持
很多行动不便的用户依赖键盘操作,靠 Tab 键和方向键等浏览网。因此我们在构建 Web 应用的时候要注意:
一些 HTML 的原生标签具备可聚焦属性,也被称为可聚焦元素。这些原生 HTML 元素,天然存在于页面 Tab 键顺序内,内置了键盘事件处理,可以通过 Tab 键聚焦,并且获得焦点时有可见的焦点指示器(往往是显眼的蓝色框框)。但对于无法聚焦的元素,我们可以设置元素的 tabindexlace 属性,使元素可聚焦。
如果想给当前元素生成快捷键的话,可以给元素设置 accesskey 属性。但使用 accesskey也需注意以下问题:
相关浏览器插件:
在 <html> 标签元素上设置正确的 lang 属性。如果你的页面没有显式设置当前页面所使用的语言,那么读屏软件将无法选择匹配的语音配置文件和字符集,读屏软件读出的页面内容是乱码。所以,为了确保页面的内容正确,请务必为 </html><html> 元素指定有效的BCP 47语言。
往往一张表情包图片就可以抵千言万语,但对于读屏软件来说,读取 <img /> 标签的有效信息,只能靠 alt 属性。所以不要忘记为 <img /> 标签添加描述性的 alt 属性。如果图片只是为了装饰效果,那么可以考虑将 <img /> 标签 替换为 CSS 背景图。
与 <img />标签类似,读屏软件对于 <a> 和 <button> 标签的信息获取,依赖于标签包裹的文本。使用”阅读更多“,甚至图片作为这类标签的包裹内容,并不能为用户提供足够的信息。如果不方便添加文本信息,也可以利用 aria-label 增强元素的语义信息:
<a href="post.php?post=632" aria-label="More on Using Meaningful Link Text">More...</a>
根据 2022 年 The WebAIM Million 统计报告,使用 JavaScript 框架的页面比不使用框架的页面存在更多的无障碍错误,其中 React 开发的页面平均存在 50.8 个错误,Vue 开发的页面存在 63.4 个错误。虽然统计结果不能说明框架导致了这些错误,但在使用框架进行 Web 开发时,常常会忽略使用 HTML 原生标签,或者引入无障碍功能支持性不佳的组件库,导致框架开发的 Web 应用可访问性普遍较差。
维护层级明晰的 HTML 结构,对于 Web 应用的无障碍功能十分重要。因为 ATs 软件,特别是读屏软件,不止是由上至下地展现页面信息,更会基于页面不同级别的标题或者文档地标元素进行页面导航。在将页面拆分成不同组件后,保持 HTML 文档结构层级会更加复杂。比如当一个组件包含 <h2> 标签时,可能在一些位置该组件会破坏原有 HTML 文档结构。
在使用 React、Vue 等框架时,我们往往需要将组件包裹在一个根元素中:
但这样的处理在编译后,会在造成元素结构的混乱:
<div> 标签混在 <tr> 标签中,会导致读屏软件无法正确解析 table ,造成用户无法访问表格内容。此时,我们应该使用 React 的 <fragment> 标签(Vue 中可以使用 vue-fragment),确保根元素不存在于最终的 DOM 结构内: