Создайте пользовательский список, совместимый с WCAG, в React

Мне не нужен фон, просто перейдите прямо к коду

Кто должен это прочитать?

Для целей данной статьи предположим следующее:

  • У вас есть общие знания о веб-разработке переднего плана или специальные знания о среде React.
  • Вы хотите узнать больше о кодировании для специальных возможностей, независимо от ваших текущих знаний. Может быть, это стало важным для вашей работы или это просто область, в которой вы хотели бы получить больше опыта.

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

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

  1. Всегда используйте правильные и точные семантические элементы HTML5. Если вам нужна кнопка на вашем сайте, используйте тег <button>. Да, можно оформить тег <div> так, чтобы он выглядел как кнопка, и добавить к нему прослушиватель событий, чтобы он вел себя точно так же, как кнопка. Зрячий пользователь не заметит разницы. Однако тогда вам также нужно будет добавить теги ARIA, чтобы сообщить программе чтения с экрана, что это должна быть кнопка. Вам нужно добавить свойство tabindex, чтобы <div> вел себя как интерактивный элемент, который будет получать фокус, если пользователь перемещается по странице с помощью клавиатуры. Вам придется приложить немало усилий, чтобы сделать ваш пользовательский элемент доступным. Когда вы используете точные семантические элементы, вы получаете доступ бесплатно.
  2. Используйте точные семантические элементы HTML5 для создания логической и иерархической структуры страницы. Имейте один тег <h1> на каждой странице и следите за тем, чтобы меньшие заголовки шли только после него. Оберните основной контент тегом ‹main› и заполните его тегами <section>. Тег <h1> идет с <main>. CSS может изменить внешний вид почти всего на вашем сайте, но доступность зависит только от простой структуры страницы, чтобы интерпретировать тип информации, которая должна быть передана пользователю, и упростить навигацию по странице для людей с ограниченными возможностями.

Эти 2 правила (которые на самом деле являются одним расширенным правилом), вероятно, позаботятся о 80% проблем доступности при кодировании.

Причины для создания собственного доступного компонента

Что происходит, когда вы не можете легко применить вышеуказанные простые правила? Мое требование состояло в том, чтобы разработать функцию, в которой пользователь взаимодействовал бы с раскрывающимся списком. Были включены некоторые макеты из UX, которые представляли раскрывающийся список с явным стилем. Также было требование использовать собственный тег <select> в мобильном Интернете для этой функции, чтобы воспользоваться преимуществами стиля, специфичного для браузера, на маленьких экранах. Только десктопные браузеры будут использовать раскрывающийся список с явным стилем, высмеиваемый UX.

Здесь есть пара вещей, на которые следует обратить внимание…

  1. В моих билетах указано, что собственный тег <select> будет иметь разный стиль в каждом браузере, особенно в мобильном. У разработчиков есть несколько вариантов прямого управления этим стилем. Я потратил немного времени, чтобы изучить это, но не нашел ничего нового, но я не смог найти способ заставить собственный тег <select> соответствовать моим макетам UX.
  2. Если НЕ использовать нативный тег <select>, это автоматически ставит под угрозу доступность этой функции, потому что мне придется нарушить первое правило доступного кодирования — использовать семантические элементы. Для этого варианта использования просто не было доступного готового HTML5-элемента. Мне нужно было построить свой собственный.

Доступные авторские рекомендации

Цели

При создании доступных веб-приложений необходимо учитывать две основные области:

  1. Навигация с помощью клавиатуры — может ли пользователь перемещаться по веб-странице только с помощью клавиатуры? Неожиданно теряется фокус при переходе между элементами? Соответствует ли поведение навигации с помощью клавиатуры ожидаемым шаблонам?
  2. Объявления программы чтения с экрана — имеет ли пользователь вспомогательной технологии (AT) доступ к той же информации, которая доступна визуально? Уведомляются ли пользователи AT об ошибках или других существенных изменениях на странице?

Консорциум World Wide Web (W3C) предоставляет исчерпывающие ресурсы по доступности в Интернете. В их иерархию ссылок включены руководства для авторов, которые точно описывают ожидаемое поведение каждого компонента. (Примечание: сайт W3C будет ссылаться на виджеты, в то время как в React общепринятым термином является компонент. Я считаю, что эти термины можно использовать взаимозаменяемо. Суть в том, что мы' воссоздание интерактивной части веб-страницы с определенной функцией.)

После просмотра вариантов нет ссылки на тег <select>, но это потому, что этот сайт не охватывает поведение нативных элементов HTML. Он охватывает то, что нам нужно включить в создание наших собственных доступных элементов.

Если мы изучим эти шаблоны дизайна немного дальше, мы обнаружим, что Listbox описывает функциональность нативного элемента <select>, так что это то, что мы будем кодировать. На самом деле существуют различные функциональные возможности, которые могут быть достигнуты в зависимости от того, как этот компонент создан. Например, мы можем захотеть, чтобы пользователь мог делать множественный выбор или иметь возможность прокручивать выбранные элементы. Сайт содержит подробные объяснения того, как доступно кодировать каждый из этих вариантов. Для этого примера мы специально напишем Сворачиваемый список.

Сайт предоставляет нам 2 диаграммы, по одной для каждой основной области, которые точно показывают, что нам нужно закодировать, чтобы список работал так, как должен.

Итак, это завершает всю предысторию доступного списка. К коду!

Кодирование доступного списка

Подводя итог, прежде чем углубляться, задача состоит в том, чтобы спроектировать и создать доступный компонент, который будет вести себя точно так же, как собственный тег <select>.

Мы собираемся создать наш доступный настраиваемый список в React, но цели, которые мы хотим достичь в отношении доступности, будут одинаковыми, независимо от используемой конкретной платформы или библиотеки.

Клавиатурная навигация

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

Итак, что мы собираемся построить:

  • Компонент с кнопкой, которая будет переключаться между отображением и скрытием раскрывающегося списка.
  • В раскрывающемся списке будут представлены параметры из массива строк, переданных в качестве реквизита, и он будет состоять из тега <ul>, который будет отображаться через элемент списка для каждого параметра.
  • Нам понадобится несколько вспомогательных функций и состояние для обработки переключателя.

Фокусировка в React

Поскольку это проект React, мы собираемся установить Refs для наших основных элементов DOM, чтобы управлять фокусом. Это считается одним из основных приемлемых вариантов использования Refs в React. Нам понадобится прямой доступ DOM к каждому элементу — внешнему списку <div>, кнопке и элементам списка — чтобы иметь возможность вызывать для них метод focus.

Исходный код

Приведенный ниже код является начальной структурой для компонента Listbox. Refs установлены для всех необходимых элементов, но мы управляем фокусом только на кнопке, когда она нажата в функции toggleDropdown. По правде говоря, браузер действительно позаботится об этом за нас, так как мы используем собственный HTML <button>. Код по-прежнему будет работать без явной установки фокуса на строку 43. Однако остальным элементам потребуется явно управлять фокусом, чтобы они вели себя как нативные <select>.

Управление фокусом

В Listbox есть метод onSelected, который ожидает либо MouseEvent, либо KeyboardEvent, когда пользователь взаимодействует с кнопкой. Независимо от типа события выполняются методы onChange и toggleDropdown.

Нам нужны некоторые методы обработки фокуса, которые будут реагировать только на события клавиатуры. Приведенные ниже функции будут реализовывать поведение клавиатуры, определенное W3C для сворачиваемого списка, как указано в таблице поддержки клавиатуры.

Важные соображения

  • Использование event.preventDefault() здесь особенно важно, чтобы реализовывалось только заданное поведение клавиатуры, хотя не все клавиши этого требуют.
  • Мы также должны учитывать, что элементы списка не изначально являются интерактивными элементами, поэтому обычно они не могут получать фокус браузера. Это означает, что нам нужно самим контролировать их способность концентрироваться. Мы можем сделать это, условно установив для атрибута tabindex каждого элемента списка значение -1, когда ему нужен фокус.

handleButtonPress срабатывает на кнопке для переключения раскрывающегося списка при нажатии кнопки и возвращает фокус на кнопку

handleKeyPress запускается для отдельных элементов списка с определенным поведением для определенных ключей.

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

В этом примере focusedValue будет отслеживаться с помощью Ref, чтобы мы могли сохранять значение между рендерингами. Мы не можем использовать здесь state, иначе значение будет сбрасываться каждый раз, когда выпадающее меню сворачивается и снова раскрывается. Мы хотим, чтобы фокус в списке всегда отражал выбранную опцию, если пользователь открывает и закрывает раскрывающийся список несколько раз. Если бы это было приложение, извлекающее и изменяющее данные из серверной части через этот список, мы могли бы положиться на state для этого.

Навигация с клавиатуры теперь работает! Используя только клавиатуру, пользователь может выполнять все следующие действия, используя те же клавиши, что и для собственного тега <select>:

  • Открыть и закрыть раскрывающийся список
  • Просмотрите раскрывающиеся варианты
  • Выйти из списка

Управление АРИИ

Однако, что касается браузера, этот компонент по-прежнему не является списком только потому, что навигация с помощью клавиатуры наводит на мысль о нем. Для браузера это по-прежнему просто набор общих элементов. ARIA предоставляет способ сообщить браузеру, что он отображает Listbox. Как определено в Веб-документах MDN:

Доступные многофункциональные интернет-приложения (ARIA) – это набор атрибутов, определяющих способы сделать веб-контент и веб-приложения (особенно разработанные с помощью JavaScript) более доступными для людей с ограниченными возможностями.

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

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

Столбец Использование также дает некоторое представление о назначении каждого атрибута.

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

После добавления этих атрибутов ARIA программа чтения с экрана объявит назначение и состояние кнопки.

Давайте продолжим с элементами <ul> и <li>. Наряду с настройкой различных атрибутов ARIA нам необходимо назначить роли listbox для <ul> и option для каждого <li>, чтобы обозначить их назначение для браузера как части этого виджета.

Доступный список в действии

Подведение итогов

Список теперь доступен! В нем по-прежнему отсутствуют многие функции, которые мы, вероятно, хотели бы иметь для зрячих пользователей (может быть, реквизиты для передачи классов для пользовательских стилей?), но он работает для тех, кто использует программу чтения с экрана или навигацию с помощью клавиатуры.

Вот краткое изложение того, что было рассмотрено:

  • Всегда используйте семантический элемент HTML5, когда это возможно.
  • Если нет возможности использовать семантический элемент:
  1. Найдите Руководство по созданию WAI-ARIA для того, что вы хотите создать.
  2. В React управляйте фокусом, устанавливая Refs для элементов. Это также может быть обработано с помощью ванильного JavaScript.
  3. Установите для атрибута tabindex значение -1, чтобы разрешить неинтерактивному элементу программно получать фокус.
  4. При кодировании функций для определенных клавиш обратите внимание на поведение по умолчанию для этой клавиши и preventDefault, если это необходимо.
  5. Установите уникальные идентификаторы элементов для ссылки на атрибуты ARIA (и убедитесь, что они уникальны для веб-страницы).
  6. Установите атрибуты и роли ARIA для элементов.

Пожалуйста, посетите эту песочницу кода, чтобы просмотреть весь проект, и спасибо за чтение!