aria-level

Рассказываем об иерархии элементов вспомогательным технологиям.

Время чтения: 14 мин

Кратко

Скопировано

Свойство виджета из WAI-ARIA для указания места элементов в их иерархии. Это важно для заголовков, древовидных и ассоциативных списков и других похожих элементов.

aria-level помогает браузерам и вспомогательным технологиям рассказать об уровнях элементов, когда не можете передать их иерархию с помощью чистого HTML.

Пример

Скопировано
        
          
          <span  role="heading"  aria-level="1">  Гайвань</span><p>  Традиционная китайская посуда для заваривания чая.  Дословно переводится как «чаша с крышкой».</p><p>  Cостоит из трёх частей: крышки, чаши и блюдца. В чашу кладут  чайные листья для заварки. Крышка сохраняет тепло и усиливает  аромат чая. Блюдце используют как подставку для чаши,  чтобы было удобнее разливать заваренный чай по пиалам.</p><p>  Самые распространённые материалы, из которых делают  гайвани, — фарфор и керамика.</p>
          <span
  role="heading"
  aria-level="1"
>
  Гайвань
</span>
<p>
  Традиционная китайская посуда для заваривания чая.
  Дословно переводится как «чаша с крышкой».
</p>
<p>
  Cостоит из трёх частей: крышки, чаши и блюдца. В чашу кладут
  чайные листья для заварки. Крышка сохраняет тепло и усиливает
  аромат чая. Блюдце используют как подставку для чаши,
  чтобы было удобнее разливать заваренный чай по пиалам.
</p>
<p>
  Самые распространённые материалы, из которых делают
  гайвани, — фарфор и керамика.
</p>

        
        
          
        
      
Открыть демо в новой вкладке

Скринридеры прочтут код примерно так: «Гайвань, заголовок первого уровня».

Как пишется

Скопировано

Добавьте к нужному элементу aria-level с положительным числом от 1 и больше. Например, aria-level="3". Исключение — кастомные заголовки с ролью heading. Их максимальный уровень не может быть выше 6.

aria-level можно использовать для элементов со следующими явными ARIA-ролями:

Чем ниже элемент в иерархии или глубже вложен в другие, тем выше его уровень. Обычно во вложенных элементах уровень первого элемента, родителя, не указывают. Отсчёт начинается с первого вложенного элемента — ребёнка.

Пример со вложенными элементами.

        
          
          <!-- Нерабочий код ❌--><div>  Первый уровень  <span aria-level="2">    Второй уровень  </span>  <span aria-level="2">    Второй уровень    <span aria-level="3">      Третий уровень      <span aria-level="4">        Четвёртый уровень      </span>      <span aria-level="4">        Четвёртый уровень      </span>    </span>  </span></div>
          <!-- Нерабочий код ❌-->

<div>
  Первый уровень

  <span aria-level="2">
    Второй уровень
  </span>

  <span aria-level="2">
    Второй уровень

    <span aria-level="3">
      Третий уровень

      <span aria-level="4">
        Четвёртый уровень
      </span>

      <span aria-level="4">
        Четвёртый уровень
      </span>
    </span>
  </span>
</div>

        
        
          
        
      

Пример с отдельными элементами, когда важно показать их иерархию.

        
          
          <!-- Нерабочий код ❌--><span aria-level="1">  Первый уровень</span><span aria-level="2">  Второй уровень</span><span aria-level="3">  Третий уровень</span><span aria-level="4">  Четвёртый уровень</span><span aria-level="2">  Второй уровень</span><span aria-level="3">  Третий уровень</span>
          <!-- Нерабочий код ❌-->

<span aria-level="1">
  Первый уровень
</span>

<span aria-level="2">
  Второй уровень
</span>

<span aria-level="3">
  Третий уровень
</span>

<span aria-level="4">
  Четвёртый уровень
</span>

<span aria-level="2">
  Второй уровень
</span>

<span aria-level="3">
  Третий уровень
</span>

        
        
          
        
      

Заголовки

Скопировано

