可访问 Web 组件指南
Web 组件是一项新支持的标准。它们与设计系统 (Design Systems) 完美契合,因为它们面向未来,并且兼容任何框架。然而,构建合适的 UI Web 组件可能并非易事,尤其是在您希望它们易于访问的情况下。以下是一些需要注意的事项。
最初发布于erikKroes.nl。也欢迎在Twitter或Twitch上提问(我每周都会直播关于无障碍设施的话题)。
在本文中,我将多次使用Lion作为示例。Lion 是一个白标 UI Web 组件的集合。更多关于 Lion 的信息请参阅结论部分。
内容
什么是 Web 组件?
Web 组件是一组标准:
- 自定义元素:创建自己的 HTML 元素的方法
- ES 模块:JavaScript 代码的自包含模块,可重复使用和扩展
- HTML 模板:可重复使用的 DOM 片段
- Shadow DOM:DOM 的封装
这些标准共同实现了“可复用、可扩展、封装、自包含的 Web 组件”。这听起来有点拗口,但含义却不太清晰。
实际上,这意味着您可以创建自己的 HTML 元素。这些元素拥有自己封装的 DOM 单元。在本例以及许多与 Web 相关的案例中,DOM 代表文档对象模型 (DOM)。DOM 是我们查看 HTML 或 XML 文档的方式。MDN 指出“DOM 将文档表示为节点和对象”。MDN 对此有相当好的解释。
这意味着您创建的 HTML 元素不会被元素外部的任何内容触及或影响。它们不会被意外地设置样式,也不会干扰您的全局样式。而且,由于它是一个 ES 模块,因此整个元素可以分发和扩展。所有这些方面共同构成了 Web 组件。ES模块代表 EcmaScript 模块。它是 JavaScript 与模块协同工作的方式,也是所有现代浏览器都支持的
标准。🎉
一个实际的例子是Google 地图 Web 组件。这个 Web 组件只需几行代码就能在你的页面上显示完整的交互式地图。你需要在页面上导入一些定义该组件的 JavaScript 代码:
<script src="good-map.js" async defer></script>
之后,您可以在页面的任何位置使用自定义元素。
<good-map api-key="AIzaSyAQuo91bcoB-KwWXaANroTrzpNZRFcNJ1k"
latitude="52.1664"
longitude="5.9075" zoom="3"></good-map>
注意到元素名称中有一个破折号吗?这是自定义元素规范的一部分,可以让浏览器更容易识别它们。
一个不太实用的例子是spacer-gif Web 组件。(spacer-gif 是一种老旧且冗余的技术,无需重新制作。)
<spacer-gif height="1" width="1"></spacer-gif>
狮子的例子可能是lion-switch
。
<lion-switch label="Label Text" help-text="Help text"></lion-switch>
所有这些优点都基于广泛支持的网络标准。
什么是影子 DOM 和光 DOM?
“确实,到目前为止听起来都很不错,但是有什么问题呢?”
英国人很快就会发现,自我孤立会带来一些弊端。假设你开发了一个卡片组件。它的使用界面(你在编辑器或浏览器中看到的)可能如下所示:
<my-card>This is the card content</my-card>
在浏览器中检查该组件时,它看起来可能如下所示:
<my-card>
#shadow-root (open)
<div class="card-wrapper">
<div class="card-header">
Presenting the card content:
</div>
<div class="card-content">
<slot>
<#text>↴
</slot>
</div>
</div>
This is the card content
</my-card>
组件会渲染一整块 DOM(“DOM 将文档表示为节点和对象。”还记得之前的<slot>
内容吗?),并将其放入 shadow-root 部分。其中,it 指的是我们最初放入元素的内容。所有添加的 DOM 都是 shadow DOM。所有其他“普通”DOM 被称为 light DOM。它是始终可见的部分。
由于 shadow DOM 被完全封装和隔离,因此它也完全断开连接。它几乎就像一个完全不同的文档,就像一个 iframe。值得庆幸的是,键盘导航可以穿透 Shadow DOM 边界。这意味着您可以<TAB>
进出 Shadow DOM。
当你想将标签指向输入框以建立明确的关系时,这会成为一个挑战。在纯 HTML 中,如下所示:
<label for="example-input">Label text</label>
<input id="example-input" type="text">
label
当两者(或)之一input
位于影子 DOM 中时,它们处于完全不同的上下文中。这使得它们无法相互引用。
同样的困境也适用于 WAI-ARIA 属性,例如aria-labelledby
、aria-describedby
和其他引用 ID 的属性。您要么需要将这两个元素都放在影子 DOM 中,要么需要将它们都放在轻量级 DOM 中。不过,轻量级 DOM 并不意味着它们必须位于同一个位置。轻量级 DOM 基本上是所有非影子 DOM 的 DOM。
我们lion-input
让开发者在标签槽中声明一个标签。该标签最终会显示在轻量级 DOM 中。
<lion-input>
<label slot="label">Label text</label>
</lion-input>
该组件将输入放置在 中slot="input"
,将帮助文本放置在 中slot="help-text"
,并将反馈放置在 中slot="feedback"
。这意味着输入可以连接到标签,但我们也可以使用它aria-describedby
来将输入连接到帮助文本(例如说明)和反馈(例如错误消息)。
扩展元素
就目前情况而言,只能通过扩展通用 HTML 元素(HTMLElement
)或另一个 Web 组件(应该位于某个深处,也是的扩展HTMLElement
)来创建 Web 组件。
对于可访问性而言,如果我们能够扩展任何元素,那将是一个巨大的优势。想象一下,例如,你可以扩展一个原生按钮(HTMLButtonElement
)。你将继承它的所有行为和语义,并且只需在此基础上进行添加。你将拥有一个坚实的基础,可以在此基础上进行构建。
该规范确实存在,但 Safari声明不支持此功能。Web Components 的优点之一就在于它是一项受支持的标准。因此,即使 Safari 有Polyfill,未来发展之路也充满不确定性。
可访问的 UI 组件
Web 组件最常用的用例可能是创建自定义用户界面控件。由于我们无法扩展任何原生元素,我们最终通常会选择包装原生元素,或者自行重新创建其行为。包装通常是最简单、最可靠的解决方案。重新创建与从零开始基本上是一样的<div>
。
单个组件包含如此多的方面,以至于很容易忽略某个功能或行为。当你忘记或未能实现某些功能时,你最终会创建出与原生元素相比有所欠缺的东西。这可能与你想要达到的目标完全相反。
以下概述了创建可访问的用户界面控件时需要特别注意的方面。这些要点并非 Web 组件所特有,也适用于 React、Vue、Svelte 或任何其他框架。
可聚焦
如果您的自定义控件是交互式的,请确保它可由键盘聚焦。对于包含单个交互式元素的简单控件,这意味着需要tabindex='0'
向控件中添加元素。对于更复杂的控件,您可能需要实现移动 tabindex或使用aria-activedescendant
。
键盘交互
用户应该能够通过键盘使用您的交互控件。对于许多设计模式,可以在WAI ARIA 创作实践中找到建议的键盘交互。
可见状态
交互式控件有几种状态,例如聚焦、悬停和激活。这些状态都应该清晰可见,并且最好每个状态都有自己独特的样式。
功能状态和属性
交互式控件也可以具有功能状态。例如,一个公开小部件(或可扩展小部件、折叠小部件、expando 小部件等)可以处于打开或关闭状态。这种状态不仅需要视觉呈现,还需要通过代码传达。这可以通过aria-expanded
在控件上切换来实现。
类似 的属性也是如此aria-multiline
。它们传达的属性可能隐含在原生元素中,在构建自定义控件时,必须手动添加以方便辅助技术使用。WAI-ARIA 提供了许多状态和属性来辅助实现这一点。
语义
原生 HTML 元素具有语义含义,并映射到 WAI-ARIA 角色。它们的语义是隐式的,并且始终存在。
自定义元素最初没有任何角色,但您可以显式地指定一个。WAI-ARIA 提供了广泛的角色,应该涵盖所有用例。不过,WAI ARIA 的语义比原生语义更明确。它更像是附加的,而不是内置的。
您可能会在使用 Windows 高对比度模式(Windows 的一个特殊工具)时注意到这一点。它并不关心您的 ARIA 属性。
可访问名称
交互式控件必须有一个名称才能被识别。例如,<button>
带有文本“保存”的元素可以通过辅助技术显示为“保存按钮”。在这种情况下,“保存”就是该元素的无障碍名称。该名称由“无障碍名称和描述计算”确定,并且有多种方法可以添加无障碍名称。
关系
从视觉上看,某些元素之间的关系可能很明显。例如,输入框旁边的简短文本很可能是该输入框的标签。然而,如果代码中没有明确这些关系,辅助技术可能无法识别它们。WCAG 成功标准 1.3.1 提到了相当多的技巧来解决这个问题。
全球标准和公约
创建自定义元素需要了解全局标准和约定。用户期望组件以特定的方式工作。重复造轮子往往会导致混乱的用户体验。遵循标准和约定可以避免混乱,并为用户创造一致的体验。
浏览器错误和变体
创建一个在每个浏览器和平台上都以相同方式工作的元素是一项巨大的挑战。一些原生元素甚至无法做到这一点。例如,当我<select>
在 Mac OS 上的 Firefox 中使用 时,它的行为与我在 Chrome 中打开时的行为不同。甚至在 Mac OS 上的 Chrome 和 Windows 上的 Chrome 之间也会有差异。使元素在不同平台上一致地工作,其细微差别和细节是一个非常大的挑战。
错误可能更难发现或规避。例如,WAI ARIA 创作实践 1.1建议aria-activedescendant
在使用 时使用 来控制焦点role="combobox"
。这听起来很棒,直到你发现这种组合实际上并不适用于所有浏览器。
可访问性对象模型
无障碍对象模型( AOM) 是 Web 平台的一项新增功能,旨在使浏览器的无障碍 API 对开发者更加透明、更易用。浏览器中对 AOM 的支持将对 Web 组件产生重大影响。但由于 AOM 仍处于开发阶段,且大部分功能尚未得到支持,我将留给Hidde de Vries等人进一步解释。
结论
创建易于访问的 Web 组件是完全可能的。它们非常适合大型组织,因为专业团队可以构建最佳的构建模块,并为开发人员和用户提供一致且出色的体验。然而,构建这些组件需要大量的时间、知识和精力。如果你问我……
每个人都应该使用 Web Components,
但很少有人应该构建它们
为了缓解这些痛点,我专业开发的 Web 组件拥有一个名为 Lion 的开源基础层。这是一个白标 Web 组件的集合,您可以轻松扩展、设置样式并进行自定义。它们的构建考虑了上述所有因素。您可以查看所有组件的现场演示,或在GitHub上查看它们。如果您发现任何可以改进的地方,请创建问题,甚至可以自行修复。所有可访问性问题均由我负责。
进一步阅读
- Web 组件:从零到英雄
- open-wc - 开放 Web 组件建议
- Web 组件和 AOM
- Web 组件的可访问性
- Web 组件缺陷清单
- Web 组件仍然需要可访问
- Web 组件 - 正确方法
- 基本自定义控制要求
关于无障碍,欢迎问我任何问题!也欢迎你在Twitter或Twitch上提问(我每周都会直播无障碍相关的内容)。
鏂囩珷鏉ユ簮锛�https://dev.to/erikkroes/the-guide-to-accessible-web-components-1mkd