Help us learn about your current experience with the documentation. Take the survey.

无障碍最佳实践

快速总结

由于 不使用 ARIA 比错误使用 ARIA 更好, 在使用 aria-*roletabindex 之前,请先查看以下建议。 使用语义化 HTML,它内置了无障碍语义,并建议使用 屏幕阅读器和浏览器的相关组合 进行测试。

WebAIM 对顶级百万首页的无障碍分析 中, 他们发现 “ARIA 与更高的可检测错误相关联”。 很可能是 ARIA 的误用导致了错误增加,因此当有疑问时,不要使用 aria-*roletabindex,而是坚持使用语义化 HTML。

在 macOS 上启用键盘导航

默认情况下,macOS 将 tab 键限制为仅文本框和列表。要启用完整的键盘导航:

  1. 打开 系统偏好设置
  2. 选择 键盘
  3. 打开 快捷键 选项卡。
  4. 启用 使用键盘导航在控件之间移动焦点 的设置。

您可以在 a11yproject 上阅读更多关于启用特定浏览器的键盘导航的信息。

快速检查清单

提供良好的文档大纲

标题是屏幕阅读器用户导航内容的主要机制。 因此,页面上的标题结构应该合理,就像一个好的目录。 我们应该确保:

  • 页面上只有一个 h1 元素。
  • 不跳过标题级别。
  • 标题级别正确嵌套。

为屏幕阅读器提供可访问名称

要提供具有可访问名称的标记,确保每个:

  • input 都有关联的 label
  • 按钮和链接有 可见文本,或者在没有可见文本时使用 aria-label,例如没有内容的图标按钮。
  • 图片有 alt 属性
  • 图表有长描述和短描述
  • fieldset 的第一个子元素是 legend
  • figure 的第一个子元素是 figcaption
  • table 的第一个子元素是 caption

请记住,alt 属性 不应超过约 150 个字符。虽然对此没有官方指南,但一些屏幕阅读器不会读取 alt 属性中较长的字符串。

可以通过多种方式提供可访问名称,并通过 可访问名称计算 决定。以下是不同技术优先级的简化顺序:

  1. aria-labelledby
  2. aria-label
  3. altlegendfigcaptioncaption
  4. title

提供可访问名称的示例

以下小节包含渲染具有可访问名称的 HTML 元素的标记示例。

请注意,使用 GlFormGroup

  • 仅传递 label prop 会渲染一个包含 label 值的 fieldsetlegend
  • 同时传递 labellabel-for prop 会渲染一个指向具有相同 label-for ID 的表单输入的 label

具有可访问名称的表单输入

复选框和单选按钮组应该在一个带有 legendfieldset 中分组。 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),它们默认提供这些行为。

请记住:

  • TabShift-Tab 应该只在交互式元素之间移动,而不是静态内容。
  • 当您添加 :hover 样式时,在大多数情况下您也应该添加 :focus 样式,以便样式同时应用于鼠标和键盘用户。
  • 如果您移除交互式元素的 outline,请确保通过其他方式(如使用 box-shadow)保持视觉焦点状态。

更多详情请参阅 Pajamas 仅键盘页面

tabindex

优先不使用 tabindex,因为:

  • 使用语义化 HTML,如 button (GlButton),隐式提供 tabindex="0"
  • Tab 顺序应该与视觉阅读顺序匹配,而正数的 tabindex 会干扰这一点。

避免使用 tabindex="0" 使元素可交互

使用交互式元素代替 divspan 标签。 例如:

  • 如果元素应该可点击,使用 button (GlButton)。
  • 如果元素应该可编辑文本,使用 inputtextarea

一旦标记在语义上完整,使用 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: nonevisibility: hiddenhidden 属性

从屏幕阅读器隐藏装饰性图片

为了减少屏幕阅读器用户的噪音,使用 alt="" 隐藏装饰性图片。 如果图片不是 img 元素,例如内联 SVG,您可以通过添加 role="img"alt="" 来隐藏它。

gl-icon 组件自动将其图标对屏幕阅读器隐藏,因此使用 gl-iconaria-hidden="true" 是不必要的。

<!-- 好的示例 - 装饰性图片对屏幕阅读器隐藏 -->

<img src="decorative.jpg" alt="">

<svg role="img" alt="" />

<gl-icon name="epic" />

何时使用 ARIA

使用语义化 HTML 时不需要 ARIA,因为它已经包含了无障碍功能。

然而,一些 UI 模式没有语义化 HTML 等价物。 这些的一般示例是对话框(模态框)和选项卡。 GitLab 特定的示例是分配人和标签下拉框。 构建这样的组件需要 ARIA 来使它们对屏幕阅读器可理解。 应进行适当的研究和测试,以确保符合 WCAG