Навигация с клавиатуры

5 июня 2020 г.9 мин

“The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect.”

Tim Berners-Lee, W3C Director and inventor of the World Wide Web

В большинстве статей по Accessibility вы прочтете, что доступность помогает людям с ограниченными возможностями. Но зачастую, мы не задумываемся об этом совсем или задумываемся в последнюю очередь (сроки то горят), лишая многих людей возможности пользоваться нашим приложением. Но сегодня я хочу поговорить именно о нас с вами, и о том, как мы можем упростить свою жизнь. Как вы уже поняли из названия, речь пойдет не об Accessibility в целом, а только об одном её аспекте - навигации с клавиатуры. Наверняка вы спросите “почему это так важно? и вообще, у меня есть мышка!”, но если задуматься, в некоторых ситуациях и мы с вами - “люди с ограниченными возможностями”. Представьте, что вы сломали руку, или у вас сломалась мышка (а вы сидите такой на карантине 😒), в обоих случаях у вас “ограниченные возможности”, и вам будет гораздо проще пользоваться приложением, если разработчики позаботились о навигации с клавиатуры. Да и вообще, зачастую клавиатурой пользоваться удобнее и быстрее. Сам я впервые столкнулся с проблемой доступности ещё в школе, когда родители прятали мышку, чтобы я не играл в игры и учил уроки 😁.

Допустим, вы решили добавить в свое приложение навигацию с клавиатуры, итак, какие существуют критерии, и на что необходимо обратить внимание? Здесь я не буду много говорить о реализации, а просто остановлюсь на основных моментах и концепциях. Далее, для краткости, под доступность я буду понимать именно доступность с клавиатуры.

Как узнать, доступно ли приложение с клавиатуры?

В первую очередь откройте ваше приложение и выбросьте мышку (желательно не далеко, она вам ещё понадобится). Затем попробуйте перемещаться по интерактивным элементам на странице: просто нажмите TAB для перехода к следующему элементу и Shift + Tab для перехода к предыдущему. Так же вы должны иметь возможность взаимодействовать с интерактивными элементами: нажимать на кнопки с помощью Enter и Spacebar, менять значения чекбоксов с помощью Spacebar и т.д.. Таким образом, если мы можем взаимодействовать со всеми элементами на странице с помощью клавиатуры, то наш приложение - доступно. На самом деле это только часть критерия, вторая часть состоит в том, что вам не нужно удерживать клавиши определенное количество времени для генерации события или вызова действия, но и здесь есть свои исключения, например: приложения для рисования и различные симуляторы. В статье речь пойдет только о первой части критерия.

Все функциональные элементы должны быть интерактивными

В первую очередь необходимо убедиться, что все функциональные элементы: кнопки, чекбоксы, формы и т.д. являются интерактивными, т.е. вы можете взаимодействовать с ними с помощью клавиатуры. В принципе, если у вас нет диватоза (вы используете правильные семантические теги), то возможно данное условие для вас уже выполнено. Но боюсь, в большинстве случаев этого недостаточно, иначе все было бы ну уж слишком просто. Перечень стандартных тегов, скорее всего, нас не удовлетворяет и мы начинаем создавать собственные компоненты: деревья, таб контейнеры, breadcrumb …, которые мы будем реализовывать на базе неинтерактивных элементов, таких как: div, span, section и т.д. Чтобы сделать неинтерактивный элемент интерактивным, нужно использовать атрибут tabindex.

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

Легко определяемое положение фокуса

Браузеры по умолчанию предоставляю стандартные стили для элементов в фокусе. Например, Chrome и Safari рисуют голубой контур вокруг сфокусированного элемента, который не так сложно заметить. В то время как Firefox и IE рисуют едва заметную пунктирную линию, которую даже зрячий человек заметит с трудом. И те, и другие стили выглядят недостаточно привлекательно и вполне могут конфликтовать с вашим дизайном, поэтому во многих приложениях разработчики просто сбрасывают их с помощью следующего CSS.

:focus {
	outline: 0;
}

В данном случае пользователь совсем теряет возможность использовать клавиатуру, поэтому ни в коем случае не делайте этого, если не собираетесь предоставить другой индикатор фокуса. Зачастую вы можете использовать для :focus те же стили, что используете для :hover.

button {
  ...;
}
button:hover,
button:focus {
  // здесь будут ваши стили для ховера и для фокуса
  ...;
}

Подробнее об этом вы можете почитать здесь.

Порядок элементов

Теперь, когда вы можете перемещаться по странице с помощью одной лишь клавиатуры, необходимо проверить порядок, в котором вы переходите по элементам - он должен быть логичным и интуитивно понятным. Обычно он просто совпадает с визуальным потоком страницы, или проще говоря соответствует тому, как вы читает - слева направо и сверху вниз (если вы поддерживаете только языки с таким направлением чтения). Именно поэтому порядок, в котором вы объявляете HTML элементы имеет значение, ведь именно в этом порядке будет происходить табуляция по элементам, а не в их визуальном порядке.

Наглядными нарушителями порядка 👮‍♂️‍ элементов, могут служить свойства order и float. Ниже вы видите три набора кнопок, в первом не используются никакие свойства для их упорядочивания, поэтому визуальный порядок совпадает с порядком табуляции, во втором и третьем же визуальный порядок отличается от порядка объявления элементов в HTML. Нажмите на кнопку “Button 1” из первой четверки и обойдите все 12 кнопок с помощью клавиши TAB, заметили разницу?

See the Pen Tab ordering by Alexander Cherkashin (@cherkalexander) on CodePen.

