Выпуск 5

Доступность с клавиатуры. Требования, рекомендации, опыт разработки компонентов

Темы

Расшифровка

Таня Фокина: всем привет! С вами «Инклюзивный ананас» — первый русскоязычный подкаст о доступных интерфейсах. Здесь мы обсуждаем всё, что связано с цифровой доступностью и делимся свежими новостями и событиями. Подпишись, чтобы не пропустить новые выпуски.

С вами ведущие подкаста Глаша Жур, фронтенд-тимлид и преподаватель на курсах по веб-доступности, а также Таня Фокина, редактор раздела доступности в Доке, дружелюбном справочнике по фронтенду, и большая любительница поговорить о доступности.

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

Новости

Глаша Жур: BrowserStack предлагает нам протестировать работу их нового инструмента Accessibility Testing. Я чуть-чуть потрогала, очень классная штука. Они его называют Super App, и он включает в себя две части — Toolkit, плагин для браузера, и Dashboard, инструмент сбора данных и создания отчётов. Toolkit умеет проверять сайт на ошибки доступности в режиме слежения за пользовательскими действиями. Он ещё может более глубоко анализировать проблемы через так называемые гайды по тестированию. Также он даёт возможность проверить работу сайта с разными скринридерами в браузерах на реальных девайсах [устройствах].

Таня: а плагин Stark для Figma добавил несколько новых фич для проектирования доступных интерфейсов. Теперь при разработке макета можно добавить к блокам разные ARIA-нотации, то есть указать на роль элемента или какие у него должны быть ARIA-атрибуты, а также можно указать уровни заголовков и посмотреть их список и иерархию. И, кроме того, обновился Sidekick [Browser] — помощник по поиску ошибок доступности в макетах. Теперь он умеет проверять конкретные фреймы и игнорировать выбранные проблемы.

Глаша: за август по ADA зафиксировано более 100 обращений в суд в неделю. Всего их было 443. Из этих 443 — 100 повторные обращения, и 100, возможно те же, возможно какие-то другие, 100 — это обращения из-за использования лейаутов по добавлению доступности.

Кто пользуется клавиатурой

Таня: теперь давай перейдём к самому вкусному в сегодняшнем выпуске. И предлагаю начать с того, кто пользуется клавиатурой, и пользуются ли ей люди вообще, не считая игры.

Глаша: ты пользуешься клавиатурой в повседневной работе?

Таня: да, конечно. В интернете в формах точно. И бывает для того, чтобы быстрее между ссылочками в меню перемещаться. Да, я табаю. Но в IDE (Integrated Development Environment): без этого никуда.

Глаша: о, да, точно. Хотя у меня получается. Почему-то не могу никак к шорткатам привыкнуть, я только свои стабильные Ctrl + C, Ctrl + V. Только что научилась множественный фокус ставить, и то я каждый раз вспоминаю, как это делать. И на этом… Ctrl + S там, хотя Ctrl + S уже не надо в IDE нажимать, что такое! На этом моя навигация по IDE завершена. Мы, кстати, с коллегой, ну не с коллегой, а с другом смотрели на IDE-шки с точки зрения доступности десктопного приложения. Это такой трэш! Там столько всего, там всё по-другому работает. Даже скринридер по-другому взаимодействует с прилагой. Не так, как в вебе. И там нужно входить в какие-то супер особенные режимы, перемещаться супер особенными способами, и шорткаты там как будто бы must-have [обязательные]. Так что да, но о шорткатах мы ещё сегодня поговорим.

Так, ну что ж, Таня, каким же пользователям нужен доступ с клавиатуры к нашим интерфейсам?

Таня: кроме разработчиков, наверное, никому. Конец.

Нет, на самом деле, довольно большое количество людей ими пользуются. Очевидно, что пользователи скринридеров пользуются клавиатурой для навигации. Это не всегда и не обязательно пользователи со слепотой, но преимущественно. Кто ещё может пользоваться клавиатурой? Люди с какими-то особенностями моторики. Может быть, из-за какой-то неврологической штуки у них проблема с мелкой моторикой, и они не могут управлять мышкой, потому что не могут ей управлять. Или, например, они парализованы.

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

Глаша со смехом: я такой сюжет в своей жизни не раз испытывала.

Таня: да, вот! И на самом деле пользователи мышки тоже пользуются клавиатурой. И нет пользователей мышки в чистом виде, потому что многим удобно заполнять форму, табая от одного поля к другому. То есть люди, пользователи, склонны переключаться между устройствами управления, поэтому, на самом деле, клавиатура нужна всем, но только в разном объёме.

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

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

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

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

Ну, кстати, в iOS, по-моему, есть возможность подключения свитч-контролов или имитации свитч-контролов, и это можно тестить в iOS 100 %. А, и в Android, по-моему, тоже можно тестить. Эти контролы, они, как вы можете понять, не только для веб-интерфейсов, они также для мобильных интерфейсов, и важно проверять, как они работают на ваших интерфейсах.

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

Глаша: ну да, ещё мы в каком-то смысле сами иногда бываем животными, нам проще с клавиатуры что-то сделать, чем мышкой навестись.

Таня: ну давай теперь перейдём к тому, что должно быть в фокусе, да?

Глаша: да.

Элементы с фокусом

Таня: и о чём мы говорим? Что, собственно, должно быть в фокусе, чем мы должны уметь, точнее, иметь возможность управлять на сайте. Это логично, наши любимые кнопки, которые, как известно из прошлого выпуска скоро все починят…

Глаша с улыбкой: конечно.

Таня: … и нам не нужно будет их обсуждать. Ссылки и любые другие элементы, которые, собственно, сделаны на кнопке, но выглядят как таб [вкладка], например.

Страшная история про кнопки

Глаша со смехом: Таня, а ты видела хайп в Твиттере про кнопки? Кажется, их не скоро починят.

Таня: слушай, до меня последняя волна дошла людей, которые жалуются на то, что их достали кнопки, но я не знаю, о чём речь. Что за кнопки?

Глаша: это был, на самом деле, не такой уж прям хайп-хайп, но идея в том, что кто-то сходил на конференцию по вебу, и там был доклад от React-разработчика, как делать React лучше и класснее, и в том числе, как сделать его более доступным. И там прямо в примере кода в докладе был пример, где <div role="button">, и чувак говорит, вот чтобы сделать вашу кнопку доступной, добавьте ей role="button", и всё будет великолепно и шикарно. На что, естественно, девушка, которая сходила на эту конференцию, сделала постик в Twitter и сказала, что, господи, пожалуйста, сколько можно? Что вам этот button [роль] дался, почему вы не можете просто кнопку использовать? И там снизу, значит, коммент от чувака говорит, ну смотри, говорит, есть несколько пунктов, по которым мы не всегда можем использовать кнопку. Первый пункт, если кнопку добавить в форму, она будет сабмитить [отправлять] форму.

Таня: вау.

