Доступность интерфейсов. Лекция Яндекса
Меня зовут Дима, я работаю в офисе Яндекса в Санкт-Петербурге и занимаюсь внутренними сервисами в команде разработки интерфейсов Толоки. В этом году я подготовил лекцию для Школы разработки интерфейсов. Ниже — её расшифровка.
Что такое доступность интерфейсов? Для кого она важна и зачем к ней нужно стремиться? Каковы основные приемы, которые делают интерфейс доступным? Кроме этих вопросов, лекция проясняет принципы, которые лежат в основе ассистивных технологий. Я постарался разобрать теорию и большое количество практических примеров, а также показать процесс работы скринридера.
— Что скрывается под модным нынче термином accessibility? Какие у вас есть варианты? Для слепых, чтение с экрана, с ограниченными возможностями, координация движений… Все верно. Доступность — возможность использования интерфейса всеми, независимо от физических или технических ограничений.
Доступный интерфейс — это интерфейс, который максимально удобен в использовании для широкого круга пользователей. Возможно, у кого-то возник вопрос, зачем делать интерфейсы доступными? Какие у этого есть мотивации? Первая и, по мне, самая важная мотивации — моральная. Люди с ограниченными возможностями здоровья ничем не хуже остальных, они тоже хотят и имеют возможность пользоваться полным функционалом ресурсов, которые вы разрабатываете.
Финансовый аспект. Люди с ограниченными возможностями — это клиенты, которые покупают ваши товары, пользуются вашими услугами, поэтому обеспечение доступности также способствует расширению вашего текущего рынка и выходу на новые рынки.
Юридический аспект. Неотъемлемое право доступа к информации заложено в законодательство многих стран. Например, в США и ЕС все веб-интерфейсы должны быть доступными для людей с ограниченными возможностями. У нас это касается в основном государственных сайтов, ко всем остальным это применяется лишь в качестве рекомендации.
Кто эти люди, кому важна доступность? В первую очередь — пользователи с нарушением зрения, полностью незрячие люди, слабовидящие люди, люди с нарушением цветовосприятия. Этим пользователям сложно видеть ваш сайт. Возможно, они вообще не видят ваш сайт. Вторая категория — пользователи с нарушением опорно-двигательного аппарата. Таким людям сложно пользоваться устройствами ввода, у них проблема с моторикой, самопроизвольное сокращение мышц.
Хотел бы обратить особое внимание, что любой из нас в какой-то момент может столкнуться с временными ограничениями, и тогда пользоваться интерфейсом в привычном виде он не сможет. Например, правша, сломавший правую руку, начинает испытывать проблемы с моторикой. И интерфейс, разработанный без учета этих особенностей, может стать для него просто недоступным.
Сейчас в России примерно 10% населения — люди с той или иной инвалидностью. И далеко не последнее место тут занимают люди с проблемами со зрением или опорно-двигательным аппаратом. И все эти люди — потенциальные посетители вашего сайта. Им особенно важна доступность.
Технологии, которые помогают людям пользоваться интерфейсами, можно разделить на два типа: аппаратные и программные. Людям с ограниченными возможностями помогают в использовании интерфейсов специальные клавиатуры, мышки, джойстики и прочие устройства ввода.
В качестве примера хочу привести эту штуку. Этот тип девайсов называется Switch. Доступность — это не только про компьютеры и ноутбуки, это еще и про мобильные устройства. Человек с ограниченными возможностями может зайти на ваш сайт с телефона или планшета. Это устройство подключается к iOS- или Android-девайсу, и на его кнопки можно назначить какие-то пользовательские действия. Например, этот Switch — с двумя большими кнопками, удобно назначить на него управление фокусом на странице.
Для пользователей, которые не видят ваш интерфейс, синтез речи бывает удобен не всегда. Поэтому существует брайлевский дисплей. Это специальное устройство вывода, которое отображает информацию в виде осязаемых точек из азбуки Брайля. Классная штука, но у нее есть две проблемы. Она очень дорогая, далеко не каждый нуждающийся человек может ее себе позволить.
Кроме того, люди, потерявшие зрение в более зрелом возрасте, часто не знают азбуку Брайля и не хотят ею овладевать.
Программных технологий тоже достаточно. Это инструменты для увеличения изображения на странице, например, экранные лупы, которые встроены в Windows, macOS, это программные модификаторы цветовой гаммы и прочее. Например, есть софт, который позволяет управлять интерфейсом с помощью движения глаз и головы. В macOS такая программа встроена, она называется Dwell Control. Но среди программных технологий особое место занимают скринридеры. Это специальное приложение, которое зачитывает пользователю содержимое сайта и ОС, предоставляет ему удобный функционал по навигации.
Скринридеры — это самый доступный и распространенный способ воспринимать информацию для людей с нарушением зрения. Более подробно мы о них поговорим в ходе лекции.
Пару слов про рекомендации в области доступности интерфейсов. У нас это ГОСТ, а на Западе — WCAG и Section 508. Способы, описанные в данных рекомендациях, не влияют на внешний вид сайта, но предоставляют пользователям с ограниченными возможностями какими-то дополнительные штуки для навигации и пользования.
Практики, которые помогают сделать ваш интерфейс доступным уже сегодня. Альтернативное описание должно передавать краткую информацию к тексту, дополнять или обозначать его.
То же самое касается графики и иконок на странице. Помните: они также нуждаются в дополнительном сопровождении. Если какой-то дополнительный элемент на вашей странице представлен в виде изображения, то его необходимо скрыть от скринридера с помощью атрибута aria-hidden=«true».
Про атрибуты с приставкой aria поговорим дальше.
Необходимо описывать поля ввода с помощью тега label, у которого мы указываем атрибут for со значением id поля ввода. Это не только позволит быстро перейти к редактированию контрола по нажатию на лейбл, но также семантически свяжет контрол с его описанием, то есть скринридер зачитает нам описание этого элемента. Указывает типы данных у полей ввода.
Помимо валидационных правил, которые автоматически добавятся к этим инпутам, во многих случаях будет представлен удобный кроссбраузерный способ ввода информации. Например, как здесь, календарь в случае с типом Date или Сolor, если это цвет. Если браузер не поддерживает какой-то из типов, то будет подставлен input с типом text, ничего не сломается.
Помечайте обязательные поля с помощью атрибутов required или aria-required=«true». Группируйте связанные поля с помощью тега fieldset, с помощью элемента legend указывается заголовок группы, где можно прописать не только назначение группы, но и общие характеристики полей (например, что все поля в этой группе обязательные).
Три радиокнопки оборачиваем в тег fieldset, задаем заголовок в теге legend. Теперь вне зависимости от того, какая из кнопок выбрана, скринридер прочитает содержимое тега legend. И человек, который посещает сайт, поймет, что происходит в этой форме.
Выводите сообщения об ошибках и успехе.
Про title и так понятно, текст в этом теге должен описывать назначение или название страницы. Если у вас single page application, то не забывайте менять содержимое тега title при навигации по странице.
Указывайте язык страницы. Для этого надо установить атрибут lang в теге HTML. Это хорошо для SEO, это помогает плагинам-переводчиком, а также скринридеры точно определяют язык такой страницы. Если у вас в документе встречается несколько языков, то вы можете указать атрибут lang для отдельных тегов. Соблюдайте валидность верстки. Это важно, потому что скринридеры могут неправильно воспринимать невалидные страницы. То же самое касается семантики. Все элементы на странице должны быть семантически верные, вы должны использовать теги HTML по их смысловому назначению. А когда вы создаете свои кастомные элементы, то позаботьтесь о правильной семантике.
Поговорим о примерах в области семантики. Сначала про заголовки. Они одни из самых важных с точки зрения семантики элементы на странице. Когда человек заходит на сайт с помощью скринридера, попадает на неизвестную страницу, первое, что он делает на этой странице, это ходит по заголовкам. И в скринридерах даже есть специальный режим, который позволяет удобно это сделать. Если на вашей странице присутствуют заголовки и отображают четкую иерархию, то человеку будет удобно навигироваться по сайту, он сможет быстро перейти к нужному разделу. Это очень удобно, используйте это.
Еще один пример важности семантики — верстка меню. Если сделаете меню как слева, с точки зрения семантики это будет просто группа. div — это группирующий тег, и внутри него будет какое-то количество несвязанных элементов, в этом случае просто три ссылки.
Если сделать навигацию, обернув в специальный семантический тег, внутри которого будет список из элементов, каждый из которых является ссылкой, то с точки зрения семантики, скринридера и браузера это будет честная навигация, внутри которой располагается список из трех элементов, по которым можно будет удобно перемещаться.
Следующий совет, надеюсь, для вас очевиден: правильное использование семантических тегов из пятого стандарта HTML гарантируют то, что логически ваша верстка будет правильно интерпретирована браузерами и ассистивными технологиями.
Про адаптивную верстку и мобильную версию. У вас уже была лекция по адаптивной верстке, надеюсь, вы понимаете важность этого.
Всегда следует отдавать предпочтение встроенным HTML-элементам, и только в случае необходимости создавать свои собственные. Причина этому простая: встроенные элементы уже из коробки имеют правильную семантику и ряд функциональных особенностей. Когда говорят об этом, часто в пример приводят кнопки. Поэтому и у меня будет две кнопки.
Они отличаются только надписью, но в действительности только левая кнопка является кнопкой. В основе ее лежит тег button. Правая — просто стилизоварованный div. Поэтому в левую кнопку мы можем сфокусироваться — button по умолчанию фокусируемый элемент. У кнопки есть состояние disabled, в котором она не может быть нажата. И клик по кнопке происходит по нажатию Enter и Space, если мы в фокусе кнопки. И только у этой кнопки правильная семантическая роль, с точки зрения браузера, скринридера это кнопка, и когда туда попадет скринридер, он так и скажет, что это кнопка. Всего этого нет у кнопки на основе div. Следующие пункты вам придется реализовывать самостоятельно. Но зачем, если есть встроенное хорошее браузерное API, которое можно использовать?
О доступности важно задумываться до этапа разработки и верстки. Кто когда-либо на работе или своих сайд-проектах занимался проектированием или отрисовкой дизайна? Нас точно больше половины зала. Пару советов в этом направлении. Не допускайте в появлении своих интерфейсов слишком мелких или сливающихся с фоном элементов.
Также не стоит использовать декоративные или каллиграфические шрифты для основного текста. Помните о пользователях со слабым зрением, с нарушением цветовосприятия. О людях, которые пользуются мониторами с плохой цветопередачей. Все они могут просто не увидеть ваши элементы интерфейсов. Особенно это критично для управляющих элементов, для контролов.
О формах мы говорили, но больше с точки зрения семантики. Здесь же обратите внимание на грамотное проектирование пользовательского взаимодействия с формами, причем с учетом особенностей некоторых пользователей. Например, есть люди с проблемами с моторикой, им бывает трудно попасть по маленьким элементам. Поэтому сделайте у таких элементов область кликов больше, чем контрол или кнопка. То же самое касается элементов, расположенных близко друг к другу. Человек, у которого проблема с моторикой, может промахнуться и нажать не на то. Разносите важные элементы дальше друг от друга, и обязательно запрашивайте подтверждение каких-то необратимых операций, например, удаления данных.
Общий пункт про единообразие дизайна, логичность, предсказуемость и консистентность. Весь ваш дизайн должен быть выдержан в каком-то одном стиле. Все блоки на вашем сайте должны располагаться в одних местах. Не должно быть такого, что блок прыгает с места на место при переходе по страницам. Особенно это критично для навигации. Человек, который не видит ваш сайт, строит в голове его изображение, представляет, как он выглядит. Если от страницы к странице ваша навигация будет прыгать с места на место, то он просто запутается в этом, пользоваться сайтом будет невозможно.
Помимо всех технологий, о которых мы узнали, есть стандарт, разработанный для того, чтобы предоставить пользователям с ограниченными возможностями удобный способ взаимодействовать с сайтами в интернете.
Прежде чем разбираться в тонкостях, давайте посмотрим, как это работает под капотом с точки зрения браузера. После загрузки вашей страницы, браузер начинает парсить HTML-разметку, и на основе нее строит DOM-дерево. Надеюсь, вам эта структура известна, на ней основывается отображение данных в браузере, ее можно менять с помощью JS и т. д. Когда DOM-дерево построено, браузер строит на основе него другую структуру данных — accessibility tree. Это дерево содержит в себе информацию, полезную с точки зрения доступности. Эту информацию берут себе ассистивные технологии, например, скринридеры. И предоставляют пользователю какой-то удобный способ взаимодействия с сайтом. В процессе этого взаимодействия DOM может меняться, поэтому браузер следит за изменениями в DOM и актуалиазирует accessibility tree по необходимости. Ассистивные технологии забирают эти изменения и как-то модифицируют функционал, который они предоставляют своему пользователю.
Это изолированная структура данных. К ней нельзя получить доступ из DOM, ее нельзя просмотреть или отредактировать с помощью JS. Реализацией и управлением данной структуры полностью занимаются браузеры. Хранится в этом дереве информация о семантике элементов, с помощью которой ассистивные технологии понимают, как интерпретировать элементы со страницы.
Доступ к этой информации сейчас можно получить лишь с помощью специальных инструментов. Один из таких элементов — DevTools Accessibility Inspector. Сейчас инспектор доступности Chrome находится в разделе экспериментальных технологий, включить которые можно, зайдя на страницу Chrome Flags. Вы включаете эти технологии, потом идете в DevTools и включаете саму панель accessibility. Так она выглядит в деле.
Здесь показана различная полезная для доступности информация. И сразу возникает вопрос, откуда эта информация берется и как ее можно изменить?
Небольшие примеры.
Просто нативный чекбокс, обернутый в label. Так он будет представлен в accessibility tree. Мы видим, что это объект, у него есть поля, тип checkbox, имя, забранное из label, и состояние, которое может быть checked и unchecked.
Теперь делаем кастомный чекбокс с помощью div, его состояние обозначаем стилем. Но с точки зрения браузера, ассистивных технологий, это будет просто текст с каким-то значением. Эти два примера наглядно подтверждают то, что я сказал ранее: по возможности всегда следует использовать нативные элементы. Но часто это невозможно. Тогда на помощь приходит стандарт ARIA.
Спецификация ARIA добавляет особые атрибуты, которые определяют то, как именно будет элемент представлен в accessibility tree, какими свойствами он будет обладать. Основное, что дает нам эта спецификация, — роли для описания типа элементов, свойства для описания его состояния.
Вкратце посмотрим, какими бывают роли и свойства, потом это закрепим на примерах. Начнем с понятия роли. Роль позволяет нам классифицировать элементы на странице. Устанавливается она добавляем к HTML элементу атрибута role c нужным значением.
Различных ролей в стандарте много, и они делятся на разные группы. Например, роли виджетов. Они назначаются элементам, которые являются независимыми частями пользовательского интерфейса. Это привычные нам кнопки, радиокнопки, табы, тултипы. Дальше идут составные роли, которые агрегируют элементы других ролей. Очевидно, radiogroup состоит из элементов radio или tablist состоит из tab. Далее следуют структурные роли и landmarks, ориентиры. Эти роли даются крупным семантическим блокам на странице.
Помимо ролей, в стандарте ARIA также предусмотрены состояния. Они могут описывать, какой статус сейчас имеет элемент. Например, состояние для виджетов, они обозначают, что элемент скрыт, отмечен, заблокирован и т. д.
Также есть состояния, которые обозначают связь элементов. Например, элемент описывает другой элемент, управляет другим элементом. Также состояния могут декларировать особые области, в которых ожидаемо произойдет изменение контента. Эти области называются live regions.
Ролей и состояний много. Все их можно почитать на сайте спецификации.
Посмотрим на примеры наиболее часто используемых ролей.
Доделаем наш кастомный чекбокс. Берем тот же div, но теперь добавляем для него роль checkbox, а также атрибут, который показывает состояние, aria-checked, true или false. Теперь надо добавить соответствующий JS, и с точки зрения браузера, семантики это будет честный чекбокс. Таким же образом можно сделать другие элементы. Например, кнопку, которая будет выполнять роль свича, переключаться между состояниями. У нее есть своя роль switch и атрибут, обозначающий ее текущее состояние.
Пример составной роли. Есть список с элементом tablist, внутри него элементы с ролями tab. Таким образом мы говорим браузеру, подразумевая, что эти элементы являются tab.
Более сложный пример — многоуровневое меню. Помимо различных ролей, среди которых есть структурная роль — navigation, — здесь есть составные роли, вложенные друг в друга. Есть пара интересных состояний, например, aria-haspopup со значением true говорит о том, что этот элемент раскрывающийся. Или, например, aria-hidden со значением true, говорящая ассистивным технологиям, браузеру, что этот элемент в данный момент скрыт.
На этом слайде я хотел обратить внимание, что само добавление состояния, ролей никак не изменяет поведение элемента, его функционал. Есть элемент, вы сверстали его, написали JS, а потом с помощью этих атрибутов описываете его поведение, которое будет интерпретировано ассистивными технологиями, которые предоставят пользователю удобный способ взаимодействия. Добавление aria-haspopup не делает элемент раскрывающимся сам по себе. Или добавление aria-hidden не скрывает элемент со страницы, помните об этом.
Про атрибуты, устанавливающие связь между элементами. Этот атрибут позволяет указать, что один элемент управляет другим элементом. В данном случае это кнопка «показать настройки», которая скрывает или показывает блок под ними. Мы используем атрибут aria-controls со значением id блока, которым мы управляем. Похожий пример, но здесь мы ссылаемся на блок, который описывает наш текущий блок, используем атрибут aria-describedby. В этом случае скринридеры прочтут нам содержимое и предоставят пользователю больше информации о том, что произойдет по нажатию на эту кнопку.
Ни для кого не секрет, что сейчас почти все сайты в интернете динамические, то есть содержимое на них меняется без перезагрузки страницы. И тут с точки зрения доступности появляются проблемы. Как оповестить ассистивные технологии, те же самые скринридеры, что на странице произошло изменение? Для этого в aria ввели концепцию live регионов. Их можно создать либо с помощью роли alert, либо с помощью атрибута aria-live. Теперь скринридер будет отслеживать изменения в этих блоках, и, если изменение произошло, он будет зачитывать пользователю новое содержимое. Причем если вы укажете role=«alert» или aria-live со значением assertive, то произношение нового контента произойдет немедленно. Если сейчас скринридер зачитывал содержимое на странице, зачитывание прервется, и начнется зачитывание содержимого в live-region.
Если вы хотите, чтобы оповещение о новом контенте было более вежливым, то необходимо создавать live region с помощью атрибута aria-live со значением polite. Тогда он дождется, пока текущий контент будет произнесен, и произнесет новый контент после.
Важно помнить интересную особенность. Чтобы live регионы работали в разных браузерах на разных ОС, лучше создавать их статическими. Вы создаете элемент, где будет изменяться контент, делаете его пустым, прячете, например, визуально, а потом, в случае необходимости, просто добавляете новое содержимое туда и показываете. Тогда можно быть уверенным на 100%, что все ассистивные технологии, все браузеры на всех ОС точно и корректно его отобразят и зачитают.
Подробнее поговорим про управление с клавиатуры и о фокусе. Для людей, которые при просмотре сайтов пользуются преимущественно или только клавиатурой, существует два основных сочетания клавиш: Shift + Tab и Tab. Переход на следующий и предыдущий фокусируемый элемент. Когда мы говорим о фокусе и его переходе по странице, важно обеспечить правильную последовательность перехода фокуса.
Фокус идет по порядку HTML-разметки. Как элементы расположены в разметке, так фокус и будет идти. Если вы изменяете положение элементов визуально с помощью каких-то CSS-свойств, например, используя абсолютное позиционирование, float или свойство order у флексбоксов и гридов, то вы меняете положение элемента только визуально, в разметке он остается на прежнем месте.
Небольшой пример. Правая колонка находится справа, мы читаем слева направо, нам кажется, что она идет после. И ожидаемо должны попасть в нее после основного содержимого. Но в разметке правая колонка идет перед основным содержимым. И если человек зайдет на сайт с помощью скринридера или просто начнет навигироваться по нему с помощью клавиши tab, то сначала попадет в правую колонку, а потом в основную. Человек, который ваш интерфейс как-нибудь разберется, а незрячий человек может сильно запутаться.
Продолжая о фокусе, хочется остановиться на focus ring, это обводка вокруг фокусированного элемента. Мы все ее знаем, и наверное, все не любим, потому что она портит наш дизайн. Поэтому мы ее отключаем на всем сайте.
Этим мы запрещаем людям, которые видят наш сайт, но пользуются им с клавиатуры, увидеть текущее местоположение фокуса. Непонятно, где мы сейчас находимся. Именно поэтому отключать ее полностью нельзя. Если вам не нравится внешний вид, то переопределите ее, причем необязательно использовать свойство outline, вы можете использовать box-shadow, border, что угодно, лишь бы она четко была визуально заметна на сайте.
Еще одна интересная особенность. Возможно, вы обращали внимание, что разные фокусируемые элементы в одинаковых условиях могут вести себя по-разному. Например, есть инпут и кнопка. Когда мы навигируемся по ним с помощью таба, оба оказываются в фокусе. Но если нажмем мышкой по инпуту, фокус вокруг него появится. Если нажмем на кнопку, фокус не появляется.
Почему так работает, какая здесь разница? У браузеров есть свои внутренние эвристики, которые определяют, должен отобразиться focus ring вокруг элемента или нет. Доступ к этим эвристикам можно получить через такой интересный псевдоселектор.
Он применяется к элементам, для которых в текущий момент браузер считает нужным отобразить focus ring. Если мы хотим сделать наоборот, отображать обводку вокруг сфокусированного элемента только если пользователь навигируется по сайту с помощью клавиатуры, то мы пишем такой не совсем тривиальный селектор, который дословно можно прочитать так: если элемент фокусируемый, но в текущий момент браузер считает, что вокруг него фокус показывать не нужно, тогда мы обводку убираем. Эту технологию можно использовать, когда создаешь свои кастомные компоненты. Но, как это обычно бывает во фронтенде, все крутые штуки сейчас не работают. Именно поэтому эта технология экспериментальная, сейчас в браузерах недоступна, ее можно использовать только с помощью полифилла, ссылку на него я оставлю в конце презентации.
Интерактивные элементы, кнопки, ссылки, поля ввода по умолчанию попадают в порядок перехода фокуса по табу. Но что делать, если мы хотим добавить в этот порядок собственные элементы или элементы, которые по умолчанию являются не фокусируемыми? Тут на помощь приходит атрибут tabindex. Если установить его в значение 0, элемент станет фокусируемым. Плюс он добавится в поток фокуса. Поток, в который мы попадаем по нажатию tab.
У нас есть фокусируемый и нефокусируемый элементы и div между ними. Если мы будем навигироваться по сайту с помощью tab, мы попадаем в первую кнопку и вторую кнопку. Если добавим для второго блока tabindex=0, то он станет фокусируемым и попадет в порядок фокуса, станет вторым.
Если элементу задать tabindex=-1, он станет фокусируемым, но в порядок фокуса не попадет. Однако, ему можно фокус поставить с помощью JS.
tabindex > 0 задает фактически порядок фокуса. Имея три нефокусируемых элемента, мы можем сделать их фокусируемыми, причем порядок фокуса будет указан явно этими индексами.
tabindex > 0 — это мощный инструмент, но пользоваться им нежелательно. Любое изменение в верстке, в дизайне может сломать эту последовательность, и тогда ваш интерфейс станет непредсказуемым. Так делать нельзя. Грамотное управление фокусом в особых ситуациях. Приведу пример двух компонентов, где используется особое управление фокусом. Этот попап, по-моему, из четвертого Bootstrap.
Любой хороший попап, помимо очевидных вещей — таких как закрытие по нажатию на ESC или закрытие по клику на оверлей, — должен возвращать фокус в тот элемент, откуда изначально он пришел. Важно, что у хорошего модального окна фокус не выходит за его пределы. И неважно, цикличен ли он или упирается в первый или последний элемент. Важно, что мы ни в какой момент не сможем провалиться за страницу за модальным окном.
Другой пример, material design, есть основное содержимое и меню слева. Здесь похожая ситуация, пока меню скрыто, мы не должны попасть в него фокусом. Мы ходим табом по странице, никуда не проваливаемся. Как только меню открывается, мы фокус ставим в первый пункт, и потом можем ходить фокусом по остальным. Всё, меню закрылось, оно инертно для фокуса, мы в него попасть никак не можем. Чтобы не получилось так, что пользователь после нескольких нажатий на tab проваливается куда-то и не видит текущего положения фокуса.
Эти примеры схожи в том, что в какой-то момент нужно делать элементы на странице. В случае модального окна это вся страница сзади. В случае выезжающего меню это скрытое меню страницы. Мы должны делать эти элементы инертными для фокуса, чтобы фокус в них не попадал. Для решения этой проблемы существует очень простой способ — атрибут inert.
Он задается просто элементу, и работает очевидно. Мы ставим inert для элемента, он и все его дочерние элементы становятся инертными для фокуса. Эта штука крутая, и, как и любая крутая штука, сейчас не работает. Эта технология экспериментальная, для ее работы нужен полифилл.
Другие решения, которые можно использовать с помощью JS. Существует большое количество библиотек для решения этой проблемы. Одна из них — Focus Manager. Она реализует два метода. Метод сapture захватывают фокус на каком-то элементе. Функция release принимает аргумент, куда вернется фокус после того, как мы его отпустим. Опять вспоминаем модальное окно, по закрытию модального окна возвращается фокус в тот элемент, кнопку или что угодно, откуда он пришел. Это круто и удобно, это надо делать.
А другое решение для реакта — просто обертка, которая не дает фокусу выйти за его пределы. Импортируешь и используешь — даже думать не надо.
Многие библиотеки модальных окон для реакта (например, React Modal) уже из коробки имеют такую логику управления фокусом.
Скринридеры являются самой удобной и доступной ассистивной технологией. Какие скринридеры бывают?
Первый на очереди VoiceOver, который идет с операционными системами Apple, это не только macOS, но и iOS, WatchOS, и он предоставляет нативный функционал по взаимодействию с интерфейсом для людей с ограниченными возможностями. Это классный инструмент, простой в использовании, функциональный, там большое количество голосов, настроек, можно очень удобно под себя настроить. Но проблема очевидна, он только под macOS.
Если смотреть на скринридеры для Windows, первым на очередь приходит NVDA. Это бесплатный скринридер. По сути, он реализует то же самое, что VoiceOver, любые скринридеры делают примерно то же самое, но он менее функционален, менее удобен, более сложен в настройке.
Третье решение для Windows — платный скринридер JAWS. Классная, функциональная, мощная штука, такая же настраиваемая как VoiceOver, но за него надо платить.
Просто говорить о скринридерах особого смысла нет. Нужно посмотреть, как они работают, понять, что это такое. Поэтому я сейчас продемонстрирую на примере VoiceOver, как скринридер может читать ваш сайт.
Настройки скринридера в macOS находятся во вкладке Accessability или «Универсальный доступ», там есть раздел VoiceOver. Нажимаете на галочку «включить VoiceOver» или комбинацию Cmd + F5. Уверен, кто пользуется маками, случайно много раз так делал, потом долго искал, где это выключить. Нажимаем, и поехали.
Добро пожаловать в macOS. Скринридер включается, сразу начинает говорить. Все, что он проговаривает, видно в черном прямоугольнике снизу. Свернем это меню, зайдем в браузер и нажнем tab, чтобы попасть в первый элемент на странице.
Это главная Яндекса. Мы попадаем в первый элемент на странице. Сразу обратите внимание, что focus ring по умолчанию переопределен, он в стиле Яндекса. Что мы видим, когда попали на этот элемент?
Во-первых, скринридер сказал, что это ссылка. Он произнес заголовок этой ссылки. Во второй строке он показал то, какому элементу принадлежит эта ссылка. Она принадлежит блоку с ролью complimentary, это дополнительный блок на странице. Цифра 2 говорит, что помимо этой ссылки там еще какой-то элемент, то есть их два. Нажимаем tab и попадаем во второй элемент этого блока.
Попали в меню настройки. Скринридер зачитал правильную роль — это всплывающая кнопка. Вспоминаем, что это aria-haspopup. Эта кнопка также имеет дополнительное состояние, она свернута, и так как это кнопка, мы можем удобно нажать на нее с помощью Enter или пробела. Нажимаем пробел:
Статус поменялся, переходим дальше.
Мы прошлись по всем ссылкам в этом popup. Обратите внимание, что про просмотренные ссылки он так и говорит, что она просмотрена. Вспоминаем про фокус менеджмент. Мы говорили, что в подобных элементах круто, если бы фокус не вываливался вовне, а циклично в них ходил. Сказано — сделано. В этом элементе мы не можем фокусом попасть вне, это круто и удобно.
Возвращаемся в настройки, идем по странице дальше.
Мы попали в блок новостей. Судя по типу, это tablist, он выбран, у него стоит нужный статус. Ожидаемо мы ждем от него перемещения по остальным табам с помощью стрелок. И действительно, нажим