Используйте для заголовков теги <h1><h6>. У них по умолчанию есть свойство aria-level. Когда по крайне важной причине верстаете кастомные заголовки, учитывайте несколько особенностей поведения aria-level.

Заголовок с ролью heading без aria-level автоматически получает второй уровень.

        
          
          <span  role="heading">  Гайвань</span>
          <span
  role="heading"
>
  Гайвань
</span>

        
        
          
        
      
Открыть демо в новой вкладке

Проверим в инструменте разработчика в Chrome. Браузер вычисляет второй уровень в строке со свойством «Level».

Вычисленные стили заголовка без уровня в Chrome.

Пытливые умы могут добавить заголовок выше шестого уровня, чтобы перехитрить браузеры. Будьте готовы к тому, что эксперимент закончится неудачей. Например, здесь пытаемся убедить браузер в существовании заголовка десятого уровня:

        
          
          <!-- Не делайте так ❌--><span  role="heading"  aria-level="10">  Гайвань</span>
          <!-- Не делайте так ❌-->

<span
  role="heading"
  aria-level="10"
>
  Гайвань
</span>

        
        
          
        
      
Открыть демо в новой вкладке

Некоторые старые браузеры понимали значения до 9. Новые браузеры всегда вычисляют 2, когда уровень heading выше шестого. Так что десятый уровень aria-level из примера станет вторым.

Заголовок десятого уровня с вычисленными стилями в Chrome.

Лучше не вкладывать заголовки друг в друга, чтобы сверстать подзаголовки. Ради интереса попробуем вложить в один заголовок с aria-level="1" другой с aria-level="2".

        
          
          <!-- Не делайте так ❌--><span  role="heading"  aria-level="1">  Гайвань  <span    role="heading"    aria-level="2"  >    История  </span></span>
          <!-- Не делайте так ❌-->

<span
  role="heading"
  aria-level="1"
>
  Гайвань
  <span
    role="heading"
    aria-level="2"
  >
    История
  </span>
</span>

        
        
          
        
      
Открыть демо в новой вкладке

Браузеры решат, что это два отдельных заголовка первого и второго уровня.

Скринридеры вообще запутаются. Например, NVDA скажет: «Гайвань, заголовок первого уровня. История, заголовок первого уровня, заголовок второго уровня». В списке всех заголовков страницы будет правильная иерархия, но повторятся названия вложенных. Получается, что название заголовка первого уровня — «ГайваньИстория», а второго — «История».

Окно со списком всех заголовков в NVDA.

Древовидные списки

Скопировано

Тип списков, в которых видна иерархия элементов. Они часто встречаются в файловых системах, где видны папки и как они вложены.

Самому списку задают роль tree, а его пунктам — treeitem с нужным значением aria-level.

Допустим, у нас есть основная (корневая) папка «Мои файлы» с двумя вложенными «Всякое» и «Разное». Внутри «Всякое» есть текстовые файлы и ещё одна папка «Черновики». Папки «Всякое» и «Разное» напрямую вложены в основную, так что они первого уровня aria-level="1". Файлы и папки внутри первого уровня станут второго aria-level="2". В папке второго уровня «Черновики» находятся файлы третьего уровня aria-level="3".

        
          
          <h3 id="label">  Мои файлы</h3><!-- Основная папка --><ul  role="tree"  aria-labelledby="label">  <!-- Вложенная папка № 1 -->  <li    role="treeitem"    aria-level="1"    aria-expanded="false"    aria-selected="false"  >    <span>      Всякое    </span>    <!-- Файлы папки № 1 -->    <ul role="group">      <li        role="treeitem"        aria-level="2"        aria-selected="false"      >        final-report.docx      </li>      <!-- Папка, вложенная в папку № 1 -->      <li        role="treeitem"        aria-level="2"        aria-selected="false"      >        <span>          Черновики        </span>      </li>        <!-- Файлы вложенной папки -->        <ul role="group">          <li            role="treeitem"            aria-level="3"            aria-selected="false"          >            final-report-draft.docx          </li>        </ul>      </li>    </ul>  </li>  <!-- Вложенная папка № 2 -->  <li    role="treeitem"    aria-level="1"    aria-expanded="false"    aria-selected="false"  >    <span>      Разное    </span>    <!-- Файлы папки № 2 -->    <ul role="group">      <li        role="treeitem"        aria-level="2"        aria-expanded="false"        aria-selected="false"      >        <span>          file-1.docx        </span>      </li>      <!-- Другие элементы -->    </ul>  </li></ul>
          <h3 id="label">
  Мои файлы