Глаша: да-да! Второй пункт… было что-то про disabled. Я не помню, но в итоге что-то типа, что очень сложно управлять состоянием disabled для кнопки, и что там надо что-то дополнительное делать. Третий пункт был про стили, естественно
(cмеётся), что для кнопки очень сложно сбросить стили. И там просто тысяча ответов ему, типа, чувак, учи HTML и CSS, потому что, чтобы убрать у кнопки поведение submit, нужно просто написать <button type="button">, а не тип submit, который по дефолту у кнопки в форме. Чтобы disabled управлять, достаточно просто добавить атрибут disabled на кнопку, если она задизейблена. А, там было, наверное, что-то из разряда, что там, визуал с disabled, не знаю… В общем, энивей [в любом случае], проблема решалась добавлением атрибута disabled.

И третий пункт про стили, естественно. Там прикольно что? Я помню-то про appearance: none, который просто вставляешь, и у кнопки все стили, естественно, пропадают. А там ещё есть какой-то стиль сброса тоже всех стилей для элемента, новый какой-то, более свежий. Короче, просто что?!! Так что не надейся, что эти кнопки когда-нибудь будут починены. Это мы ещё про фокусное состояние не начали говорить. Вот такая история.

Таня с иронией: cпасибо, спасибо, что просветила. Люблю role="button" добавлять, особенно ссылкам. Класс, получается круто. Главное, семантично. В конце-то концов, ты же задаёшь role!

Глаша со смехом: максимально семантика, да.

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

Глаша: у меня так разработчики, которым показываешь, что у скринридера есть возможность фокусироваться на конкретный тип элементов, они в этом случае добавляют фокусное состояние для хедингов [заголовоков] и параграфов, потому что, типа, у пользователя скринридера есть возможность по заголовкам перемещаться, а у обычного клавиатурного пользователя нет. (Со смешком) И почему бы не сделать? Поэтому забавно.

Таня: консистентный опыт.

Глаша: да, в данном случае, если что, это не про равный доступ к элементам. Совсем нет!

Таня: да, это немножечко про другое. Теперь давай поговорим подробнее о том, что, собственно, мы ожидаем от управления с клавиатурой в веб-интерфейсе и от фокуса.

WCAG 2.1 про клавиатуру

Глаша: да, и у нас есть отличное место, в котором мы можем начать этот разговор. У нас есть гайдлайны [руководства], в которых куча требований про обеспечение работы с клавиатурой, про состояние фокуса, про всякие возможности и невозможности пользователя. И, в частности, я, подготавливаясь к сегодняшнему выпуску, вспомнила в очередной раз про существование такого ресурса, как… он называется How To Meet WCAG. Это такой интерактивный WCAG, гайдлайн, где можно на вкладочке фильтры выбрать просто пункт «Клавиатура» и посмотреть все критерии, которые относятся именно к клавиатурной навигации по вашим интерфейсам. И сейчас мы пробежимся по списку. Я вам расскажу про в WCAG 2.1, который сейчас текущий стандарт, а Таня расскажет вам про новые штучки, которые добавили во WCAG 2.2, который мы вот-вот ждём. Всё ждём-ждём, никак не дождёмся. Новостей, кстати, там не было про это? По-моему, не было, да.

Таня: нет. Я специально перед выпуском посмотрела в надежде, что они послушали наш выпуск за август, где мы сказали, что до конца августа всё будет принято, и они не послушали всё ещё. Конечно!

Глаша с иронией: вот какие люди. А вот если бы послушали выпуск, то сразу бы всё приняли!

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

Критерий, который называется «Контент при наведении и фокусе» уровня AA, то есть мы обязаны его соблюдать, и он о том, что когда элемент получает фокус, либо на него наводятся мышкой, и этот элемент триггерит какой-нибудь дополнительный контент, который сначала видимый, а потом он должен скрыться при уходе с этого элемента, то нам нужно следовать следующим правилам. Во-первых, этот элемент можно закрыть, находясь на нашем, собственно, точнее этот текст можно закрыть, находясь на элементе. Прямо вот фокусом ты на кнопке, у тебя выпал какой-то попапчик с информацией, ты можешь его выключить, например, Esc, и на него, на этот элемент можно тоже навестись, и при этом он не закроется. То есть ты можешь оттуда какой-то контент, допустим, скопировать без закрытия этого элемента, пока ты не кликнешь где-то вне него на страницу, либо пока ты не стригеришь закрытие Esc, либо нажатием на вызвавший элемент. И ещё этот контент должен оставаться видимым, пока мы его, собственно, не закрыли. Следует из предыдущих пунктов. Вот единственное исключение — это когда такие штуки управляются браузером, то есть когда вы не можете на них никак повлиять. Например, title у тегов, которые мы можем к ним приделать, выпадашечка с текстом, браузер сам управляет её поведением, и мы никак не можем на это повлиять.

Следующий критерий — мой самый, наверное, любимый, потому что какая бы проблема у меня ни была в моём проекте, я всегда могу сказать, что это проблема клавиатуры 2.1.1. Обожаю, люблю, целую, что называется. Это критерий называется «Клавиатура». Он уровня A, то есть он даже суперобязательный. Без него нельзя в доступность соваться ни при каких обстоятельствах. О чём он? Он о том, что все элементы на сайте, которые предполагают работы с клавиатурой, должны быть доступны с клавиатуры без всяких дополнительных специфичных шорткатов, нажатий клавиш и так далее. То есть они должны быть доступны по стандарту, типа там Tab мы на них попадаем, Shift + Tab мы с них уходим, переходим на предыдущий и так далее. Это как раз вот про все те элементы, которые Таня упоминала. Это про кнопки, про ссылки, про всякие контролы формы.

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

Следующий критерий 2.1.2. Отсутствие фокусных ловушек, ловушек для фокуса. Это тоже критерий уровня А. Идея в том, что если мы куда-то перемещаем наш фокус, он не должен там застревать. То есть мы должны иметь возможность попасть в элемент и выйти из элемента. У меня в работе был один такой кейс [пример], когда мы делали поле добавления файла, и это поле расширялось. То есть мы добавляли файл, и он снизу добавлялся как отдельный фокусабельный элемент с крестиком, чтобы его можно было удалить и так далее. И вот всё это поле захватывало фокус пользователя и не отдавало его. То есть ты мог добавить файл, но выйти из этой области, Esc нажать или ещё что-нибудь ты не мог. То есть дальше пользоваться страницей не было никакой возможности. Ты перезагружаешь страницу, табаешься к этому элементу, естественно, чтобы ещё раз попробовать, опять застреваешь, снова перезагружаешь, и уже хочешь проскочить его, но проскочить у тебя не получается, потому что он находится в фокусной навигации твоей страницы. И всё, ты застреваешь там, и дальше, после него, ты ничего сделать не можешь.

Следующий критерий 2.1.3, он тоже называется «Клавиатура». Это более крутой критерий по сравнению с предыдущей клавиатурой. В нём никаких исключений нет. Ваше приложение полностью должно быть доступно с клавиатуры и не включать никакой функциональности, которая как-то альтернативно доступна и нет вот этой дополнительной клавиатурной поддержки. Этот критерий уровня AAA, то есть он для специфических интерфейсов, которые предназначены для людей с ограниченными возможностями.

