Pull to refresh

It's a (focus) Trap

Reading time 5 min
Views 15K
Люди которые пишут стандарты — очень хитро устроились. Им достаточно написать как должно все хорошо работать, а дальше уже не их проблемы.

Примерно так и получилось с тем «как должны работать диалоги», точнее «правильные» с точки зрения a11y модальные диалоги.

В описание к dialog role на MDN все написано очень просто:

  • The dialog must be properly labeled
  • Keyboard focus must be managed correctly

Проблема в том, что MDN забыла еще об одном важном пункте, а все остальные забыли про один из сказанных – про то, что модал не должен выпускать фокус из своих рук. Активный элемент надо посадить под замок. Не дать ему сбежать из нашей ловушки.



Modal dialog


История началась совсем недавно — в рассылке Веб-стандартов попалась мне ссылка на «правильный» WAI-ARIA Dialog. И понеслось.

Компонент на самом деле хорош:

  • он вешает aria-hidden на страницу, чтобы скрыть контент от screen-readers (работает только в первом примере).
  • он затеняет контент и вырубает скрол странице.
  • контролирует фокус, так чтобы из модала нельзя было табнуться.
  • после закрытия диалога он возвращает фокус на исходную позицию.
  • и добавляет разные aria-специфичные тэги, конечно же.

Те он делает все что просит MDN и даже больше, так как без первого пункта «выйти» из диалога с активированным screen reader — не составляет никакого труда.

В общем — must have!



Focus


Но вот реализация "focus-management" немного подкачала — ребята реально перехватывают keyboard events(и не только) и эмулируют кнопку tab самостоятельно.

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

Начнем с сайтов (немного предвзятая выборка):

  • Google Gmail|G+ — идеально. ️
  • Одноклассники — с уходом таба закрывают модал, на который фокус так и не приходит
  • FB — зависит от страницы. В группе/на личной страницы — ничего нет, в момент написания сообщений есть. Никогда не жмакайте Таб(в сафари) на главной — крышу сносит.
  • VK — страница «рандомно» игнорирует таб ️
  • Yandex.Maps — страница полностью игнорирует таб ️
  • Yandex.Music — страница полностью игнорирует таб ️
  • РСЯ — нет focus management.
  • LiveJournal — нет focus management.
  • Мои собственные сайты — нет focus management.
  • Habrahabr — нет ни focus management, модалов
  • Jira/Confluence — идеально. ️

Вывод простой — у «нормальных» сайтов немного не хватает мозгов, а Яндексу руки оторвать.

С фреймворками (немного предвзятая выборка) сильно интереснее:


С фреймворками оказывается тоже совсем плохо. И совсем никто-никто не вешает aria-hidden на остальной контент, чтобы сделать модал на самом деле доступным для людей, которые вынуждены использовать скрин-ридеры.

Офтопик


На самом деле я тоже раньше совершенно не заморачивался всей этой фигней, но тут, как на зло, моя дорогая жена решила научиться верстке в HTML Academy где pepelsbey с большой-большой компанией заставили и ее и меня задуматься над вопросом насколько таббабельный у меня сайт.

Пришлось и науку новую выучить, и проблемму решить с фокусом.

PS: Вадим рекомендует забить на всю эту aria-hidden с focus-management и воспользоваться html атрибутом inert, который просто «выключит»(врям совсем) все кроме модала и проблем нет будет ни с screen/reader, ни с фокусом.

Хотя насчет второго не уверен, да и работает он пока не очень, а полифилы просто ужасающие.....

Focus Lock


В общем, как говорили на улице Льва Толстого… – а какие же ваши предложения.

На самом деле проблема очень проста — не смотря на то, что для JS было написано миллионы модулей — модулей для focus management фактически нет.

  • focus-manager — простой focus-manager с простым и ванильным API и отличным примером. Есть пара минусов
  • ToleFocus — какой-то монстр, от которого бежать хочется.
  • react-focus-trap — настолько простой, что возвращает фокус только в начало.
  • Focus manager из AUI, но кто раньше слышал про AUI?
  • focus-trap, он же focus-trap-react который был использован в WAI-ARIA демке в начале статьи. И который по дефолту выключается по Esc и вообще не очень правильно использует DOM-API

В общем 7 бед = +1 новый велосипед. А точнее настоящий поезд из focus-lock, dom-focus-lock, react-focus-lock и vue-focus-lock — на все случаи жизни.

Со стороны обертки (react, vue, dom) все очень просто — получить DOM ноду и закрыть в ней фокус. Вся соль именно в focus-lock.

Причин создания новой библиотеки несколько:

  • К сожалению все решения(кроме focus-trap/lock) совершенно полностью игнорируют tabIndex и становятся полностью неработоспособными если один умный програмист сломает порядок таббания.
    Случай, конечно, немного синтетический, но вполне реальный. К моему большому большому сожалению.
  • Из всех решений (кроме focus-trap/lock и react-focus-trap) можно без проблем табнуться в сафари(JFYI: сафари различает Tab и Opt+Tab). И если фокус единожды покинет ловушку — назад его уже никто не вернет.
  • focus-trap, который так хорошо везде работает, делает это потому что перехватывает и эмулирует Tab, те полностью игнорирует настройки того же Safari пунктом выше.
  • Все решения(кроме focus-lock и BluePrint.js) по входу селектят первый элемент, а не элемент с автофокусом.
    PS: focus-trap ищет элемент с атрибутом initialFocus. С чего бы?

Так что пришлось сделать очередной велосипед, который временно отвечает чуть большему списку свистелок, чем его ближайшие конкуренты. Или конкретно всем.

Просто оберни модалы(и не только модалы) в FocusLock — и половина проблем будет решена
Демо React-focus-lock — codesandbox.io/s/jvl0k6zyk3 (найдите разницу).
Демо Vue-focus-lock — codesandbox.io/s/l5qlpxqvnq

<FocusLock>
   <Modal>
     any data
   </Modal>
</FocusLock>

Но только половина, так как aria-hidden (или inert) вешать прийдется кому-то другому и куда-то в другое место. Но это уже другая история.

Итого


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

Но самое главное — не забывай что не надо мешать пользователю оперировать с сайтом не только мышкой.

PS: А еще лучше включить VoiceOver или другой ScreenReader и попробуйте свои сайты на прочность. Будете удивлены.
Многие вещи, например «ручная клавиатурная навигация» в ЯндексПочте — дефакто не не меняет активный элемент.
Одного програмиста из Финляндии Яндекс точно потерял как пользователя.

PPS: Gmail, правда, не так чтобы сильно лучше.


В общем — не попадайтесь — github.com/theKashey/react-focus-lock
Tags:
Hubs:
+18
Comments 24
Comments Comments 24

Articles