Привет, Хабр! В предыдущей статье я рассказывал о простых случаях проблем с доступностью, исправив которые можно сделать свой сайт или 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 доступным, вы во много повторяете функциональность, уже заложенную в браузеры.

В любом случае, я надеюсь, что эта небольшая прогулка на волне моих мыслей оказалась для вас полезной. Теперь вы знаете, как можно справиться с проблемами доступности, которые, на первый взгляд, кажутся неприступными.

И помните, что тот факт, что вы дочитали этот материал до конца, означает, что вы знаете, как решать проблемы с доступностью лучше, чем большинство разработчиков.

Спасибо за внимание, всем добра!