Дальше идёт критерий 2.1.4. Он про то, что у вас на сайте есть шорткаты, если они есть, конечно. И вот в случае, если ваш шорткат состоит только из какого-то символа или цифры, или знака пунктуации, или ещё каких-нибудь дополнительных спецсимволов, то тогда вы должны предоставить следующие возможности пользователю. Во-первых, у вас на сайте должен быть механизм, чтобы это всё отключить. Шорткаты я имею в виду. Также должен быть механизм, который позволяет шорткаты переопределить, то есть добавить к ним, может быть, какие-то дополнительные клавиши. Не только одну вот эту оставить A, а, например Ctrl + A или ещё что-нибудь в этом роде. Также эти шорткаты могут присутствовать, но только в случае, если они используются, когда элемент прямо сейчас находится в фокусе.

Зачем это нужно? Если вспомнить, как работает скринридер, например, NVDA, то когда вы его запускаете, он переопределяет всю клавиатуру под себя. И кое-что он оставляет привычным для пользователя, перемещаться по элементам табами, а кое-что он переписывает. И когда вы на странице находитесь, фокус где-то там на странице у вас, и вы нажимаете одну клавишу, например, это клавиша D для скринридера NVDA. Клавиша D сама по себе перемещает пользователя скринридера по регионам, то есть по хедерам, футерам, секшенам и так далее: по элементам, которые скринридер считает регионами. И у него очень много функциональности завязано на нажатии одной единственной буквенной клавиши либо цифирной клавиши. Мы нажимаем один, мы перемещаемся по заголовкам первого уровня. То есть пользователь скринридера имеет свои клавиатурные сочетания, которые ему скринридер предоставляет. И есть шанс, что вы перебьёте поведение скринридера своим каким-то клавиатурным поведением. Так что тут нужно быть очень аккуратными. Один из выходов — это делать всегда двойные сочетания клавиш, типа Ctrl + A, Ctrl + D, Ctrl + C и так далее. Но тоже важно не перебить системные. Поэтому, да, реализация клавиатурных взаимодействий в плане шорткатов — это сложный процесс.

Таня: быструю ремарочку [комментарий] хочу сделать ещё про вот эту одну клавишу в сокращении клавиатурном. Этот критерий ещё важен для людей, которые по какой-то причине могут зажать одновременно две клавиши рядом или там, не знаю, например, у них тремор, они хотели нажать на M, которое у вас замьючивает видео. В итоге они нажали на N, а N вроде как на YouTube куда-то перематывает. Неважно, предположим, она перематывает. И вот, чтобы избежать подобных ошибочных нажатий на клавиши, лучше добавлять всегда функциональную. Так что здесь чуть больше пользователей этой штуки.

Глаша: вот, например, мы сейчас в StreamYard записываемся. Я навела на микрофон, на mute [выключить микрофон]. Естественно, написано нажмите Ctrl + D, чтобы замьютиться. Отлично! сразу на практике используем.

Ещё один критерий 2.4.3 называется «Порядок фокуса». Он также уровня A. И его идея в том, что все элементы на вашей странице, которые могут попасть в фокус, должны попадать в него последовательно. То есть фокус не должен скакать с первого элемента на 12-ый и потом опять на второй элемент. Для пользователя это будет большой проблемой. К сожалению, такое поведение часто достигается с помощью неправильного использования атрибута tabindex на элементах. Этого лучше, конечно, избегать.

Такое поведение, кстати (недавно, конечно, появилась эта штука в Firefox-браузере), но его можно тестировать с помощью специальной функции проверки порядка фокуса. Очень крутая фишка. И там сразу вы включаете в Firefox свой сайт, в DevTools выбираете проверить порядок фокуса, и вам выводятся цифры 1, 2, 3, 4, 5. Потом вам выводятся 17, 18, 19. А где находятся 6, 7, 8, 9, 10, никто не знает. Нужно проскролить страницу до самого дна, найти там 6, 7, 8, 9, 10. И получается, что пользователь, табаясь по вашему интерфейсу, сначала протабается через первые пять ссылок, потом его унесёт куда-то резким скролом вниз, потом обратно перенесёт его наверх. Забавная тема. Я ещё много показывала пример с сайта железных дорог, РЖД. Там у них на первой же странице есть форма, где перепутан порядок фокуса. Точнее, одному из всех элементов задан tabindex="5", а всем остальным он вообще не задан. Получается, что ты, открывая сайт, сразу попадаешь на тот элемент, у которого положительный tabindex, минуя все остальные. То есть неважно, что до этого были, какие элементы, что у них не был задан никакой tabindex, это тоже неважно. Как только он [браузер] видит положительный tabindex, сразу фокусится в него. Порядок фокуса важная штука, и появились инструменты, которые визуально позволяют его протестить.

Таня: а ведь когда ты, например, используешь flex-order, чтобы визуально поменять местами элементы, ты, получается, тоже, да, нарушаешь критерий?

Глаша: да, да, ты сбиваешь порядок фокуса. Есть классные всякие визуальные примеры на эту тему. Много в каких докладах об этом рассказывают. Да, это тоже проблема. Это может быть флекс, это может быть гридами изменён порядок элементов тоже. То есть CSS влияет на порядок фокуса, и важно это увидеть. Элементы будут фокуситься так, как они находятся в коде. Не важно, как они выглядят визуально. И если есть положительный tabindex, то эти элементы идут в приоритет.

Таня: держите свои гриды и флексы в узде, пожалуйста. Дизайнеры любят, кстати, они любят, чтобы на десктопе у тебя менюшечка, например, выглядела одним образом, а на мобилках кнопка, которая была на десктопе, наверху перемещалась вниз и так далее. И ты, как разработчик… либо нужно писать много кода, либо ты можешь по-быстрому гридами чуть-чуть подвигать на этом брейкпоинте. Так и живём.

Глаша: так, хорошо. Следующий критерий — это критерий 2.4.7, называется «Видимый фокус». Он уровня AA, то есть мы обязаны его выполнять, если мы следуем WCAG. Идея этого критерия в том, что все элементы, которые доступны с клавиатуры, должны иметь видимый фокус, когда фокус клавиатурный попадает, собственно, на них. Мы очень просим вас бездумно не сбрасывать outline для элементов, то есть не писать outline: none и не добавлять какую-то альтернативу. Кстати, линтеры умеют распознавать, что вы написали outline: none без альтернативы, и они вам подскажут, в частности, это Stylelint и его дополнительный плагин A11y Stylelint. Он вам скажет: outline: none, а где, собственно, альтернативные варианты? Ты не можешь просто так взять и написать outline: none. Сделай что-нибудь ещё. Сделай border на фокусе, сделай рамку на фокусе, что угодно сделай на фокусе, но не оставляй это так.

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

