Хабр, я снова пришёл к вам с практическими советами про доступность вместе с Ильёй. Мы показываем, как HTML и CSS могут улучшить или ухудшить её. Напоминаю, что Илья — мой незрячий знакомый, который помогает мне найти наши косяки в вёрстке.
Сегодня мы рассмотрим следующие аспекты:
- какие сюрпризы есть при использовании свойства
display
; - о чём нужно помнить при вёрстке кнопки «Показать пароль»;
- зачем нужны подсказки с помощью атрибута
aria-label
при разметке областей навигации; - как можно переборщить с заголовками и какие проблемы будут.
Давайте начнём!
▍ Подводные камни свойства display
для скринридера NVDA
В предыдущей части я рассказывал, что паттерн «visually-hidden» приводит к тому, что скринридер NVDA в браузере Firefox зачитывает текст отдельно. Так получается, потому что у элемента устанавливается значение block
для свойства display
.
Дальше я подумал: «А как будет озвучен текст, находящийся внутри элемента с установленными значениями flex
и grid
для свойства display
?» Ведь браузеры для их дочерних элементов устанавливают значение block
. Давайте посмотрим, к чему в итоге я пришёл.
В своём исследовании я использовал разметку блока с ценой.
<body>
<span class="price">
<span>94 682</span>₽ за ночь
</span>
</body>
При правильном варианте я предполагал, что скринридер должен озвучить полностью текст сразу. В Firefox должна быть ошибка, которая приводит к тому, что пользователям нужно будет сделать несколько действий.
Тестирование проводил в скринридерах NVDA и JAWS вместе с браузерами Google Chrome, Firefox, Edge и Opera. В каждом тесте я использовал значения flex
, inline-flex
, grid
и inline-grid
для элемента с классом .price
.
.price {
display: flex; /* также по очереди изменял значение на `inline-flex`, `grid` и `inline-grid` */
}
В результате скринридер JAWS зачитывает весь текст сразу во всех браузерах. NVDA делает так в Google Chrome, Edge и Opera. В браузер Firefox, как ожидалось, текст озвучивается за два нажатия клавиши со стрелкой вниз (↓
). При первом пользователи услышат число из отдельного элемента <span>
, а при втором — оставшийся текст.
Исправить такое поведение можно, скрыв отдельный элемент <span>
с сохранением доступа к его тексту. Сделает это значение presentation
для атрибута role
.
<body>
<span class="price">
<span role="presentation">94 682</span>₽ за ночь
</span>
</body>
Теперь для скринридера NVDA в элементе с классом .price
существует только текст «94 682₽ за ночь». Поэтому он будет озвучен сразу за одно взаимодействие пользователей.
▍ Как показать пароль пользователям скринридера
Одной из важнейших функций для меня является возможность показать введённый пароль. Реализуется он элементом в виде глазика рядом с полем для ввода пароля.
Честно, я думал, данный паттерн бесполезен для пользователей скринридера. Но Илья переубедил меня.
«Данная функция очень полезна. Во-первых, не нужно стирать весь пароль из-за одной ошибки при ручном вводе. Во-вторых, бывают ситуации, когда сохранённые пароли подставляются сами. Необходимо убедиться, правильный ли пароль».
А я думал, что функция не нужна. Хорошо, что Илья поправил меня. Кроме того, он рассказал, какие есть нюансы. Первый заключается в расположении кнопки.
«Перемещаюсь по форме с помощью клавиши Tab
через логин и пароль. Если есть необходимость отредактировать или проверить пароль, нажимаю кнопку, которая показывает пароль. Обычно она расположена после поля».
По этой причине я всегда рекомендую располагать кнопку за элементом input
.
<body>
<!-- здесь элементы формы -->
<label for="password">Пароль</label>
<input id="password" type="password">
<button type="button" aria-label="Показать пароль">
<!-- здесь иконка в виде глаза -->
</button>
<!-- здесь элементы формы -->
</body>
Обращаю ваше внимание, что кнопка должна быть кнопкой. Так пользователи скринридера и клавиатуры смогут на неё попасть.
Следующий нюанс заключается в том, как приложение работает с фокусом. Как говорит Илья, есть два сценария.
«В худшем случае приходится возвращаться от кнопки на шаг назад в поле и нажимать enter для входа в режим редактирования. В лучшем — фокус сразу попадает в поле с текстом».
Для демонстрации первого варианта подходит сайт Госуслуг.
Я уже сфокусировался на кнопку. Нажму на клавишу Enter
.
Я остался на ней. Мне нужно вернуться в поле редактирования. В моём случае я нажму сочетание клавиш Shift + Tab
. После этого я уже могу редактировать пароль. Илье потребуется ещё войти в режим редактирования поля.
Другой пример можно найти на сайте Сбера.
Я также нахожусь на кнопке «Показать пароль». Жму клавишу Enter
.
Сайт перенёс меня в поле. Я могу отредактировать пароль и авторизоваться.
▍ Помогаем скринридерам ориентироваться в областях навигации
Представьте, что вы работаете доставщиком еды и вам требуется доставить заказ. Вы на месте. Перед вами три одинаковых дома. Таблички с номером дома нет. Как вы найдёте нужный?
Такой квест проходят пользователи скринридера, когда на странице несколько областей навигации.
<body>
<nav>
<!-- область основной навигации -->
</nav>
<nav>
<!-- область хлебных крошек -->
</nav>
<nav>
<!-- область пользовательской навигации -->
</nav>
</body>
Давайте мысленно удалим комментарии и попробуем понять, где находится основная навигация на странице. Да чёрт знает, где она. Есть три области. Основная навигация может быть в любой из них.
С такой же проблемой сталкиваются скринридеры. Они тоже не знают, какая область за что отвечает. Вот так JAWS видит страницу в режиме «Список областей»:
Можно решить проблему, добавив подсказку о типе навигации. Она будет озвучена пользователю скринридера и отображена в специальных режимах. Сделать это можно с помощью атрибута aria-label
.
<body>
<nav aria-label="Основная">
<!-- элементы основной навигации -->
</nav>
<nav aria-label="Хлебные крошки">
<!-- область хлебных крошек -->
</nav>
<nav aria-label="Пользовательская">
<!-- элементы пользовательской навигации -->
</nav>
</body>
Вот теперь сразу понятно, где какая область навигации. А сейчас расскажу, как озвучиваются подсказки при переключении с помощью клавиши со стрелкой вниз (↓
).
В случае основной области навигации я слышу при входе: «Основная область навигации». А при выходе из неё: «Основная конец область навигации». В области хлебных крошек — «Хлебные крошки область навигации» и «Хлебные крошки конец область навигации».
В области пользовательской навигации он скажет: «Пользовательская область навигации» и «Пользовательская конец область навигации».
▍ Не весь текст это заголовок
У меня есть список вопросов, вызывающих массовое обсуждение. Давай посмотрим на заголовок и «подзаголовок» на следующем изображении.
Каким элементом вы разметили бы текст «Фокус внимания Правительства Нижегородской области»? Если хотите использовать элемент <h3>
, то вы удачно зашли. Не делайте так.
Проблема заключается в том, что пользователи скринридера используют заголовки для понимания структуры блока. Если им попадается ситуация, когда несколько заголовков определены для одного блока с контентом, то они будут удивлены. С первого раза такую структуру они не поймут. Вот, что говорит Илья:
«Заголовки размечают контент на части, служат ориентирами для быстрого перехода. Если два заголовка созданы для одного блока с контентом, это бьёт по логике его восприятия и задерживает навигацию горячими клавишами. Приходится дополнительно разбираться».
Решить эту проблему можно несколькими способами. Первый, это подойти к дизайнеру и сказать: «Друг, давай мы тут заголовок изменим, чтобы подзаголовка не было». Это будет наиболее лучший вариант. В моём примере блок можно изменить так:
Второе решение — сделать один заголовок.
<body>
<section>
<h2>
<span>Приоритеты</span>
<span>Фокус внимания Правительства Нижегородской области</span>
</h2>
<!-- здесь оставшиеся контент -->
</section>
</body>
Это моё решение. Я рекомендую использовать его, потому что для прочтения текста «Приоритеты Фокус внимания Правительства Нижегородской области» пользователю достаточно сделать одно взаимодействие. Правда Илья, сказал, что длинный текст это не очень здорово.
«В любом случае от длинных заголовков устаёшь больше, чем от коротких. Даже если прерываешь чтение клавишей H и идёшь дальше».
Третье решение от Ильи — оставить текст текстом. Это можно сделать, использовав элемент <p>
.
<body>
<section>
<h2>Приоритеты</h2>
<p>Фокус внимания Правительства Нижегородской области</p>
<!-- здесь оставшиеся контент -->
</section>
</body>
Но всё же я думаю, что моё решение подойдёт тоже, если текст сделать короче. В общем мы с Ильёй не нашли общее решение. Поэтому рассказал все, которые мы рассматривали.
▍ Заключение
С помощью этой статьи мы с Ильёй хотели призвать вас:
- если часть текста скрыта через паттерн, добавить к элементу
role="presentation"
; - расположить кнопку «Показать пароль» после поля для ввода пароля;
- переносить пользователя в поле для ввода пароля после использования кнопки «Показать пароль»;
- не использовать в атрибуте
aria-label
слова, описывающие тип элемента; - Внимательнее относиться к разметке заголовков, чтобы не запутывать пользователей скринридера.
Оставлю ссылки на все выпуски:
- Первый выпуск;
- Второй выпуск;
- Третий выпуск;
- Четвёртый выпуск;
- Пятый выпуск;
- Шестой выпуск;
- Седьмой выпуск;
- Восьмой выпуск.
Также нам будет интересен и ваш опыт. Делитесь своими кейсами в комментариях. Спасибо за чтение.
P.S. Если вы хотите больше узнать о цифровой доступности, добавляйтесь в мой ТГ канал. Ссылка в профиле.
P.S.S. Ещё я начал делиться своими практическими лайфхаками на Хабре. Серия статей называется «Магия CSS на практике: советы по вёрстке от гика»
Telegram-канал со скидками, розыгрышами призов и новостями IT ?
Комментарии (9)
kranid
09.07.2024 11:05Подзаголовок мог бы быть просто заголовком большего уровня. Это темболее имело бы смысл, если бы в статье было бы множество подразделов, т.к. тогда пользователь мог бы быстро изучать статью по разделам.
bubn0ff
09.07.2024 11:05По всем этим нюансам во всех выпусках хорошо бы составить некий "чек-лист доступности".
ifap
Может просто не оборачивать контент в два
span
подряд?melnik909 Автор
Всегда будут разработчики, которые обвернут.
ifap
Вряд ли такие разработчики будут заморачиваться доступностью, если у них сам код через известное место написан...
ilekarev
Так то представлен вариант, если появилась необходимость в этом оборачивании. Да и в места
span
, полагаю вторым тегом какойнибудьstrong
может так же быть?Какая проблема тут использовать два
span
?price__value
для стилизации значения. Либо вставить в текст для простого поиска значений для использования в JSwadowad
Такое семантичней или наоборот?
ilekarev
Я не думаю, что это семантично :) Здесь нет списка, и нет элементов "термин - описание".
wadowad
У DL смысл несколько шире, чем просто "термин - описание", скорее это "ключ - значение". А список есть (тут правда только из одного пункта). Всё встаёт на свои места, когда появляются варианты: