Клавиатурный доступ и фокус
Что это такое
Клавиатурный доступ — гарантия, что каждое действие, доступное мыши, достижимо и с помощью Tab, Enter, Space и стрелок, а пользователь всегда видит, где находится. Фокус — метка браузера для «элемента, который получит следующее нажатие клавиши», то есть курсор пользователя с клавиатуры и его чувство положения.
Почему это важно
Многие пользуются клавиатурой, потому что не могут пользоваться указывающим устройством, а многие другие переключаются на неё ради скорости. Если элемент управления недостижим по Tab, для них его не существует, и обходного пути нет. Фокус — более трудная половина: невидимое кольцо создаёт ту же проблему, что и полное его отсутствие, ведь пользователь печатает в то, что не может опознать. Обе ошибки невидимы разработчику, который проверяет мышью, — именно поэтому они и доживают до продакшена.
Ключевые идеи
Порядок табуляции повторяет DOM
Порядок табуляции — порядок документа, поэтому если визуальный порядок создан перестановкой через CSS — order во flex, размещением в grid, абсолютным позиционированием, — они расходятся. Пользователь с клавиатуры проходит страницу в последовательности, которая выглядит случайной: исправлять нужно разметку, а не добавлять положительные значения tabindex, которые ломают естественный порядок для всех последующих.
Никогда не убирайте кольцо фокуса, не заменив его
outline: none — самая распространённая ошибка доступности. Если кольцо по умолчанию конфликтует с дизайном, замените его более чётким, которое не опирается только на цвет.
:focus-visible {
outline: 3px solid teal;
outline-offset: 2px;
}
focus-visible показывает кольцо при работе с клавиатуры и обычно скрывает его при клике мышью — компромисс, которого хочет большинство интерфейсов.
Держите фокус там, где пользователь его видит
Всё, что перемещает внимание пользователя — открытие меню, открытие диалога, удаление строки, на которой он был, — должно намеренно переносить фокус и возвращать его, когда взаимодействие закончилось. Диалог, который открывается, не перемещая фокус, оставляет пользователя табулировать по странице за ним: это даже запутаннее, чем звучит.
dialog.showModal(); // перемещает фокус в диалог
closeButton.addEventListener("click", () => {
dialog.close();
trigger.focus(); // возвращает фокус туда, откуда он пришёл
});
Перехватывайте фокус только внутри модального окна и дайте выход
Пока модальный диалог открыт, Tab должен циклически проходить внутри него, а Escape — закрывать его. Везде в другом месте перехват фокуса — ошибка: он оставляет пользователя без выхода, а нативный элемент dialog с showModal() обрабатывает оба поведения — сильный довод в пользу его использования.
Дайте способ миновать повторяющуюся навигацию
Ссылка «Перейти к основному содержимому», видимая при фокусе, позволяет пользователю с клавиатуры миновать заголовок на каждой странице. Это один элемент, и он экономит несколько десятков переходов Tab при каждом визите.
Практика
Пройдите страницу по Tab и запишите каждую остановку
Откройте страницу, которую сопровождаете, и нажимайте Tab, пока не покинете страницу, отмечая каждый элемент и то, видно ли, где находится фокус.
Готово, когда: каждая остановка видима, ни один элемент управления не пропущен, а порядок совпадает с визуальным — и любое расхождение исправлено в разметке, а не через tabindex.
Соберите диалог, из которого можно выйти
Откройте диалог кнопкой, переместите в него фокус, пройдите Tab по кругу внутри него, закройте его клавишей Escape и кнопкой закрытия.
Готово, когда: при открытии фокус находится внутри диалога, не может его покинуть, пока диалог открыт, возвращается к кнопке-триггеру при закрытии, а Escape закрывает диалог из любого положения.
Частые ошибки
- Удаление обводки фокуса — пользователи с клавиатуры теряют всякое чувство положения; исправить: переоформите
:focus-visibleвместо того, чтобы его удалять. - Положительный
tabindex— элемент прыгает в начало порядка, и всё после него сдвигается; исправить: держитеtabindexравным0или не задавайте его вовсе, а порядок правьте в разметке. - Открытие диалога без переноса фокуса — диалог выглядит модальным, но клавиатура остаётся за ним; исправить: переносите фокус при открытии и возвращайте его при закрытии.
- Обработчик клика на неинтерактивном элементе — действие недостижимо без мыши; исправить: используйте
buttonили ссылку, чтобы элемент участвовал в порядке табуляции. :hoverкак единственный способ раскрытия — меню и подсказки, которым нужен указатель, никогда не открываются для пользователя с клавиатуры; исправить: открывайте их и по фокусу, а содержимое делайте закрываемым по Escape.
Что читать дальше
- Семантика и ARIA (02-frontend/11-accessibility-wcag-a11y/01-semantics-and-aria) — роли и имена решают, что услышит пользователь с клавиатуры, когда фокус попадёт на элемент.
- Инструменты разработчика в браузере (02-frontend/27-browser-devtools) — прогон с клавиатуры при открытом инспекторе доступности показывает ровно то, что объявляется.
- Юзабилити-тестирование (03-design-ux/33-usability-testing) — наблюдение за одним пользователем с клавиатурой за минуты находит барьеры.