Следующий критерий, последний критерий во WCAG 2.1, это критерий 3.2.1, он называется «При фокусе», то есть что-то, что происходит, когда вы в фокус берёте ваш элемент. Этот критерий уровня A, то есть без него тоже никуда. Его идея в том, что когда вы фокуситесь на элемент, у вас не должен меняться пользовательский контент. Очень часто такое происходит, когда при фокусе на элементе сдвигается что-то, вылезает, появляются дополнительные какие-то части формы. Очень часто у нас на проектах это бывает фокус на радиокнопках, когда по дефолту сразу ещё заодно и инпут происходит, то есть выбирается кнопка одна из всех. Такой критерий, по-моему, тоже есть при инпуте. При выборе дефолтном в радиокнопке в форме меняется какая-то часть, потом ты выбираешь другую опцию и часть формы убирается, добавляется другая часть формы. То есть это называется сменой контекста пользователя. Пользователь об этом никак не оповещён, и поэтому это может быть для него проблемой. Он изучил форму и ожидал чего-то одного, а получил что-то совершенно другое. Также при фокусе не должны открываться новые какие-то элементы, модальных окон, не должна отправляться форма и так далее. Поэтому с этим будьте осторожны.

WCAG 2.2 про клавиатуру

Глаша: так, ну что, а теперь-то поговорим о WCAG 2.2. Что там новенького?

Таня: ох, теперь-то поговорим. Когда авторы WCAG 2.2 засели за изучение WCAG 2.1, они вдруг поняли, что всё это время, с 2018 года, мы жили без каких-то требований к тому, как, собственно, должен выглядеть индикатор фокуса. Он должен быть, но каким он должен быть, никто не знал, но догадывался. И немножечко вспомнили про такую неприятную штуку, как когда у вас всплывает какое-то окно, и вы можете из него, ну не модальное, конечно же, из него уйти под окно, и фокус где-то там под ним будет, но никто об этом не узнает, кроме вашего кода. Поэтому они [авторы] добавили целых три новых критерия. Два из них касаются как раз того, чтобы фокус не был скрыт другим элементом. Это 2.4.11 (АА) и 2.4.12 (ААА).

Критерий, который такой средненький, который нам нужно соблюдать, и мы можем это сделать, можем себе это позволить, в отличие от AAA. Это что элементы, находящиеся в фокусе, у вас не должны быть скрыты за другим элементом. То есть всё логично. Мне кажется, чаще всего это либо какое-нибудь сообщение сверху или снизу сайта, которое налезает на вашу менюшку или подвал. Ну, либо, да, какие-то немодальные штуки, которые бесят, конечно, но, к сожалению, это…

Глаша: а знаешь, какой самый частый кейс [пример] использования этого критерия? Такой. Разработчик забыл скрыть элемент, скрыл его только визуально, оставил его доступным с клавиатуры, и теперь ты табаешься через много невидимых пунктов меню, ты даже не представляешь, где они. Возможно даже это кейс [пример], когда у тебя есть десктопное меню и мобильное меню. Это два разных блока. У тебя есть пункты все эти в десктопном, а потом ты через все них невидимо проходишься в мобильном меню, потому что тебе же нужна красивая анимация при выведении меню, а анимации у нас на display: none, display: block не делаются же взрослыми милыми разработчиками. У нас же нет Animation API, которая позволяет нам всякие штуки делать. Да, поэтому вот такой вот самый частый кейс [пример], к сожалению. Это не про кривой дизайн, а про кривую разработку.

Таня: это про жертву доступности в угоду opacity и анимации. Да, я, кстати, помню, когда выкатили первую версию Доки, это было первое ишью, которое я принесла. Там ты мог сфокуситься на элементе из выпадающего списка, который opacity такое. Типа, я тут, но ты меня не увидишь, но можешь сделать фокус. Так что, да, это реально частая проблема.

Собственно, не зря эти пункты внесли. Кажется, что давно уже сообщество это просило. И есть, соответственно, продвинутый вариант этого критерия. Это когда у тебя даже частичный элемент не может быть скрыт. То есть, если вот этот предыдущий двойной минимальный критерий предполагает, что, ну, чуть-чуть что-нибудь может наехать, да, то есть, какая-то рамочка частично будет скрыта, но, в принципе, будет понятно, что ты фокус сделал. Это ок. В AAA нет, не ок. То есть, у тебя должен элемент быть полностью виден и его индикатор на странице. Ну и напоследок, на десерт, самый любимый, обожаемый всеми критерий 2.4.13. «Внешний вид фокуса».

Таня со смехом: итак, вспоминаем школьную математику. Вот это на самом деле, я подозреваю, тот критерий, из-за которого WCAG 2.2 так ещё и не стал рекомендацией. Потому что это… это надо, в общем, запастись терпением и мотивацией, с психотерапевтом поговорить перед этим желательно, чтобы его понять. Итак, если в общих чертах, то в этом критерии рассмотрены разные варианты, разные формы, например, ваших кнопок, и в зависимости от этого вам предлагаются разные опции, которые помогут в том или ином случае, например, кнопки со скруглёнными краями, круглые кнопки или квадратные, сделать этот индикатор видимым. И там есть куча каких-то своих, на своей волне, терминов про то, что бывает рамка у элемента, бывает окаймление. И, в общем, это очень может сильно запутать, но, по сути, смотрите, у вас бывают несколько ситуаций, точнее, две. Ладно, больше, я не сильна в математике.

Глаша со смехом: А тут тебе нужно [быть сильной в математике]!

Таня: да, да! У вас может быть несколько ситуаций, когда фокус, индикатор фокуса повторяет форму элемента, и это называется окаймление. Или когда он, ну, её не повторяет, например, находится где-то внутри. Такая полосочка, сбоку, справа, слева, не знаю, перечёркнутый пункт, такое тоже бывает. Это называется границей на самом деле, то есть она может просто быть внешней и внутренней. И значит, что к окаймлению, что к границе предъявляются одни и те же требования. Ну, например, то, что этот индикатор любой формы должен обводить элемент, ну, либо каким-то образом выделять этот элемент, да, опять же, рамочка внутри. Точнее, не рамочка внутри, а линия внутри. Также в этом критерии речь идёт о контрасте, соотношении контраста фона, на котором этот индикатор расположен, и, собственно, самого индикатора. Этого, вот, кстати, тоже не было в предыдущей версии, и теперь мы должны делать официально наш индикатор контрастным по отношению к фону.

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

Таня: может, он завтра уже всё, станет официальным, и не соврала, получается, не обманула. Так что, да, он [контраст] должен… Причём это может быть фон страницы, то есть если эта рамка выходит за границы элемента, то, соответственно, этот контраст мы должны измерять, точнее, это соотношение контраста мы должны измерять по отношению к фону страницы или того блока, в котором находится кнопка. А если это рамка, которая внутри кнопки, предположим, то, соответственно, здесь нужно смотреть на соотношение этой рамки и фона кнопки. Если у вас двойная рамка, то только одна из них должна быть контрастной, другая может отдыхать.

