Хабр, я уже третий месяц пишу про доступность вместе с Ильёй. Мы показываем, как HTML и CSS могут улучшить или ухудшить её. Напоминаю, что Илья — мой незрячий знакомый, который помогает мне найти наши косяки в вёрстке. Сегодня уже будет не только HTML и CSS. В некоторых кейсах мы будем использовать ARIA-атрибуты. Я расскажу: Давайте начнём! Стандарт Accessible Rich Internet Applications (WAI-ARIA) относит элементы <section> и <form> к группе навигационных элементов, с помощью которой мы можем дать быстрый доступ к основным областям страницы пользователям скринридера. Элемент <section> используется для доступа к разделам страницы, а элемент <form> — к формам. Только на практике нас ждёт сюрприз.  Начну с элемента <section>. Я проинспектирую следующую разметку скринридером JAWS. Он говорит мне: «Области на странице не найдены». Получается, для него элементов <section> не существует. Погуглив, я понял, что если не указывать текстовую подсказку для элемента, то он будет определён скринридерами как элемент <div>. Сделать это можно при помощи атрибута aria-labelledby.  Атрибут помогает скринридеру найти текстовое описание элемента. Другими словами, атрибут создаёт «связь» между элементом, который нужно описать, и элементом, который будет описывать его.  В нашем случае нужно описать элемент <section>, поэтому добавим к нему атрибут aria-labelledby, а элемент, который будет описывать его — это заголовок <h2>. К нему добавим атрибут id со значением, которое было использовано для атрибута aria-labelledby. 

Например, для раздела «Обо мне» я буду использовать значение about-me-heading, для раздела «Портфолио» — значение portfolio-heading, а для раздела «Контакты» — значение contacts-heading. Класс! Теперь JAWS увидел созданные разделы.  Перейдём теперь к формам. Для примера я создам следующую разметку: Проинспектировав её JAWS, он выдаёт такое же сообщение, как в примере с элементом <section>: «Области на странице не найдены».  В качестве решения можно также использовать атрибут aria-labelledby, но тогда нужно добавить заголовок внутрь формы. Но предположим, что мы не можем этого сделать. Здесь будет полезен атрибут aria-label.  Как в случае с атрибутом aria-label, он помогает браузеру найти текстовое описание. Только происходит это не через связь с помощью атрибута id, а просто можно написать текст. Например, «Подписаться на наши новости», а потом добавить его к элементу <form>: Вот теперь полный порядок. Скринридер нашёл форму, и пользователь может заполнить её. Кстати, если вы ничего не знаете об атрибуте aria-label, я написал отдельную статью, в которой детально рассказал о нём. Когда мы работали над одним проектом с Ильёй, однажды он прислал мне сообщение: Я открыл страницу и начал нажимать клавишу Tab. Первый Tab меню привёл в пункт «Министерство». Потом дошёл до пункта «Меры государственной поддержки АПК». Жму ещё раз. Попадаю вверх сайта к ссылке «Войти» вместо родительского пункта «Министерство».  Для объяснения причины такого поведения я упростил код шапки страницы до следующего:  Видите, у ссылок объявлено tabindex="1"? Собака зарыта в этом атрибуте.  По умолчанию браузеры переносят фокус согласно тому порядку, в каком интерактивные элементы находятся в разметке. Но если мы используем положительное значение для атрибута tabindex, то у элемента, к которому оно применено, появляется приоритет. Это произошло в примере выше.  Разработчики добавили для ссылок в меню tabindex="1", поэтому первый Tab переносил фокус к пункту «Министерство». Далее скринридер идёт по всем ссылкам, у которых также установлен атрибут tabindex. После пункта «Меры государственной поддержки АПК» идёт уже обычный порядок переноса фокуса, основываясь на порядке элементов. В коде первый интерактивный элемент это ссылка «Войти». Поэтому скринридер переносил Илью на эту ссылку из меню, тем самым запутывая его.  Но это не единственная причина запутывания. Илья также использует клавиши стрелок, чтобы переключаться между элементами. Что это такое? Пользователь скринридера с помощью клавиши ↓ переходит вниз по элементам, а с помощью клавиши ↑ — наверх.  При тестировании примера Илья нажал клавишу ↓, и первым элементом была ссылка «Войти», а не пункт «Министерство», как при нажатии клавиши Tab. Это тоже сбило с толку Илью. Пожалуйста, избегайте изменения порядка перехода фокуса. Тогда не будет проблем. Все элементы найдутся всеми доступными способами. Одной из распространённых проблем современных интерфейсов являются визуально скрытые элементы, но доступные для клавиатуры и скринридера. В случае взаимодействия при помощи клавиатуры я не могу показать пример, потому что показывать нечего. Визуально ничего не происходит. Я просто жму очень много раз клавишу Tab.  А вот взаимодействие скринридера с интерфейсом рассмотрим.



Перед тем, как сделать скриншот, я переключался по интерфейсу в скринридере JAWS, нажимая клавиши стрелок. Красная обводка появляется вокруг текущего элемента. А на скриншоте выделен подпункт меню, который визуально не отображается.  Давайте поговорим, как же можно скрыть элементы, чтобы они не были доступны скринридеру и клавиатуре. Первое, что приходит мне на ум, использование CSS-свойства display и visibility.  В этом случае все кнопки будут недоступны. Ещё одним способом является использование атрибутов aria-hidden и tabindex, как я показал в следующем фрагменте кода: Что лучше использовать? Нет универсального решения. Всё зависит от конкретного случая. Но я постараюсь выдать краткий совет.  Если у вас в результате действия пользователя появляется какой-то блок с интерактивными элементами, то лучше использовать CSS-свойства. Например, анимация появления меню, всплывающие окна и т. п. А HTML-атрибуты можно использовать для сокращения количества итераций или скрытия визуально важных элементов, но бесполезных для пользователя скринридера. К слову, в примере с меню достаточно использовать display: none для блока, содержащего все подпункты меню. Мне очень нравился приём, когда для обозначения обязательного поля символ * вставляется при помощи свойства content. Мне казалось это крутым, потому что HTML не захламляется.  К сожалению, мне пришлось отказаться от него. Когда я протестировал мою форму скринридером JAWS, то он для каждого поля добавлял «звёздочка». Представляю удивление незрячего. Вот, что думает Илья о ней: Поэтому лучше её скрыть. Для этого нужно сначала добавить символ в HTML и скрыть с помощью атрибута aria-hidden. А совсем хорошо будет помочь пользователю скринридера узнать, что поле ввода обязательно для заполнения. Тем более, это очень просто сделать. Просто нужно добавить атрибут aria-required со значением true. Теперь скринридер JAWS говорит: «Ваш email. Редактор обязательный. Введите текст». С помощью этой статьи мы с Ильёй хотели призвать вас: Оставлю ссылки на все части: первая, вторая и четвёртая. Также нам будет интересен и ваш опыт. Делитесь своими кейсами в комментариях. Спасибо за чтение. Помогаю узнать больше про Accessibility, HTML, CSS