ARIA-атрибут role с несколькими значениями
Опубликовано:Недавно случайно узнала, что ARIA-атрибут role
может содержать больше одного значения. И это очень неожиданно, по крайней мере для меня.
Этот пост написала из исследовательского интереса, так что в нём нет большого количества полезных практических советов. А что-то даже лучше не применять на практике.
Небольшая справка о роли элемента
Роль содержит информацию о функциях элемента и как с ним можно взаимодействовать. К примеру, о <button>
скринридер объявит, что это кнопка, а также сможет на неё нажать.
У элементов может быть либо встроенная роль, либо явно заданная в атрибуте role
.
Есть много категорий и видов ролей, и существуют чёткие правила их явного использования. Например, нельзя менять роли при взаимодействии с элементом или задавать абстрактные (landmark
, section
, widget
и другие). Также есть роли, которые предполагают вложенные элементы. Например, в элемент с role="menu"
должен быть вложен как минимум один с menuitem
. Однако главное правило — стараться как можно реже явно задавать роли, особенно таким образом переопределять семантику элементов.
Что там в документации
В WAI-ARIA (Web Accessibility Initiative — Accessible Rich Internet Applications), как в самом очевидном источнике о role=""
, нет примеров с двумя ролями. И про несколько значений у этого атрибута пришлось поискать.
WAI-ARIA 1.1
В пункте 7.1 Role Attribute собраны характеристики атрибута роли, которые нужно учитывать в языках реализации (host languages):
- Имя атрибута должно быть
role
.- Значение атрибута должно допускать список токенов в качестве значения.
- Появление имени литерала любой конкретной роли в качестве одного из токенов не должно само по себе делать недопустимым значение атрибута в синтаксисе языка реализации.
- Первый литерал имени неабстрактной роли в списке токенов в атрибуте
role
определяет роль в соответствии с тем, как User Agent должен обрабатывать элемент. То, как User Agent обрабатывает роли, установлено в Core Accessibility API Mappings.
Раз WAI-ARIA отсылает нас к другой документации, то давайте почитаем теперь её.
Core Accessibility API Mappings 1.1
Это спецификация для браузеров и любого другого User Agent. В ней описано, как они должны взаимодействовать с Accessibility API.
В пункте 5.4 Role mapping есть то, что нам нужно:
Традиционно Accessibility API платформы имеют ограниченный набор заранее установленных ролей, которые ожидаются вспомогательными технологиями на этой платформе, и предоставлены могут быть только одна или две роли. Напротив, WAI-ARIA позволяет указывать несколько ролей в виде упорядоченного набора валидных токенов ролей, разделённых пробелами. Дополнительные роли — фоллбэк для других ролей, что похоже на концепцию указания нескольких шрифтов на случай, если первый тип шрифта не поддерживается.
В этом пункте собраны и сами правила работы с ролями из WAI-ARIA.
- User Agent должен использовать первый токен в их последовательности в значении атрибута роли, который соответствует имени любой неабстрактной роли из таблицы маппинга ролей… Обратите внимание, что, когда роли из WAI-ARIA перезаписывают семантику языков реализации, то DOM (Document Object Model) не изменяется, только дерево доступности.
- User Agent не должен мапировать роли, которые определены в спецификации WAI-ARIA как «абстрактные», с помощью стандартного механизма ролей Accessibility API.
HTML
Давным-давно в спецификации по HTML был пункт 3.2.7.3.1 ARIA Role Attribute, который тоже описывал возможность добавлять несколько ролей для элемента:
Для каждого HTML-элемента можно указать ARIA-атрибут
role
. Это атрибут роли из ARIA, который определён в спецификации в Section 5.4 Definition of Roles.
Атрибут, если он задан, должен иметь значение, которое выглядит как набор токенов, разделённых пробелами и представляющих различные роли WAI-ARIA, к которым относится элемент.
Роль WAI-ARIA, которая назначена для HTML-элемента, первая неабстрактная роль, найденная в списке значений, сгенерированных, когда атрибутrole
разделён пробелами.
Пункта больше нет, но интернет помнит всё.
Будущее
В следующих, пока что черновых версиях WAI-ARIA 1.2 и Core Accessibility API Mappings 1.2, нет ничего нового про несколько атрибутов у role=""
. Единственное, в спецификации для User Agent появилось больше деталей о поддержке ролей разными реализациями Accessibility API.
Перевод с документационного
Какие выводы можно сделать после путешествия по спецификациям?
- В атрибуте
role
можно указывать несколько значений. - Несколько значений перечисляется стандартно через пробел.
- Несколько ролей нужны для фоллбэка. Если первая роль не поддерживается или не существует, то применяется вторая и так далее.
- Если это абстрактная роль, то браузер и скринридер её проигнорируют.
Тестируем
Давайте проверим, что будут делать браузеры и скринридеры с двумя значениями в атрибуте role
. Поэкспериментируем в Chrome 97, Firefox 96 и Safari 14 с NVDA 2021.2 и десктопным VoiceOver.
Кстати, в старых браузерах и скринридерах это не получится проверить. Они просто игнорируют role=""
с несколькими значениями. Имейте это в виду.
Разметка, которую буду тестировать, может напугать. Это проверка работы одного атрибута, так что решила хотя бы раз в жизни использовать запрещённые приёмы 😀 В реальных проектах так лучше никогда не делать. Это ужасный антипаттерн.
Оба значения существуют
<div role="button link" aria-label="Это не кнопка" tabindex="1">Что-то преступное</div>
- NVDA и Chrome: «Это не кнопка, кнопка».
- NVDA и Firefox: «Кнопка».
- VoiceOver и Safari: «Это не кнопка».
В Chrome и Safari <div>
c role="button link"
получил роль button
, в Firefox — pushbutton
.
Вывод: когда два значения валидные, то в дерево попадает самое первое.
Одно значение существует, другое нет
<div role="opossum button" aria-label="Это не кнопка" tabindex="1">Что-то преступное</div>
- NVDA и Chrome: «Это не кнопка, кнопка».
- NVDA и Firefox: «Кнопка».
- VoiceOver и Safari: «Это не кнопка».
В Chrome и Safari <div>
c role="opossum button"
получил роль button
, в Firefox — pushbutton
.
Вывод: если одно значение невалидное, а второе валидное, то в дерево попадает существующее.
Одно значение абстрактное, другое нет
<div role="widget button" aria-label="Это не кнопка" tabindex="1">Что-то преступное</div>
- NVDA и Chrome: «Это не кнопка, кнопка».
- NVDA и Firefox: «Кнопка».
- VoiceOver и Safari: «Это не кнопка».
В Chrome и Safari <div>
c role="widget button"
получил неабстрактную роль button
, в Firefox — pushbutton
.
Вывод: когда есть абстрактная роль, то она игнорируется и применяется неабстрактная.
Обоих значений не существует
<div role="tapir opossum" aria-label="Это не кнопка" tabindex="1">Что-то преступное</div>
- NVDA и Chrome: ничего не объявляет.
- NVDA и Firefox: ничего не объявляет.
- VoiceOver и Safari: «Это не кнопка». Объявляет только содержимое атрибута
aria-label
.
В Chrome для <div>
c role="tapir opossum"
вычислена роль generic
, в Firefox — text leaf
, а в Safari просто не нашлось подходящей роли.
generic
— это встроенная роль <div>
. Это значит, что перед нами безымянный элемент-контейнер без семантического значения. А text leaf
означает какой-то текстовый контент.
Вывод: если оба значения невалидные, то в дерево доступности или попадает встроенная роль элемента, или ничего не вычисляется. Зависит от браузера.
Финальные мысли
Всё это время WAI-ARIA предусматривала возможность задавать для атрибута role
больше одного значения, но не особо это афишировала.
Не думаю, что это ужасное упущение в спецификации. Сложно представить, для каких ролей нужны фоллбеки в современных браузерах. К тому же, с атрибутом role
и так легко запутаться. Когда у тебя есть возможность задать бесконечное количество ролей, то это ещё больше усложняет разметку и может привести к неожиданным ошибкам.
Что почитать
- Accessible Rich Internet Applications (WAI-ARIA) 1.1.
- Core Accessibility API Mappings 1.1.
role
attribute has valid value, ACT-Rules Community.- Notes on use of multiple ARIA role attribute values, Стив Фолкнер.
Спасибо Василию Дудину за помощь с редактированием.
Другие статьи
- CSS-медиафичи для улучшения доступности Следующая статья →
- Принципы WCAG. Консистентная идентификация ← Предыдущая статья