无障碍

Web 无障碍 (简称 a11y),是指确保没有任何障碍阻碍残疾人与万维网上的网站进行互动或访问的包容性做法。Vuetify 组件是为所有基于鼠标的操作提供键盘交互,并在适当的情况下利用 HTML5 语义元素。

Material Design 无障碍

Google 详细介绍了如何创建其组件。它们还提供了一些示例,说明如何在创建应用程序时确保使用最佳实践(超出了 Vuetify 默认支持的范围)。你可以在规范网站上找到有关 implementing accessibility 的更多信息。

激活槽位

Vuetify 对许多组件使用激活插槽,如 v-menu,v-dialog 等等。 在某些情况下,这些 activator 元素应该具有特定的 a11y 属性,将它们与它们的相应内容联系起来。 为了做到这一点,我们通过插槽范围传递必要的 a11y 选项。

  1. <!-- Vue HTML Markup -->
  2. <template>
  3. <v-menu>
  4. <template v-slot:activator="{ on, attrs }">
  5. <v-btn
  6. v-bind="attrs"
  7. v-on="on"
  8. >
  9. Click me
  10. </v-btn>
  11. </template>
  12. <v-list>
  13. <v-list-item @click="method">
  14. <v-list-item-title>Option 1</v-list-item-title>
  15. </v-list-item>
  16. <v-list-item disabled>
  17. <v-list-item-title>Option 2</v-list-item-title>
  18. </v-list-item>
  19. <v-list-item @click="method">
  20. <v-list-item-title>Option 3</v-list-item-title>
  21. </v-list-item>
  22. </v-list>
  23. </v-menu>
  24. </template>

当 activator 元素被呈现时,它现在将包含绑定的 a11y 属性

  1. <!-- Rendered Activator HTML Output -->
  2. <button
  3. type="button"
  4. role="button"
  5. aria-haspopup="true"
  6. aria-expanded="false"
  7. >
  8. Click me
  9. </button>

Item slots

在某些情况下,你将插槽用于组件,并且需要正确的 aria 属性才能绑定到元素。当使用 (v-slot) 时,这在插槽范围内传递给你,正如上面的 v-menu 示例所示。然而,还有其他更复杂的组件,你需要将属性绑定到正确的组件以确保获得适当的支持。

v-select 组件将自动配置所有需要 a11y 的属性。每个项目将生成对应的 id, aria-labelledby, aria-selectedroles

  1. <!-- Vue HTML Markup -->
  2. <template>
  3. <v-select
  4. :items="['Foo', 'Bar']"
  5. label="Items"
  6. ></v-select>
  7. </template>

渲染时,v-select 组件的内容将类似于:

  1. <!-- Rendered `v-select` content HTML Output -->
  2. <div
  3. role="listbox"
  4. tabindex="-1"
  5. >
  6. <div
  7. aria-labelledby="foo-list-item-12"
  8. aria-selected="true"
  9. class="v-list-item"
  10. role="option"
  11. tabindex="0"
  12. >
  13. <div
  14. class="v-list-item__title"
  15. id="foo-list-item-12"
  16. >
  17. Foo
  18. </div>
  19. <div
  20. aria-labelledby="bar-list-item-13"
  21. aria-selected="false"
  22. class="v-list-item"
  23. role="option"
  24. tabindex="0"
  25. >
  26. <div
  27. class="v-list-item__title"
  28. id="bar-list-item-13"
  29. >
  30. Bar
  31. </div>
  32. </div>
  33. </div>

但是,在某些情况下使用插槽时,你将需要利用传递的属性来获得正确的 aria 设置。这个信息同样也适用于 v-autocomplete, v-combobox and v-overflow-btn,因为它们都是 v-select 扩展而来。

template


  1. <template>
  2. <v-select
  3. :items="['Foo', 'Bar', 'Fizz', 'Buzz']"
  4. label="Fizzbuzz"
  5. >
  6. <template v-slot:item="{ item, attrs, on }">
  7. <v-list-item
  8. v-bind="attrs"
  9. v-on="on"
  10. >
  11. <v-list-item-title
  12. :id="attrs['aria-labelledby']"
  13. v-text="item"
  14. ></v-list-item-title>
  15. </v-list-item>
  16. </template>
  17. </v-select>
  18. </template>

Accessibility (无障碍) - 图1