Глаша: что такое двойная рамка?

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

Глаша: а, это как в Chrome сейчас фокус двух цветов? Такая тема?

Таня: а я не знаю. А я не знаю, что в Chrome фокус двух цветов.

Глаша: да, ты когда ходишь фокусом по странице, у него два цвета, он чёрный и бёлый, там две линии. И чёрную ты видишь на белом фоне, а белую ты видишь на тёмном фоне. И, естественно, на каком-нибудь синем ты увидишь их обе.

Таня: ага! Это, кстати, такой хак, который, я помню, рекомендуют делать… да, как раз, когда у тебя, например, тёмная и светлая тема. Ну да.

Глаша: чтобы не заморачиваться сильно. Да, да.

Таня: цниверсальная такая штука, кстати. Я что-то не думала об этом и не обращала внимание. Угу. Так, и также [в критерии] речь идёт про толщину, что здесь как раз нужна математика, потому что они там приводят формулы того, как рассчитать толщину этой границы или окаймления. Но смысл в том, что вот эта штука должна быть… граница, на один CSS-пиксель больше минимальной области элемента, или больше четырёх пикселей. Короче, два пикселя ок. Перевожу. Один пиксель не ок, если кнопочка у вас большая. Но если кнопочка у вас маленькая, один пиксель ок. Вот, как-то так.

Глаша: а кнопочка не может быть меньше, чем 44 на 44, напоминаю. Target size. Который, кстати, переносят в AA вместо AAA в новом WCAG.

Таня: да, так что все, что двух пикселей, то хорошо. Но нужно, да, смотреть на размер элемента. Так, и что ещё? Да, если у вас, всё-таки, речь идёт о фокусе, который повторяет элемент, то здесь он может быть поменьше. Например, минимум один пиксель, то есть там не надо прям совсем такую жирную линию вокруг обводить. И ещё на размеры вот этого индикатора влияет какой это тип линии. То есть, чем менее заметна эта линия, например, пунктирная dashed, тем толще должен быть этот индикатор. И в случае пунктирной линии — это минимум четыре пикселя. И если, как я говорила, речь идёт об вот этом вот индикаторе, который находится внутри элемента, ну, типа такая жирная линия, то она тоже должна быть минимум четыре пикселя.

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

Единственное, что если речь идёт о списке ссылок или о ситуации, когда они расположены рядом друг с дружкой, между элементами должно быть расстояние, равное их величине или больше, чтобы пользователи не совершали случайный клик. Так, и что касается сложных элементов, там тоже про это рассказывается, но на самом деле ничего особенного. Common sense [здравый смысл]. Например, <select> таким элементом может считаться. Индикатор фокуса должен быть при попадании на элемент у всего элемента как у <select> и у вложенных в него элементов, пунктов. <option>, если это <select>. Но также могут быть ситуации, и они легальны, когда в фокусе оказывается довольно большая область. Например, у всего документа, и это частая история для текстовых редакторов. Это тоже всё легально, и там, собственно, те же самые требования касательно внешнего вида фокуса, что и у кнопочек, например.

Из этого пункта, самое последнее, обещаю, есть некоторое исключение. Вообще во WCAG всё, что касается того, что вы никак не кастомизируете, и что отрисовывается браузером, и предлагается браузером, это исключение. Здесь тоже, то есть, если вы просто добавили кнопку и никак её не стилизовали, то Edge, в котором ужасный фокус, я считаю, несмотря на то, что дефолтный фокус так себе, то это не нарушает критерий, потому что вы никак не кастомизировали этот элемент. Здесь вам даётся свобода, чтобы уж совсем вас не связывать по рукам и ногам. «Не стилизуйте кнопки», говорит вам WCAG. Тогда не надо будет думать о математике. Если вы не можете как-то кастомизировать стили фокуса у этого элемента, в <select> так бывает, например. Окей. Что есть, то есть.

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

Реальные кейсы, распространённые ошибки

Глаша: ну что ж, мы получили список критериев, по которым нужно проверять наши интерфейсы на доступность с клавиатуры. А теперь я бы хотела поделиться несколькими кейсами, когда у меня возникали или до сих пор возникают сомнения о том, как их использовать, куда ставить фокус и так далее. Особенно часто они возникают не у меня, а у моих коллег-разработчиков, на удивление. При том, что есть очень чёткие и ясные гайдлайны по каждому типу элемента, который мы используем. Энивей [ладно].

Что я чаще всего вижу на всяких сайтах, какие проблемы? В основном это проблемы с модальными окнами. Супер часто фокус остаётся на элементе, который это модальное окно открывал. То есть вы пришли на какую-то кнопочку с клавиатуры, нажали Enter или пробел, если вам очень сильно повезло, и всё это не было перебито JavaScript. У вас открылось модальное окно, и дальше мы должны действовать по принципам управления фокусом. Есть прям такой раздел доступности — фокус-менеджмент. Там очень много всяких принципов, и один из них в том состоит, что при открытии модального окна фокус автоматически должен переноситься внутрь этого модального окна. И у нас всегда вопрос, куда фокус переносить.

У нас есть разные типы модальных окон. Например, есть модалка, где много контролов. То есть это форма внутри модального окна. И вот куда ставить фокус, и как все это будет звучать? Самый классный способ по итогу ресёрчей [исследований] — это поставить фокус в первый инпут внутри модального окна. Очень часто мы говорим — первый фокусабельный контрол, и из-за этого фокус иногда попадает на крестик закрытия модального окна. Поэтому крестик закрытия нужно по коду, он обычно там абсолютно как-то спозиционирован, его по коду нужно в конец модалки перенести, чтобы он в порядке фокуса встал за всеми контролами. Вот, а фокус попадает на первый инпут [поле].

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

Для пользователя зрячего с клавиатуры попасть сразу в поле ввода это удобно. Он видит весь контекст, он может сразу начинать вводить штучки. Есть модалки, где перед контролами много текста какого-то дополнительного. Например, у нас есть модальные окна, где сначала табличка со всеми данными, а потом уже идут какие-то дополнительные экшны [действия] к этой табличке. Можно что-то отфильтровать и так далее. Тут фокус, я бы ставила в начало модального окна, то есть после его заголовочной области. Может быть, если там есть заголовок в модалке, то на заголовок ставить фокус (тут Глаша дала вам плохой совет, не ставьте фокус на заголовок, потому что скринридер прочитает его два раза: один раз из aria-атрибутов модалки и второй раз — из самого заголовка — примечание Глаши). Это как раз тот кейс [пример], когда мы можем делать фокусабельными какие-то текстовые элементы типа заголовков или параграфов или таблиц. Но это нужно делать не с помощью атрибута tabindex="0", а с помощью атрибута tabindex="-1" Такой атрибут позволяет вам установить из кода, из JavaScript фокусный элемент, и при этом с клавиатуры табом вы попадать на этот элемент не будете. То есть tabindex="-1" в целом предназначен иногда для того, чтобы у контролов убирать фокусабельность. То есть на кнопке tabindex="-1" сделать кнопку недоступной с клавиатуры. Но при этом там, конечно, она доступна для всего другого, но это другой разговор.

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

