Привет, Хабр! В предыдущей статье я рассказывал о простых случаях проблем с доступностью, исправив которые можно сделать свой сайт или web-приложение гораздо доступнее. Я упоминал о правиле 80/20 и писал о проблемах, которые при наименьших затратах дают наибольший результат. Сегодня я бы хотел поговорить о другой группе проблем, которые входят в 20% и для решения которых нет готовых рецептов вроде «всегда заполняйте атрибут alt» или «используйте верные заголовки».
Выбирая формат повествования, я не придумал ничего лучше, чем просто описать ход своих мыслей на достаточно часто встречающемся примере.
Формализуем проблему
Как я уже говорил, для некоторых проблем в сфере доступности невозможно предложить какое-то простое решение. Эти проблемы очень вариативны, а иногда и вовсе уникальны для конкретного приложения. Помимо этого, ситуация осложняется различиями в работе разных скринридеров в разных браузерах и на разных операционных системах. Дополняет всё это то, что решение лежит на стыке двух дисциплин: доступности с её стандартами и
Interaction Design
(Проектирование взаимодействия) с её достаточно гибкими правилами.Если вы, как и я в такие моменты, чувствуете уныние, то предлагаю не решать все проблемы сразу, а двигаться постепенно.
Обычно я начинаю с описания алгоритма взаимодействия с моим элементом для пользователей без мышки. Пользователи клавиатуры — это большая группа пользователей, далеко не всегда с какими-то ограничениями. Иногда это просто люди, которые ценят скорость работы.
Почти все сложные проблемы с доступностью возникают на элементах, которые динамически изменяют контент после какого-то действия пользователя.
К таким элементам можно отнести:
- табы;
- модальные окна;
- аккордеон;
- меню (в том числе с большой вложенностью).
Каждый компонент их этого списка можно условно разделить на две составляющие:
- триггер — элемент, запускающий действие;
- целевой контент — элемент, который содержит в себе кусочек видоизмененного контента.
Реализация триггера
Первое, с чем я предлагаю разобраться, — это какой базовый элемент будет лежать в основе триггера. Использование элементов, которые сразу являются фокусируемыми и интерактивными, я считаю хорошей идеей. Поэтому сразу отбрасываю всевозможные реализации с помощью
div
, span
и т. д.В итоге я прихожу к первому вопросу: должен ли быть триггер ссылкой или кнопкой?
Ответ на этот вопрос звучит примерно так: «в зависимости от ситуации», но в целом, вы не ошибётесь, если будете всегда использовать кнопку.
Кнопка — это элемент, предназначенный для запуска каких-либо интерактивных элементов внутри страницы. Поэтому пользователь, нажимая на кнопку, будет готов, что на странице что-то изменится. Кажется, это то, что нам нужно. Тем не менее, я считаю, что ссылки имеют некоторые преимущества в некоторых случаях.
Поэтому я задаю себе второй вопрос: должен ли изменившийся контент получить фокус?
И опять ответом на этот вопрос будет фраза «в зависимости от ситуации». Но можно помочь себе, просто посмотрев на DOM-дерево и оценив, насколько далеко триггер находится от целевого контента.
Если целевой контент находится сразу после триггера, то стоит использовать кнопку и обойтись без автоматического фокуса на целевой элемент. Поскольку пользователь следующим «табом» попадает на появившийся контент.
<button>Trigger Text</button>
<div id="target">
<p>Target content.</p>
</div>
Но если целевой контент находится на некотором расстоянии от активирующего его элемента, то, возможно, стоит посмотреть в сторону не очень модных, но отлично работающих гиперссылок с фрагментами.
<a href="#target">Trigger Text</a>
…
<div id="target">
<p>Target content.</p>
</div>
Реальный пример
Предположим, в макете, предоставленном дизайнером, имеется элемент «Войти», расположенный в шапке сайта. И, согласно дизайну, по нажатию должно открыться модальное окно с формой входа. Сам код модального окна, скорее всего, находится в конце DOM-дерева. В этом случае использование ссылки не кажется мне плохой идеей.
При работе со ссылками, в отличие от кнопок, пользователь ожидает, что после клика он будет куда-то перенаправлен. И, в целом, модальные окна можно рассматривать как отдельные страницы, поэтому результат не будет неожиданным для пользователя.
Вернёмся к вопросам, поставленным выше: «Должен ли быть триггер кнопкой или ссылкой?» и «Должен ли фокус автоматически переключиться на целевой контент?» Ответ на эти вопросы можно найти, просто ответив на вопрос: «Как далеко друг от друга находятся триггер и целевой контент».
Далее я добавляю на страницу немного JavaScript, чтобы убедиться, что пользователи клавиатуры и мыши могут взаимодействовать с созданным компонентом. Конечно, не обходится без небольшой магии с
tabindex
и методом focus()
, которые заслуживают отдельной статьи.Теперь самое время подумать о пользователях скринридеров. Первое, что стоит сделать — это добавить атрибуты
aria-expanded
в триггер и aria-hidden
в целевой контент. // Таргет не активирован
Триггер - aria-expanded="false",
Целевой контент - aria-hidden="true".
// Пользователь нажал на таргет элемент
Триггер - aria-expanded="true",
Целевой контент - aria-hidden="false".
Ещё существует атрибут
aria-controls
, который позволяет явно указать взаимосвязь между целевым компонентом и триггером.<button aria-controls="target">Trigger Text</button>
<div id="target">
<p>Target content.</p>
</div>
Правда, он не всегда работает так, как это можно ожидать.
В качестве заключения
Для более наглядного примера я реализовал два
codepan
: один с триггером в виде кнопки, а второй со ссылкой (кнопка, ссылка). В чём разница, спросите вы? Всё просто: в примере со ссылкой целевой контент сразу получил фокус без каких-то дополнительных телодвижений со стороны разработчика.Это наводит меня на мысль, что, возможно, если вы работаете над SPA, то стоит задуматься, стоит ли делать SPA, а потом пытаться сделать его доступным, продумывая, куда перевести фокус в появившемся контенте. Где должен оказаться фокус после исчезновения появившегося контента, что будет с фокусом, когда целевой контент скроется? Ведь очень часто, пытаясь делать SPA доступным, вы во много повторяете функциональность, уже заложенную в браузеры.
В любом случае, я надеюсь, что эта небольшая прогулка на волне моих мыслей оказалась для вас полезной. Теперь вы знаете, как можно справиться с проблемами доступности, которые, на первый взгляд, кажутся неприступными.
И помните, что тот факт, что вы дочитали этот материал до конца, означает, что вы знаете, как решать проблемы с доступностью лучше, чем большинство разработчиков.
Спасибо за внимание, всем добра!