Помимо этого, необходимо стараться избегать употребления позитивных значений tabindex, так как такие элементы помещаются перед дефолтными интерактивными элементами (c tabindex="0"), и это означает лишь одно - разработчиками придется устанавливать (и поддерживать) значения tabindex для всех фокусируемых элементов, чтобы обеспечить верный порядок навигации. Подробнее о свойстве tabindex можно почитать на данной странице.

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

Карта навигации Яндекса
Карта навигации Яндекса

Ссылка “Перейти к содержимому”

Если вы дошли до этого пункта, то ваше приложение уже достаточно доступное и люди могут пользоваться им с клавиатуры. Однако, нет предела совершенству, верно? Дело в том, что есть одна важная особенность, которая отличает пользователей с мышью от пользователей с клавиатурой. Первые могут сразу перейти к интересующему их контенты, в то время как пользователи с клавиатурой должны десятки раз нажать TAB, чтобы пролистать вечно повторяющиеся заголовки и панели навигаций. Но вы можете упростить им жизнь, добавив ссылку “Перейти к содержимому”.

Обычно эта ссылка:

  1. самый первый элемент, получающий фокус;
  2. невидима, пока не получит фокус, именно поэтому большинство людей, которые используют только мышку, даже не догадываются о её существовании;
  3. реализуется с помощью якорей.

Нажмите чуть выше заголовка в примере, чтобы sandbox получил фокус и нажмите кнопку TAB, чтобы посмотреть реализацию.

See the Pen Skip to main content pattern by Alexander Cherkashin (@cherkalexander) on CodePen.

Удержание фокуса

Я уверен, пользователи уже очень довольны доступностью вашего приложения, однако есть ещё одна потенциальная проблема. Дело в том, что фокус не должен выходить за пределы модальных элементов (например за пределы модального окна), иначе пользователь сможет взаимодействовать с остальным содержимым на странице. В этом есть смысл, ведь, если диалог открыт, то вероятнее всего (но не 100%), пользователь должен взаимодействовать только с его содержимым и не выходить за его пределы, пока не закроет диалог. Также это может привести к ситуации, в которой пользователь не сможет увидеть какой элемент владеет фокусом. Как вы наверняка помните, у многих компонентов существует описание общепринятого взаимодействия с клавиатурой, так вот, диалог не является исключением, подробнее об этом вы можете почитать здесь, а о примерах реализации удержания фокуса здесь.

Горячие клавиши

Также не будет лишним добавить различные стандартные сочетания клавиш. Например, если вы разрабатываете графический редактор, то имеет смысл добавить такие сочетания как CTRL + Z, CTRL + Y для отмены и применения действия или же CTRL + S для сохранения внесенных изменений. Я не зря сказал “стандартные”, ведь именно эти сочетания пользователи могут машинально использовать в вашем приложении, ведь все знают (все же, правда?), что CTRL + S - это сохранить. Также можно добавлять и кастомные сочетания клавиш, но в этом случае необходимо подсказать пользователю о их существовании. Обычно разработчики добавляют небольшие подсказки, используют placeholder для текстовых полей или отображают комбинации клавиш в тултипах. Приведу несколько примеров:

  • Подсказки в тултипах в VsCode
Тултипы в VsCode
Тултипы в VsCode
  • Подсказка в правой части поля поиска на GitHub, для фокусировки необходимо нажать /
Поле поиска на github, для фокусировки необходимо нажать "/"
Поле поиска на github, для фокусировки необходимо нажать "/"
  • Подсказка в placeholder’е на сайте библиотеки Vuetify, для фокусировки необходимо нажать /
Поле поиска на vuetifyjs, для фокусировки необходимо нажать "/"
Поле поиска на vuetifyjs, для фокусировки необходимо нажать "/"

Все ли можно сделать доступным с клавиатуры?

Очень хороший вопрос, и ответ на него - НЕТ! Здесь вы можете найти исключения из правил. Как я уже говорил в начале, приложения для рисования нельзя сделать доступными, так как они очень завязаны на движения пользователя, так же как и различные обучающие симуляторы (например, симулятор полетов на вертолете). Однако, это относится не ко всем графическим редакторам, а только к тем, где пользователь рисует с помощью “карандаша” или “кисти”. Вполне возможно сделать доступным редактор для рисования геометрических фигур, например, чтобы добавить прямоугольник, нам всего то нужно указать его координаты и размер.

А теперь ответьте мне на вопрос, можем ли мы сделать доступной функцию Drag and Drop? В том виде, в котором она существует - явно нет, ведь эта функция также как и в случае с рисованием очень зависит от движений пользователя, однако мы можем реализовать перемещение объектов с помощью стрелок. Очень распространенным применением Drap and Drop является палитра компонентов, в которой вы выбираете объект и бросаете его, например, на холст, в этом случае мы можем сделать функцию доступной следующим образом:

  • Переходим в палитру компонентов;
  • Перемещаемся на нужный элемент;
  • Нажимаем CTRL + C;
  • Перемещаемся на холст;
  • Нажимаем CTRL + V;

Тестирование вместо заключения

Теперь, когда всё готово, откройте ещё раз свое приложение и ответьте на следующие вопросы:

  • Можете ли вы попасть на каждый элемент?
  • Можете ли вы взаимодействовать с каждыми элементом?
  • Доступны ли вам все возможности приложения без использования мышки (не считая ранее оговоренных исключений)?
  • Легко ли определить текущее положение фокуса на странице?
  • Заперт ли фокус внутри модальных элементов?

Если вы можете ответить на все эти вопросы положительно - поздравляю.

Как видите, для тестирования вам не нужен какой либо специальный софт. Однако, пока я писал данную статью, я нашел пару расширений для хрома, которые вы возможно сочтёте полезными: Accessibility Insights и deque.

Полезные ссылки