Ещё у нас есть часто такие модальные окна, где нужно что-то подтвердить, и внизу кнопки находятся. «Отменить», «Подтвердить», «Согласиться» и так далее. И в таких модалках очень часто ставят фокус сразу на кнопку, то есть ты открываешь модальное окно, и у тебя фокус улетает вниз. Ты не можешь проскролить текст, прочитать его, что-то с ним сделать удобное тебе, тебе нужно вернуться назад, чтобы это сделать. То есть правильнее в таких модальных окнах будет поставить все-таки фокус на вот этот вот, собственно, параграф с текстом. Если он скролится, то важно, чтобы он скролился стрелочками там вверх-вниз, то есть чтобы там был нативный скрол. Да, то есть не надо ставить на экшны [действия] сразу. Пользователь должен попасть на текстовый элемент. Для скринридеров это особенно важно, так мы сразу их сориентируем на контент.

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

И тут у нас, например, на проекте возникла проблема. А что, если модальной окно было открыто из пункта меню, который закрылся, когда мы открыли модальное окно. Честно говоря, решение очень сильно зависит от вашего кода и от того, как сейчас всё работает, и от срочности, которой вам нужно пофиксить все эти проблемы. В нашем случае проще: открываешь пункт меню, ну, точнее, всё меню открываешь, фокусишься на пункт меню, нажимаешь Enter/пробел. Открывается модальное окно, сейчас закрывается меню сзади, чего я как бы не хотела, но пока нет возможности это быстро исправить. Меню закрывается, модалка открывается, мы по ней ходим, фокусом делаем все действия, закрываем модальное окно, и фокус переносится у нас на ту кнопку, которая открывала пункт меню, то есть на родителя триггернувшего элемента. И всё-таки я бы хотела, чтобы она возвращалась в открытое меню, чтобы меню не закрывалось. Не всегда это, конечно, возможно с точки зрения дизайна, UX, поэтому тут нужно думать. Да.

Ещё у нас часто возникают вопросы, что делать, если мы удалили какой-то элемент из списка, куда ставить фокус, или что делать, если мы что-то нажали, и, собственно, у пользователя поменялся контекст на странице, то есть вылезла какая-то дополнительная информация снизу, сверху и так далее. Про удаление элементов мы с вами поделимся ссылочкой на статью Адриана Розелли, который… его достал вопрос, куда же ставить фокус при удалении элементов. В этой статье он рассказывает, если очень коротко, то у вас есть набор элементов в списке, например. Вы ходите по этому списку, встали на один из элементов, нажали кнопку «Delete», элемент удалился. Куда поставить фокус? Фокус часто ставят на родителя, то есть на сам список. Могут его ещё поставить на какие-нибудь кнопки дополнительные. Например, есть удаление элемента, и потом появляется сообщение «Может быть, вы хотите восстановить этот элемент? Что вы его удалили?» на месте элемента. Может быть, фокус пойдёт туда.

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

Важно, чтобы пользователь оставался в контексте списка либо рядом с ним, и чтобы его не переносило никуда в конец страницы. Типа там: удалите элемент, дальше появляется модалка «Элемент будет удалён. Вы уверены?» Вас переносит в эту модалку. Вы нажимаете «Да, я уверен». Модалка закрывается, элемент удаляется. И где фокус? Чаще всего он вообще в начало страницы улетает. Это уже зависит от браузера, куда он поставит фокус пользователя, если мы не будем им управлять. Поэтому важно им управлять, собственно. И у меня буквально недавно был прикольный кейс [пример].

Я тестила выпадашечки на сайте Walmart. Это сеть супермаркетов в Америке, если я ничего не путаю. Ещё у них в Канаде, конечно же, есть подразделение. Это очень крупная сеть. Поскольку они в Америке, они обязаны обеспечить доступность. 7 сентября на них выкатили иск коллективный о том, что они не обеспечили доступность. Я так смеялась с этого. Потому что до 7 сентября я каждый день была на сайте Walmart и тестировала их маркетплейс. Они не очень давно просто анонсировали свой маркетплейс.

Таня с улыбкой: признавайся, это ты этот иск накатала на Walmart? Вот ты и попалась!

Глаша со смехом: нет, конечно нет. Ты что? У меня нет юридического права накатывать иск на Walmart, я полагаю. Находясь в Литве, вряд ли я могу это сделать. Но энивей [в любом случае], я могла бы подговорить кого-нибудь из друзей. У меня есть парочка живущих в Америке.

Так вот, про Walmart. Я тестировала. Естественно, я сразу всегда сайт и начинаю тестировать клавиатурой, потому что так быстрее и удобнее. Сразу заходишь на сайт и начинаешь табаться. Конечно же, первое, что ты встречаешь, это невидимый фокус практически в 90 процентах случаев. Но на Walmart всё было прекрасно. Весь фокус был видимый, там был skip link, кстати, который при нажатии перемещал меня в основной контент, но сам фокус перемещался не туда, куда было видно. И это как раз таки подпало под новые критерии в WCAG про то, что фокус где-то там находится, перекрытый каким-то элементом сайта. То есть там хедер наезжал на контролы, которые были до основного контента, и эти контролы табались, а я этого не видела.

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

И, короче, там, значит, вот этот вот попапчик изменения цены. Ты табаешься на кнопку, которая вызывает попап, нажимаешь Enter, вылазит попап. Фокус свой попап не переносится. Попапы — это не модальные окна, ну в данном случае. Хотя я бы туда все равно запихивала всю идею модальности, потому что для клавиатурного пользователя, если он мышкой не пользуется, то ему как бы всё равно, модально это не модально, он все равно хочет пользоваться только этим окном, он не хочет выходить за его пределы, пока вот оно открыто. То есть я бы там всё-таки циклила фокус внутри, но это уже, конечно, так, на усмотрение. Либо зацикливаете фокус, либо, когда мы выходим за пределы этого попапа и выходим от этого элемента, фокус переносим, то, наверное, не знаю, пусть бы он закрывался, но мы только что говорили об этом про критерии WCAG, что не надо, чтобы они закрывались. В общем, тут уже UX-решение с мыслями об accessibility. Энивей [ладно], моё предложение всё-таки зациклить там фокус.

Там внутри этого попапа, значит, слайдер, а слайдер не в смысле картинки перелистывать, а слайдер как HTML-элемент, который type="range". Можно изменять границы чего-либо, верхнюю и нижнюю границы какого-то промежутка. Ну и там, значит, ценовой диапазон этот. И у тебя есть возможность зафокуситься на нижнюю границу, на верхнюю границу. С клавиатуры это происходит так. Точнее как, с мышки это происходит так. Ты двигаешь мышкой одну границу, и у тебя автоматически отправляется запрос, и ты перерисовываешь страничку в соответствии с тем, что ты подвигал. То есть фильтр применяется автоматом. Закрывается, и ты такой, ладно, хорошо, теперь давайте другую границу. Открываешь снова этот попап, двигаешь другую границу, у тебя применяется автоматически действие, закрывается попап. Что хочешь с этим делай. Но ajax, который там на странице используется, это большая-большая проблема, это другой вопрос.