当在插槽中提供 v-list-item 组件时,aria 属性会通过 attrs 数据属性,并且可以与 v-bind 绑定。

  1. <!-- Vue HTML Markup -->
  2. <template>
  3. <v-select
  4. :items="['Fizz', 'Buzz']"
  5. label="Items"
  6. >
  7. <template v-slot:item="{ item, attrs, on }">
  8. <v-list-item
  9. v-bind="attrs"
  10. v-on="on"
  11. >
  12. <v-list-item-content>
  13. <v-list-item-title
  14. :id="attrs['aria-labelledby']"
  15. v-text="item.title"
  16. ></v-list-item-title>
  17. <v-list-item-subtitle v-text="item.sub"></v-list-item-subtitle>
  18. </v-list-item-content>
  19. </v-list-item>
  20. </template>
  21. </v-select>
  22. </template>

为了正确地将标签与正确的 option 关联,你休要绑定 attrs.idv-list-item-title 上的 aria-labelled-by。这将去报文本被正确关联。请记住,如果你没有使用插槽,这些都会自动完成。

焦点管理和键盘交互

除了属性之外,诸如 v-menu 之类的组件还通过按 ↑ 和 ↓ 在选项之间导航来支持交互。

v-menu

v-menu 中,v-list-item 将自动配置为 menuitem 的作用。浏览 Menu 以获得有关组件功能的更多信息。

template script


  1. <template>
  2. <div class="text-center">
  3. <v-menu>
  4. <template v-slot:activator="{ on, attrs }">
  5. <v-btn
  6. v-bind="attrs"
  7. v-on="on"
  8. >
  9. Click me
  10. </v-btn>
  11. </template>
  12. <v-list>
  13. <v-list-item @click="method">
  14. <v-list-item-title>Option 1</v-list-item-title>
  15. </v-list-item>
  16. <v-list-item disabled>
  17. <v-list-item-title>Option 2</v-list-item-title>
  18. </v-list-item>
  19. <v-list-item @click="method">
  20. <v-list-item-title>Option 3</v-list-item-title>
  21. </v-list-item>
  22. </v-list>
  23. </v-menu>
  24. </div>
  25. </template>
  1. <script>
  2. export default {
  3. methods: {
  4. method () {
  5. // Perform an action
  6. },
  7. },
  8. }
  9. </script>

Accessibility (无障碍) - 图2

  1. <!-- Rendered `v-menu` content HTML Output -->
  2. <div
  3. class="v-menu__content"
  4. role="menu"
  5. >
  6. <div class="v-list">
  7. <div
  8. class="v-list-item"
  9. role="menuitem"
  10. tabindex="0"
  11. >
  12. ...
  13. </div>
  14. <div
  15. aria-disabled="true"
  16. class="v-list-item"
  17. role="menuitem"
  18. tabindex="-1"
  19. >
  20. ...
  21. </div>
  22. </div>
  23. </div>

v-list-item-group

v-list-item-group 组件在按 tab 键时,v-list-item 变得可以互动和导航。 它还将 v-list-item 配置为具有 listitem 的功能。参阅 List-item group 以获取更多关于组件特性的信息。

template script


  1. <template>
  2. <v-card
  3. class="mx-auto"
  4. max-width="500"
  5. >
  6. <v-list>
  7. <v-list-item-group v-model="model">
  8. <v-list-item
  9. v-for="(item, i) in items"
  10. :key="i"
  11. :disabled="item.disabled"
  12. >
  13. <v-list-item-content>
  14. <v-list-item-title v-text="item.text"></v-list-item-title>
  15. </v-list-item-content>
  16. </v-list-item>
  17. </v-list-item-group>
  18. </v-list>
  19. </v-card>
  20. </template>
  1. <script>
  2. export default {
  3. data: () => ({
  4. items: [
  5. {
  6. text: 'Item 1',
  7. },
  8. {
  9. text: 'Item 2',
  10. disabled: true,
  11. },
  12. {
  13. text: 'Item 3',
  14. },
  15. ],
  16. model: 0,
  17. }),
  18. }
  19. </script>

Accessibility (无障碍) - 图3

  1. <!-- Rendered `v-list-group` content HTML Output -->
  2. <div
  3. class="v-list-item-group"
  4. role="listbox"
  5. >
  6. <div
  7. aria-selected="true"
  8. class="v-list-item"
  9. role="listitem"
  10. tabindex="0"
  11. >
  12. ...
  13. </div>
  14. <div
  15. aria-disabled="true"
  16. aria-selected="true"
  17. class="v-list-item"
  18. role="listitem"
  19. tabindex="-1"
  20. >
  21. ...
  22. </div>
  23. <div
  24. aria-selected="false"
  25. class="v-list-item"
  26. role="listitem"
  27. tabindex="0"
  28. >
  29. ...
  30. </div>
  31. </div>

额外资源

虽然 Vuetify 尽可能保证你在开发应用程序中轻松地使用 a11y,但有时候仍然需要额外的信息。 下面可以找到有用的资源列表。