Хабр, я снова пришёл к вам с практическими советами про доступность вместе с Ильёй. Мы показываем, как HTML и CSS могут улучшить или ухудшить её. Напоминаю, что Илья — мой незрячий знакомый, который помогает мне найти наши косяки в вёрстке.
Сегодня мы рассмотрим следующие аспекты:
- какие скрытые проблемы с паттерном «visually-hidden» нас ждут;
- в каких ситуациях кнопка «Закрыть» указывает на выход;
- чем вредно значение
contents
у свойстваdisplay
; - почему подсказка с помощью атрибута
aria-label
вызывает недоумение.
Давайте начнём!
▍ Нежданчик с паттерном «visually-hidden»
Я занимаюсь цифровой доступностью 6 лет, и всё это время существует паттерн «visually-hidden». Скорее всего, вы его видели. Данная техника представляет собой набор свойств, позволяющих только скрыть визуально элемент. Вот они:
.visually-hidden {
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
Для демонстрации принципа работы я добавлю его для элемента <h1>
.
<body>
<h1 class="visually-hidden">Дизайнер пользовательского интерфейса Стас Мельников</h1>
</body>
Так я скрыл визуально элемент, но для скринридера он останется доступен. Он скажет: «Заголовок уровень один. Дизайнер пользовательского интерфейса Стас Мельников». Вроде всё отлично. Но есть момент. Вы знаете, что будет, если использовать паттерн для части текста?
Например, представим, что имя и фамилия должно визуально отобразиться, а текст «дизайнер пользовательского интерфейса» должен быть доступен только скринридерам. Для этого нужно его обвернуть элементом с классом .visually-hidden
.
<body>
<h1 class="logo">
<span class="visually-hidden">Дизайнер пользовательского интерфейса</span>
<span class="logo__firstname">Стас</span>
<span class="logo__lastname">Мельников</span>
</h1>
</body>
Скринридере NVDA во всех браузерах озвучит текст за раз, кроме браузера Firefox. Для полного прочтения пользователю нужно нажать клавишу со стрелкой вниз (↓
) два раза. После первого нажатия он скажет: «Заголовок уровень один. Дизайнер пользовательского интерфейса». После второго: «Заголовок уровень один. Стас Мельников».
Почему так? Дело в том, что в паттерне используется position: absolute
. Значение absolute
добавляет для элемента display: block
. А скринридер считывает такие элементы отдельно.
Для решения проблемы я предлагаю использовать атрибут role
со значением presentation
. Если вам интересно узнать больше деталей, у меня есть отдельная статья про атрибут. А сейчас просто добавим его в наш код.
<body>
<h1 class="logo">
<span class="visually-hidden" role="presentation">Дизайнер пользовательского интерфейса</span>
<span class="logo__firstname">Стас</span>
<span class="logo__lastname">Мельников</span>
</h1>
</body>
Значение presentation
скрывает элемент, но оставляет доступным его контент. Таким образом, скринридер увидит только текст в элементе <h1>
.
▍ Кнопка «Закрыть» случайно прогоняет пользователя скринридера
В своих статьях я стараюсь показать, что разработчикам нужно быть внимательными при вёрстке. Случайно сделал неверный порядок элементов, сразу страдает доступность. Очередным примером будет модальное окно.
Рассмотрим следующую разметку:
<body>
<dialog class="modal">
<button type="button" aria-label="Закрыть">
<!-- здесь иконка --->
</button>
<!-- здесь контент модального окна -->
</dialog>
</body>
Как думаете, где здесь проблема? После открытия модального окна кнопка «Закрыть» будет первым элементом, на который попадёт пользователь при нажатии клавиши со стрелкой вниз (↓
) или Tab
. Илья говорит, что такое поведение негативно настраивает пользователя.
«Таким образом ты настраиваешь пользователя, что это главное действие на этом экране. Что маловероятно. У меня есть аналогия из бытовой жизни. Заходишь в магазин и сразу видишь стрелку к выходу. Ощущение, что тебя там не ждут и хотят, чтобы быстрее ушёл».
Конечно, не все пользователи нажмут на кнопку в такой ситуации. Как говорит Илья, они попробуют поискать то, что им нужно.
«Сам факт фокуса на «Закрыть» вызывает подозрение, что с окном что-то не так. Я попробую табать вниз, чтобы посмотреть, есть ли там функционал, или экран пустой».
В итоге ошибка в порядке элементов приводит к тому, что люди тратят дополнительные усилия, чтобы выполнить свою задачу. По этой причине я предлагаю располагать кнопку «Закрыть» последним элементом в модальном окне.
<body>
<dialog class="modal">
<!-- здесь контент модального окна -->
<button type="button" aria-label="Закрыть">
<!-- здесь иконка --->
</button>
</dialog>
</body>
Тем более такая позиция позволяет использовать жест перехода к последнему элементу в скринридере TalkBack на платформе Android. Это мне подсказал тоже Илья.
▍ display: contents
для интерактивных элементов
На сегодняшний день CSS позволяет нам скрыть элемент с сохранением его контента с помощью значения contents
для свойства display
. Допустим, мы применили его для элемента <div>
.
<body>
<div class="container">
<button type="button">Кнопка</button>
</div>
</body>
body {
display: grid;
}
.container {
display: contents;
}
Стили элемента <body>
стали действовать на кнопку, которая встала на место скрывшегося элемента <div>
. По этой причине мы видим, что она растянулась на всю ширину элемента <body>
. В этом заключается вся мощь значения contents
. И в этом же скрыта большая проблема.
А что будет, если применить значение для элемента <button>
или <a>
? Давайте посмотрим.
<body>
<button class="control" type="button">Кнопка</button>
<a href="#0" class="control">Ссылка</a>
</body>
body {
display: grid;
}
.control {
display: contents;
}
Мы видим текст. Кнопка потеряла все стили. У ссылки остался только цвет. Вроде всё нормально. Где подвох?
Признаюсь. Когда я первый раз увидел эту демонстрацию, у меня была эйфория, и я подумал: «Вроде классный способ сбросить стили». Хорошо, что потом задумался о том, а можно ли взаимодействовать с такими элементами?
Сначала я нажал клавишу Tab
. Тут ждало меня разочарование. У меня не получилось сфокусироваться на элементы. Это означает, что скринридеры тоже не найдут их, если пользователи используют Tab
. А также для них останется доступен только текст, а сами элементы — нет. В итоге пришлось отказать от идеи. И это хорошо!
А потом я узнал, что кнопки и ссылки — не единственные элементы, для которых не стоит применять display: contents
. Adrian Roselli подробнее рассказал в своём исследовании. Я не вижу смысла делать пересказ в этой статье. Лучше сами прочтите. Там много интересного.
Завершу раздел своим личным выводом. Не стоит отказываться от display: contents
. Значение часто выручает. Лучше использовать его для элементов <div>
и <span>
. Скринридеры для этих элементов не делают подсказок, следовательно, отменить их не получится. Поэтому очень маленькая вероятность навредить пользователям.
▍ Как не перестараться с атрибутом aria-label
Я много общаюсь на тему цифровой доступности с разработчиками. Заметил одну проблему, которая появилась от большого желания людей сделать интерфейс доступным. Она есть в следующем коде:
<body>
<a href="/logout" aria-label="Кнопка выйти">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path d="M24 20v-4h-10v-4h10v-4l6 6zM22 18v8h-10v6l-12-6v-26h22v10h-2v-8h-16l8 4v18h8v-6z"/>
</svg>
</a>
</body>
Чтобы понять ошибку, нужно услышать, как скринридер озвучит ссылку. А сделает он вот так: «Кнопка выйти, ссылка».
Илья говорит, что такие элементы ставят в ступор:
«В программе экранного доступа можно выставить порядок озвучивания атрибутов элемента. Если у меня в начале будет стоять тип элемента, то я подумаю, что это кнопка, а ссылка — это подпись к кнопке.
Конечно, я нажму на такой элемент. Но у меня не будет уверенности в том, что будет дальше. Этот элемент неоднозначный для меня. Он как чёрный ящик. Поэтому мне приходится включать опыт, знания и фантазию».
Как же исправить ошибку? Нужно помнить, что для большинства HTML-элементов уже определены подсказки, сообщающие скринридерам назначение элементов. По этой причине они знают, что элемент <a>
является ссылкой.
Таким образом не надо использовать слова, которые уже используются в подсказках. Например, слова «Кнопка», «Ссылка», «Меню»», «Навигация». А уж тем более не надо добавлять слова противоречащие значению HTML-элемента. Скринридеры сами правильно расскажут про элемент, если вы используйте его правильно.
Осталось убрать из нашего примера слово «Кнопка».
<body>
<a href="/logout" aria-label="Выйти">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path d="M24 20v-4h-10v-4h10v-4l6 6zM22 18v8h-10v6l-12-6v-26h22v10h-2v-8h-16l8 4v18h8v-6z"/>
</svg>
</a>
</body>
▍ Заключение
С помощью этой статьи мы с Ильёй хотели призвать вас:
- если часть текста скрыта через паттерн, добавить к элементу
role="presentation"
; - следить за тем, чтобы кнопка «Закрыть» была последним элементом в модальном окне;
- использовать
display: contents
с осторожностью; - не использовать в атрибуте
aria-label
слова, описывающие тип элемента.
Оставлю ссылки на все выпуски:
- Первый выпуск;
- Второй выпуск;
- Третий выпуск;
- Четвёртый выпуск;
- Пятый выпуск;
- Шестой выпуск;
- Седьмой выпуск.
Также нам будет интересен и ваш опыт. Делитесь своими кейсами в комментариях. Спасибо за чтение.
P.S. Если вы хотите больше узнать о цифровой доступности, добавляйтесь в мой ТГ канал. Ссылка в профиле.
Автор: Стас Мельников