Но с клавиатуры этот попап работает вообще не так. Ты типа открываешь попап, фокусишься внутрь этого попапа, переходишь на нижнюю границу, двигаешь её стрелками, всё великолепно, там все озвучивается. Ну потому что нативный, по-моему, range там используется под капотом. Пока ты двигаешь, ничего не применяется. То есть с клавиатуры никакого сабмита не идёт. Ты нажимаешь на Enter, ну привычно как бы, чтобы засабмитить свои изменения, ничего не происходит. Попап там не закрывается, фильтр не применяется, ну ты без понятия вообще что делать. Оказывается работает пробел, но пробел это ж не первое, на что ты нажимаешь, чтобы что-то засабмитить. Ты на Enter нажимаешь, но если пробел случайно нажал, то засабмитится, всё закроется, страница перезагрузится, фокус улетит вообще бог знает куда. И ты снова открываешь этот попап, чтобы верхнюю границу поправить. Но пока ты вот не засабмитил, ты можешь и верхнюю, и нижнюю с клавиатуры поправить. С мышки автоматом променяется всё, с клавиатуры обе можешь потрогать. И вот ты правишь, и потом ты нажимаешь этот пробел, чтобы засабмитить, применяется только одна из двух. То есть ты закрывается попап, применяется только первая, которую ты поменял, вторая сбрасывается в предыдущее состояние. Ты снова табом ищешь, где этот контрол, попадаешь в него, открываешь попап, пытаешься вторую применить, применяешь, она заново применяется, заново всё перезагружается.

Короче, очень прикольная доступность на американском сайте, который должен быть эталоном доступности. Но там много таких прикольных кейсов [примеров]. Так что то, как это всё читается со скринридера, я даже объяснять не буду. Это такой трындец. Там всё очень интересно. И там отсутствует всяческое управление фокусом в подобных элементах. Например, выпадающий список сортировки. Казалось бы, это список пунктов, где нужно стрелочкой перемещаться по элементам. Но поскольку там это всё кастомизировано, чтобы стили красивые нарисовать, никто не сделал это комбобоксом. И это теперь просто попап с кучей кнопок, которые табаются каждая отдельно, нажимаются и таким образом сортируется страница. Короче, прикольно. Много интересных кейсов [примеров] на этом вашем Walmart с клавиатурой. Прям, муа, понравилось.

Таня: я сейчас пребываю в лёгком шоке от описания твоего пользовательского опыта. Да, бедные люди со скринридерами. Тут, конечно, мне кажется, надо выкидывать WCAG и в первую очередь задавать вопрос такой с точки зрения UX, а зачем это прятать какие-то попапы? То есть это звучит как довольно важный функционал, почему его надо скрывать со страницы? Это же основа основ. Всякие различные тултипы и что-то вдруг внезапно выпадающее при нажатии на кнопку должно там быть, только если оно не обязательно. А если это что-то важное, то его надо просто оставлять на странице и тогда не будет проблем, тогда нам не надо будет думать о управлении фокусом, куда там что должно попадать.

Глаша: я хотела тебе сказать, что там так и сделано. Там эти фильтры продублированы, видимые на странице, но, дело в том, чтобы попасть, в них нужно попасть в левую часть с фильтрами и раскрыть аккордеон. И тогда ты увидишь эти фильтры на странице. Попапов нет, есть аккордеон. Это другое, ты не понимаешь, это другое.

Таня: нда, уже теплее, теплее.

Глаша: он, кстати, закрывается после сабмита и после перерисовки всех элементов страницы, кроме хедера.

Таня со вздохом: Прекрасно. И ещё я хотела сказать про модалки, добавить из практического. Во-первых, хорошо поддерживать ещё нажатие на Esc, чтобы закрывать, чтобы кнопочка была, например, для пользователей мышки, для тех, кому удобно эту кнопочку нажать, и чтобы Esc работал, эскейпилось. Интересно, изобрела я это слово или оно существовало? В общем, чтобы Esc работал. И нативный <dialog> прекрасно с этим справляется уже, так что можете его смело использовать. Там и ловушка фокуса автоматически устанавливается, в Chrome были проблемы, но их исправили. И по Esc [нажатию] события срабатывают и все нужные атрибуты навешиваются в зависимости от того, какой метод показа вы используете. Будьте уверены, если вы используете метод для модального окна, там совершенно точно будет состояние его открытости. Ну и уже молчу про то, что там есть встроенная роль dialog. С alertdialog пока что такого нет, тут надо кастомно делать, но в принципе можно навесить alertdoalog на <dialog> при желании. Но это очень редкий паттерн, который вообще в супер редких ситуациях, когда что-то очень критичное, что-то очень критичное мешает, всё поломалось вот только тогда. Так что ладно, не будем об этом.

Хотела ещё рассказать, тут поделиться проблемой управления фокусом на странице с куки, потому что обычно на сайтах мы встречаем два паттерна. Обычно это фиксированная всплывашка, ну или сразу при загрузке страницы, появляющаяся внизу страницы чаще всего, иногда наверху страницы. За редким исключением бывают куки, которые всегда находятся внизу страницы и не фиксированы, то есть когда вы скролите, вы их не видите. И вот тут вопрос: что делать с такими сообщениями? Вот эта кнопочка «Понятно», или «Принять», когда мы на неё должны попасть?

И я сейчас на сайте Доки пытаюсь побороться с этой проблемой, потому что там по классике в конец кода добавлено сообщение о куки с кнопочкой внутри. Соответственно в DOM, объектной модели документа, это в конце страницы. Соответственно вы должны протабать через всю страницу, чтобы дойти до этой кнопки, а ещё этот куки загораживает футер, ссылки в нём. И вот что делать в такой ситуации? Первый выход, о котором я узнала и сейчас размышляю, это добавлять такие сообщения, уж если они фиксированы, в самое начало страницы. Да, бесит, но вы можете как пользователь клавиатуры сразу же попасть на эту кнопку принять, а в каких-то случаях на кнопку с настройками куки и закрыть это.

Глаша: это бесит только разработчиков.

Таня: да. Второй вариант, он интересный и может, наверное, спасти ситуацию, когда у вас это сообщение всегда торчит в самом низу страницы и фиксировано при этом. Это skip link. Skip link прямо к куки. Ведь в skip link мы можем добавлять несколько ссылок, которые ведут, например, в основную область страницы или, например, если у вас есть поиск по сайту к поиску сайта, там не надо 100 500 ссылок размещать, но две–три окей.

