无障碍最佳实践
快速总结
由于 不使用 ARIA 比错误使用 ARIA 更好,
在使用 aria-*、role 和 tabindex 之前,请先查看以下建议。
使用语义化 HTML,它内置了无障碍语义,并建议使用
屏幕阅读器和浏览器的相关组合 进行测试。
在 WebAIM 对顶级百万首页的无障碍分析 中,
他们发现 “ARIA 与更高的可检测错误相关联”。
很可能是 ARIA 的误用导致了错误增加,因此当有疑问时,不要使用 aria-*、role 和 tabindex,而是坚持使用语义化 HTML。
在 macOS 上启用键盘导航
默认情况下,macOS 将 tab 键限制为仅文本框和列表。要启用完整的键盘导航:
- 打开 系统偏好设置。
- 选择 键盘。
- 打开 快捷键 选项卡。
- 启用 使用键盘导航在控件之间移动焦点 的设置。
您可以在 a11yproject 上阅读更多关于启用特定浏览器的键盘导航的信息。
快速检查清单
- 文本、 文本域、 选择框、 复选框、 单选按钮、 文件 和 切换开关 输入框都有可访问的名称。
- 按钮、 链接 和 图片 都有描述性的可访问名称。
- 图标
- 交互式元素可以 通过 Tab 键访问 并且有可见的焦点状态。
- 带 工具提示 的元素可以通过 Tab 键获得焦点。
- 是否有任何
role、tabindex或aria-*属性是不必要的? - 是否可以将任何
div或span元素替换为更语义化的 HTML 元素,如p、button或time?
提供良好的文档大纲
标题是屏幕阅读器用户导航内容的主要机制。 因此,页面上的标题结构应该合理,就像一个好的目录。 我们应该确保:
- 页面上只有一个
h1元素。 - 不跳过标题级别。
- 标题级别正确嵌套。
为屏幕阅读器提供可访问名称
要提供具有可访问名称的标记,确保每个:
- input 都有关联的 label。
- 按钮和链接有 可见文本,或者在没有可见文本时使用
aria-label,例如没有内容的图标按钮。 - 图片有
alt属性。 - 图表有长描述和短描述。
fieldset的第一个子元素是legend。figure的第一个子元素是figcaption。table的第一个子元素是caption。
请记住,alt 属性 不应超过约 150 个字符。虽然对此没有官方指南,但一些屏幕阅读器不会读取 alt 属性中较长的字符串。
可以通过多种方式提供可访问名称,并通过 可访问名称计算 决定。以下是不同技术优先级的简化顺序:
aria-labelledbyaria-labelalt、legend、figcaption或captiontitle。
提供可访问名称的示例
以下小节包含渲染具有可访问名称的 HTML 元素的标记示例。
请注意,使用 GlFormGroup 时:
- 仅传递
labelprop 会渲染一个包含label值的fieldset和legend。 - 同时传递
label和label-forprop 会渲染一个指向具有相同label-forID 的表单输入的label。
具有可访问名称的表单输入
复选框和单选按钮组应该在一个带有 legend 的 fieldset 中分组。
legend 为复选框和单选按钮组提供标签。
如果不希望 label、子文本或子元素在视觉上显示,
使用类名 gl-sr-only 来隐藏元素,但屏幕阅读器除外。
文件输入示例:
<!-- 带标签的文件输入 -->
<label for="attach-file">{{ __('附加文件') }}</label>
<input id="attach-file" type="file" />
<!-- 带隐藏标签的文件输入 -->
<label for="attach-file" class="gl-sr-only">{{ __('附加文件') }}</label>
<input id="attach-file" type="file" />具有可访问名称的图片
图片示例:
<img :src="imagePath" :alt="__('图片的描述')" />
<!-- SVG 隐式具有图形角色,因此如果语义上是图片,我们应该应用 `role="img"` -->
<svg role="img" :alt="__('图片的描述')" />
<!-- 装饰性图片,对屏幕阅读器隐藏 -->
<img :src="imagePath" :alt="" />具有描述性可访问名称的按钮和链接
按钮和链接应该有足够描述性的可访问名称,以便能够独立理解。
<!-- 不好的示例 -->
<gl-button @click="handleClick">{{ __('提交') }}</gl-button>
<gl-link :href="url">{{ __('页面') }}</gl-link>
<!-- 好的示例 -->
<gl-button @click="handleClick">{{ __('提交审核') }}</gl-button>
<gl-link :href="url">{{ __("GitLab 的无障碍页面") }}</gl-link>Role
通常,避免使用 role。
使用隐式具有 role 的语义化 HTML 元素代替。
| 不好的示例 | 好的示例 |
|---|---|
<div role="button"> |
<button> |
<div role="img"> |
<img> |
<div role="link"> |
<a> |
<div role="header"> |
<h1> 到 <h6> |
<div role="textbox"> |
<input> 或 <textarea> |
<div role="article"> |
<article> |
<div role="list"> |
<ol> 或 <ul> |
<div role="listitem"> |
<li> |
<div role="table"> |
<table> |
<div role="rowgroup"> |
<thead>、<tbody> 或 <tfoot> |
<div role="row"> |
<tr> |
<div role="columnheader"> |
<th> |
<div role="cell"> |
<td> |
支持仅键盘使用
键盘用户依赖焦点轮廓来理解他们在页面上的位置。因此,如果一个元素是交互式的,您必须确保:
- 它可以接收键盘焦点。
- 它有可见的焦点状态。
使用语义化 HTML,如 a (GlLink) 和 button (GlButton),它们默认提供这些行为。
请记住:
- Tab 和 Shift-Tab 应该只在交互式元素之间移动,而不是静态内容。
- 当您添加
:hover样式时,在大多数情况下您也应该添加:focus样式,以便样式同时应用于鼠标和键盘用户。 - 如果您移除交互式元素的
outline,请确保通过其他方式(如使用box-shadow)保持视觉焦点状态。
更多详情请参阅 Pajamas 仅键盘页面。
tabindex
优先不使用 tabindex,因为:
- 使用语义化 HTML,如
button(GlButton),隐式提供tabindex="0"。 - Tab 顺序应该与视觉阅读顺序匹配,而正数的
tabindex会干扰这一点。
避免使用 tabindex="0" 使元素可交互
使用交互式元素代替 div 和 span 标签。
例如:
一旦标记在语义上完整,使用 CSS 更新到所需的视觉状态。
<!-- 不好的示例 -->
<div role="button" tabindex="0" @click="expand">展开</div>
<!-- 好的示例 -->
<gl-button class="gl-p-0!" category="tertiary" @click="expand">展开</gl-button>不要在交互式元素上使用 tabindex="0"
交互式元素已经可以通过 Tab 键访问,因此添加 tabindex 是多余的。
<!-- 不好的示例 -->
<gl-link href="help" tabindex="0">帮助</gl-link>
<gl-button tabindex="0">提交</gl-button>
<!-- 好的示例 -->
<gl-link href="help">帮助</gl-link>
<gl-button>提交</gl-button>不要在供屏幕阅读器读取的元素上使用 tabindex="0"
屏幕阅读器可以读取不可 Tab 访问的文本。
使用 tabindex="0" 是不必要的,并且可能导致问题,
因为屏幕阅读器用户期望能够与之交互。
<!-- 不好的示例 -->
<p tabindex="0" :aria-label="message">{{ message }}</p>
<!-- 好的示例 -->
<p>{{ message }}</p>不要使用正数的 tabindex
始终避免使用 tabindex="1"
或更大值。
图标
图标可以分为三种不同类型:
- 装饰性图标
- 传达信息的图标
- 可点击的图标
装饰性图标
当从 UI 中移除图标不会丢失用户信息时,图标就是装饰性的。
由于 GitLab 中的大多数图标都是装饰性的,GlIcon 会自动将其渲染的图标对屏幕阅读器隐藏。
因此,您不需要向 GlIcon 添加 aria-hidden="true",因为这是多余的。
<!-- 不必要的:gl-icon 默认对屏幕阅读器隐藏图标 -->
<gl-icon name="rocket" aria-hidden="true" />
<!-- 好的示例 -->
<gl-icon name="rocket" />传达信息的图标
如果从 UI 中移除图标会导致用户信息丢失,图标就会传达信息。
一个例子是机密图标,它传达问题是机密的,并且旁边没有文本"机密"。
传达信息的图标必须有可访问的名称,以便信息也能传达给屏幕阅读器用户。
<!-- 不好的示例 -->
<gl-icon name="eye-slash" />
<!-- 好的示例 -->
<gl-icon name="eye-slash" :aria-label="__('机密问题')" />可点击的图标
可点击的图标在语义上是按钮,因此应该渲染为按钮,并具有可访问的名称。
<!-- 不好的示例 -->
<gl-icon name="close" :aria-label="__('关闭')" @click="handleClick" />
<!-- 好的示例 -->
<gl-button icon="close" category="tertiary" :aria-label="__('关闭')" @click="handleClick" />隐藏元素
在适当的时候,使用下表隐藏元素。
| 对视力正常的用户隐藏 | 对屏幕阅读器隐藏 | 对视力正常的用户和屏幕阅读器都隐藏 |
|---|---|---|
.gl-sr-only |
aria-hidden="true" |
display: none、visibility: hidden 或 hidden 属性 |
从屏幕阅读器隐藏装饰性图片
为了减少屏幕阅读器用户的噪音,使用 alt="" 隐藏装饰性图片。
如果图片不是 img 元素,例如内联 SVG,您可以通过添加 role="img" 和 alt="" 来隐藏它。
gl-icon 组件自动将其图标对屏幕阅读器隐藏,因此使用 gl-icon 时 aria-hidden="true" 是不必要的。
<!-- 好的示例 - 装饰性图片对屏幕阅读器隐藏 -->
<img src="decorative.jpg" alt="">
<svg role="img" alt="" />
<gl-icon name="epic" />何时使用 ARIA
使用语义化 HTML 时不需要 ARIA,因为它已经包含了无障碍功能。
然而,一些 UI 模式没有语义化 HTML 等价物。 这些的一般示例是对话框(模态框)和选项卡。 GitLab 特定的示例是分配人和标签下拉框。 构建这样的组件需要 ARIA 来使它们对屏幕阅读器可理解。 应进行适当的研究和测试,以确保符合 WCAG。