</h3>
<!-- Основная папка -->
<ul
  role="tree"
  aria-labelledby="label"
>
  <!-- Вложенная папка № 1 -->
  <li
    role="treeitem"
    aria-level="1"
    aria-expanded="false"
    aria-selected="false"
  >
    <span>
      Всякое
    </span>
    <!-- Файлы папки № 1 -->
    <ul role="group">
      <li
        role="treeitem"
        aria-level="2"
        aria-selected="false"
      >
        final-report.docx
      </li>
      <!-- Папка, вложенная в папку № 1 -->
      <li
        role="treeitem"
        aria-level="2"
        aria-selected="false"
      >
        <span>
          Черновики
        </span>
      </li>
        <!-- Файлы вложенной папки -->
        <ul role="group">
          <li
            role="treeitem"
            aria-level="3"
            aria-selected="false"
          >
            final-report-draft.docx
          </li>
        </ul>
      </li>
    </ul>
  </li>

  <!-- Вложенная папка № 2 -->
  <li
    role="treeitem"
    aria-level="1"
    aria-expanded="false"
    aria-selected="false"
  >
    <span>
      Разное
    </span>
    <!-- Файлы папки № 2 -->
    <ul role="group">
      <li
        role="treeitem"
        aria-level="2"
        aria-expanded="false"
        aria-selected="false"
      >
        <span>
          file-1.docx
        </span>
      </li>
      <!-- Другие элементы -->
    </ul>
  </li>
</ul>

        
        
          
        
      

Древовидные сетки

Скопировано

Древовидная сетка похожа на таблицу, которая состоит из нескольких раскрывающихся ячеек. Данные внутри ячеек расположены в определённой иерархии. Вы могли видеть такие сетки в почтовых клиентах. В них важно показать связь писем между собой.

Сетке задают роль treegrid, а в строках row уже группируют ячейки gridcell.

В древовидных сетках aria-level может быть только у строк с ролью row. Предположим, мы хотим показать цепочку их трёх писем. У первого письма в ней будет первый уровень aria-level="1", у ответа на него — второй aria-level="2", а у ответа на ответ — третий aria-level="3".

        
          
          <h3 id="label">  Полученные письма</h3><table  role="treegrid"  aria-labelledby="label">  <colgroup>    <col id="col-1">    <col id="col-2">    <col id="col-3">  </colgroup>  <thead>    <tr>      <th scope="col">        Тема      </th>      <th scope="col">        Содержимое      </th>      <th scope="col">        Адрес      </th>    </tr>  </thead>  <tbody>    <!-- Первое письмо -->    <tr      role="row"      aria-level="1"      aria-expanded="true"    >      <td role="gridcell">        Продам картофель      </td>      <td role="gridcell">        Дорого. Медленно. Некачественно.      </td>      <td role="gridcell">        <a href="mailto:potato@potatomail.mail">          potato@potatomail.mail        </a>      </td>    </tr>    <!-- Ответ на первое письмо -->    <tr      role="row"      aria-level="2"    >      <td role="gridcell">        re: Продам картофель      </td>      <td role="gridcell">        У меня свой картошки полно.      </td>      <td role="gridcell">        <a href="mailto:potato2@potatomail.mail">          potato2@potatomail.mail        </a>      </td>    </tr>    <!-- Ответ на предыдущее письмо -->    <tr      role="row"      aria-level="3"    >      <td role="gridcell">        re: Продам картофель      </td>      <td role="gridcell">        Если у тебя много картошки, отдай тогда мне.      </td>      <td role="gridcell">        <a href="mailto:potato3@potatomail.mail">          potato3@potatomail.mail        </a>      </td>    </tr>  </tbody></table>
          <h3 id="label">
  Полученные письма