Так что можно в случае, когда вы не можете дизайнерски решить эту проблему, вот такой вот хитрый способ придумать, который будет доступен как для пользователей скринридеров, так и для пользователей клавиатуры, которые видят интерфейс. Ещё интересный вопрос, это как управлять фокусом, когда у тебя кнопка «Показать ещё», которая удаляется. То есть она открывает какой-то контент на странице, например, список ссылок на новости, на новостных сайтах это очень частая ситуация. Вот куда фокус направлять в таком случае? И я не уверена. На первый элемент в раскрывшемся списке?

Глаша: у тебя есть кнопочка «Показать ещё», ты там читала-читала текст, затабилась на эту кнопочку «Показать ещё» и нажала на неё и кнопка такая: «До свидания»? Интересно, что она удаляется в твоём случае, потому что обычно она перевоплощается в кнопку «Скрыть подробности» или что-нибудь ещё. То есть ну вообще, что только с этими кнопками не делают. Кажется, что если она остаётся и просто меняет свой текст, то можно ничего не делать. Но да, то есть не хотелось бы фокус пользователя переносить куда-то. Но, кстати, в коммьюнити тоже обсуждали эту тему, и ребята говорят, что нормально поставить фокус в начало текста, который открылся.

То есть ту часть, которая была видимая, мы уже прочитали, и нормально поставить в ту, которая была невидимой. Но с другой стороны, это часто кусочек текста, где в конце три точки такие заблюрены, и там может даже слово не до конца договорено. И куда тогда ставить фокус? Кажется, что тогда на начало всего текста. Ну то есть пользователи скринридеров, кажется, не сильно возмущаются по поводу того, что их переносит в начало текста. Единственное, что да, кнопку эту, конечно, хотелось бы сохранить, чтобы она никуда не исчезала. Но если она удаляется, то что поделать?

Таня: я с этим разбиралась, опять же, на сайте Доки, и в итоге я переношу фокус на первый интерактивный элемент, то есть ссылку. Там просто это список ссылок. Соответственно, я переношу на первую ссылку, которая была скрыта в этом блоке. Это, наверное, самое логичное здесь решение. Но вообще в идеале, конечно, не делайте так, чтобы ваши кнопки исчезали. Ну серьёзно, это очень странно. Не знаю, это на слух странно: куда делась эта кнопка, почему она удалилась? Это странно визуально.

Так что, если можно, максимально оставляйте все на странице. Особенно, если это кнопка, которая что-то там открывает. Не лишайте пользователя, во-первых, возможности контролировать полностью интерфейс. Это, кстати, один из критериев WCAG, да и вообще, в принципе, вещь, которая базово понятна, про удобство интерфейсов. А во-вторых, ну вот не надо будет потом думать, что там куда этот фокус деть с внезапно исчезнувшей кнопки. И последнее, что хотела обсудить, тоже связано с попапами, — это тултипы. Клёво, когда они появляются при взаимодействии с элементом клавиатурой. То есть, title, которые любят многие использовать в надежде, что все всё узнают, так себе решение. Если у вас много каких-то тултипов, которые там содержат дополнительную информацию, клёво всё-таки делать, пока нет нативного решения, кастомный тултип, и давать пользователям клавиатуры возможность его увидеть.

Глаша: кажется, что вроде бы уже же делают нативный тултип.

Таня задумчиво: я не знаю.

Глаша: не помнишь? Да, по-моему, в HTML делают новый свеженький классный нативный тултип, который будет хорошо работать, всем понравится и будет хорошо использоваться. По-моему, туда даже интерактивные контролы можно будет вставлять что-то такое. Это не то, чтобы было про тултип, это, по-моему, там про попап всё. Но кажется, что его можно будет использовать как тултип в том числе.

Руководство от W3C Developing a Keyboard Interface

Глаша: в общем, чтобы как-то структурировать ваши знания, которые вы сегодня от нас получили, мы предлагаем вам прочитать очень крутой гайд [руководство] от W3C. Он так и называется Developing a Keyboard Interface. То есть разрабатывая… Да, конечно, давайте напрямую переводить.

В общем, рекомендации по разработке клавиатурного интерфейса со всеми-всеми подробностями. И там вы узнаете, какие есть основные паттерны использования клавиатуры у пользователей, вообще у всех типов, у скринридеров, там, не скринридеров и так далее. Вы узнаете, что у каждого виджета в HTML, а под виджетами мы понимаем как инпуты [поля], к которым мы привыкли, и составные формы, так и какие-то кастомные виджеты, которые есть сейчас только в нашей спецификации WAI-ARIA. То есть скринридеры их прекрасно понимают, озвучивают, если мы правильно их заверстали. Но в HTML они всё ещё не представлены нативными элементами. Такие вот виджеты у нас есть. Для них куча всяких клавиатур надо писать, там обрабатывать, фокусы и так далее. То есть это тоже там будет все описано, как именно с клавиатуры к каждому виджету подступиться, например, в списке, как правильно управлять клавиатурой.

Также вам там напомнят, что фокус должен быть видимым, что есть определённый порядок фокуса, и там про tabindex ещё раз почитаете подробно. Вам расскажут там, куда перемещать фокус в случае каких-то сложных кейсов [примеров]. Вам расскажут, как в большом наборе однотипных фокусабельных элементов перемещаться стрелочками. Какие есть способы, как реализовать перемещение, чтобы скринридер не сошёл там с ума. Очень важная вещь клавиатурной навигации – это то, что скринридер ориентируется на роль вашего элемента. И для этой роли он использует клавиатуру и подстраивает её под эту роль. Поэтому если у вас какая-то неправильная роль, но вы пытаетесь скринридеру намекнуть, что тут стрелочками надо перемещаться, вы не получите этого. У вас не получится скринридер именно заставить переходить стрелками. Без скринридера все будет работать шикарно. С ним начнутся проблемы. И только в определённых ролях он ожидает работу со стрелками. Поэтому это всё описано в этом документе.

Про шорткаты также описано. Какие стандартные есть шорткаты тоже прописано: для Windows, Linux и для macOS всяких там. Как открыть контекстное меню, скопировать, вставить и так далее. Вот супер такие стандартные штуки. Поэтому, пожалуйста, обращайтесь к этому документу, прочитайте его весь, как мы с Таней это сделали в своей жизни очень много раз, конечно же (смеётся), каждая по четыре раза. В общем, очень-очень советую его. Он классный и он вправляет мозги и рассказывает вам, что вы можете делать, что вы не можете делать, создавая ваш интерфейс для клавиатуры.

Таня: последнее наставление от нас. Думайте о пользователях клавиатуры. Думайте о пользователях, которые используют что-то похожее на клавиатуру, но не клавиатуру. То есть какие-то специальные вспомогательные технологии, которые имитируют её поведение. И самое последнее. Читайте руководства, читайте всякие статьи и тестируйте, экспериментируйте. Доступность — классная штука, которая помогает вам задумываться о том, о чём вы никогда не задумывались, и нагружать свой мозг!

Таня: с вами был подкаст «Инклюзивный ананас» и его ведущие Глаша и Таня. Вы можете найти нас на любой подкаст-платформе. До встречи в следующем выпуске!