</h3>
<table
  role="treegrid"
  aria-labelledby="label"
>

  <colgroup>
    <col id="col-1">
    <col id="col-2">
    <col id="col-3">
  </colgroup>

  <thead>
    <tr>
      <th scope="col">
        Тема
      </th>
      <th scope="col">
        Содержимое
      </th>
      <th scope="col">
        Адрес
      </th>
    </tr>
  </thead>

  <tbody>
    <!-- Первое письмо -->
    <tr
      role="row"
      aria-level="1"
      aria-expanded="true"
    >
      <td role="gridcell">
        Продам картофель
      </td>
      <td role="gridcell">
        Дорого. Медленно. Некачественно.
      </td>
      <td role="gridcell">
        <a href="mailto:potato@potatomail.mail">
          potato@potatomail.mail
        </a>
      </td>
    </tr>

    <!-- Ответ на первое письмо -->
    <tr
      role="row"
      aria-level="2"
    >
      <td role="gridcell">
        re: Продам картофель
      </td>
      <td role="gridcell">
        У меня свой картошки полно.
      </td>
      <td role="gridcell">
        <a href="mailto:potato2@potatomail.mail">
          potato2@potatomail.mail
        </a>
      </td>
    </tr>

    <!-- Ответ на предыдущее письмо -->
    <tr
      role="row"
      aria-level="3"
    >
      <td role="gridcell">
        re: Продам картофель
      </td>
      <td role="gridcell">
        Если у тебя много картошки, отдай тогда мне.
      </td>
      <td role="gridcell">
        <a href="mailto:potato3@potatomail.mail">
          potato3@potatomail.mail
        </a>
      </td>
    </tr>
  </tbody>
</table>

        
        
          
        
      

Комментарии

Скопировано

На многих социальных платформах можно отвечать на другие комментарии. Визуально это выглядит как вложенные в друг друга блоки с именем пользователя, временем публикации и текстом комментария. Один из способов передать это на уровне кода — aria-level.

К примеру, первый пользователь отреагировал на пост в соцсети, другой оставил комментарий к нему, а третий — ответил на комментарий второго. Получается, первый комментарий с ролью comment будет без aria-level, второй — с aria-level="1", а третий — с aria-level="2".

        
          
          <!-- Изначальный комментарий --><div  role="comment">  <h3>А я — томат</h3>  <time datetime="2024-01-03T14:04">    3 дня назад  </time>  <p>    Люблю окрошку на квасе 🥰  </p>  <!-- Комментарий в ответ на первый -->  <div    role="comment"    aria-level="1"  >    <h3>Сырная косичка</h3>    <time datetime="2024-01-04T23:11">      2 дня назад    </time>    <p>      Окрошка на кефире лучшая!    </p>    <!-- Комментарий в ответ на второй -->    <div      role="comment"      aria-level="2"    >      <h3>Картошка фри</h3>      <time datetime="2024-01-05T10:34">        1 день назад      </time>      <p>        Фу, окрошка 🤢 А вот картошка…      </p>    </div>  </div></div>
          <!-- Изначальный комментарий -->
<div
  role="comment"
>
  <h3>А я — томат</h3>
  <time datetime="2024-01-03T14:04">
    3 дня назад
  </time>
  <p>
    Люблю окрошку на квасе 🥰
  </p>

  <!-- Комментарий в ответ на первый -->
  <div
    role="comment"
    aria-level="1"
  >
    <h3>Сырная косичка</h3>
    <time datetime="2024-01-04T23:11">
      2 дня назад
    </time>
    <p>
      Окрошка на кефире лучшая!
    </p>

    <!-- Комментарий в ответ на второй -->
    <div
      role="comment"
      aria-level="2"
    >
      <h3>Картошка фри</h3>
      <time datetime="2024-01-05T10:34">
        1 день назад
      </time>
      <p>
        Фу, окрошка 🤢 А вот картошка…
      </p>
    </div>
  </div>
</div>