Обход страницы клавишей Tab
Нажмите Tab на веб-странице, и фокус перепрыгнет к следующей ссылке, кнопке или полю. Эта проверка выписывает весь маршрут для любого адреса и указывает на элементы управления, которые в него не попали.
Фокус: курсор клавиатуры
Мышью можно указать куда угодно. Клавиатура в каждый момент может «стоять» только на одном элементе, и это положение называется фокусом. Tab двигает его вперёд, Shift+Tab назад, Enter переходит по ссылке, пробел нажимает кнопку. Так перемещаются люди с тремором или ограниченной подвижностью рук. А также пользователи программ экранного доступа, люди с кнопочными устройствами или голосовым управлением и все, кто быстро заполняет формы.
Браузер определяет, что может получить фокус, только по HTML. Ссылки с href, кнопки, поля форм, <summary>, медиаплееры с элементами управления и фреймы входят в маршрут по умолчанию, в том порядке, в каком они идут в исходном коде. Всё остальное пропускается, если атрибут tabindex не говорит иначе.
Три значения tabindex
| Значение | Действие | Когда применять |
|---|---|---|
tabindex="0" | Добавляет элемент в маршрут на его естественное место | Нестандартные элементы управления, которые не могут быть настоящей <button> |
tabindex="-1" | Убирает его из маршрута; скрипты всё ещё могут передать ему фокус | Неактивные вкладки, пункты меню, к которым переходят стрелками, слайды карусели за пределами экрана |
tabindex="1" и больше | Ставит его впереди всех остальных, начиная с наименьшего числа | Почти никогда. Фокус скачет по экрану |
Обход следует этим правилам буквально: сначала положительные значения по возрастанию, затем порядок исходного кода. Группа переключателей с одинаковым name считается одной остановкой, потому что внутри группы фокус перемещают стрелки. Отключённые элементы, закрытые <dialog>, области с inert или hidden и содержимое закрытых <details> не учитываются.
Что отмечает проверка
- Кликабельные элементы, недоступные с клавиатуры
<div onclick>или элемент сrole="button"безtabindex. Пользователь мыши может его нажать, а пользователь клавиатуры даже не может до него добраться. Замените его на<button type="button">: фокус, Enter и пробел достаются бесплатно.- Ссылки без
href - Для браузера
<a>без адреса это обычный текст, и Tab проходит мимо. - Элементы с
tabindex="-1" - Отмечаются, если только ссылка не дублирует другую доступную ссылку на тот же адрес и не входит в список вкладок, меню, панель инструментов или похожую группу, где управление берут на себя стрелки.
- Ссылка пропуска
- Проверка ищет среди первых трёх остановок ссылку на якорь на той же странице, например
<a href="#content">Skip to content</a>, и убеждается, что целевойidсуществует. Она также считает, сколько нажатий нужно от верха страницы до<main>. Если их пять или меньше, отсутствие ссылки пропуска отмечается лишь для сведения. - Контур фокуса
- Мы ищем в блоках
<style>страницы, встроенных стилях и максимум пяти таблицах стилей с того же сайта правила вродеa:focus { outline: none }или* { outline: 0 }. Если контур ничем не заменён в стилях:focus-visibleили:focus, находка красная: пользователи клавиатуры не видят, где находятся. - Другие остановки, на которые стоит взглянуть
- Фокусируемые элементы внутри
aria-hidden="true", остановки без доступного имени, фреймы безtitleи ссылки на#илиjavascript:, которые работают как кнопки.
Как вернуть видимый фокус
Дизайнеры часто убирают контур, потому что он появляется после клика мышью и кажется им некрасивым. Селектор :focus-visible закрывает этот спор. Он срабатывает, только когда браузер решает, что пользователю нужно видеть фокус, а на практике это означает работу с клавиатуры.
:focus-visible {
outline: 3px solid #1a5fb4;
outline-offset: 2px;
}WCAG 2.2 требует индикатор с контрастом не ниже 3:1 по отношению к окружающим цветам. Шаблон :focus:not(:focus-visible) { outline: none } безопасен, и о нём не сообщается.
Пределы статического чтения
Маршрут вычисляется по HTML, который получает наш сервер. Обработчики кликов, подключённые в JavaScript через addEventListener, не оставляют следов в разметке, поэтому <div>, подключённый таким образом, не обнаруживается. Меню, построенные скриптом, здесь тоже не видны, как и клавиатурные ловушки, когда фокус попадает в виджет и не может из него выйти. Таблицы стилей с другого домена или сверх пятой не читаются.
Закончите настоящей проверкой, она займёт две минуты. Отложите мышь, загрузите страницу и нажимайте Tab, пока не дойдёте до подвала. Вы всегда должны видеть, где находитесь, и должны суметь открыть каждое меню и отправить каждую форму.
Частые вопросы
Как самому проверить навигацию с клавиатуры?
Щёлкните в адресной строке, затем снова и снова нажимайте Tab. Shift+Tab возвращает назад, Enter работает на ссылках, пробел на кнопках и флажках, стрелки внутри групп переключателей и меню, а Escape закрывает диалоги. В Safari на macOS сначала включите в дополнительных настройках «Выделять каждый объект на веб-странице при нажатии Tab».
Достаточно ли tabindex="0" на div, чтобы сделать его кнопкой?
Нет. Элемент становится фокусируемым, но Enter и пробел ничего не делают, пока вы не добавите обработчики клавиш, а программа экранного доступа не назовёт его кнопкой без role="button". Настоящая <button> даёт всё три сразу.
Нужна ли ссылка пропуска, если на странице есть элемент main?
Да, для зрячих пользователей клавиатуры. Программы экранного доступа умеют переходить к ориентиру <main> сочетанием клавиш. У того, кто пользуется только клавишей Tab, такого сочетания нет, и без ссылки пропуска ему приходится проходить каждый пункт меню.
Почему порядок Tab не совпадает с тем, что я вижу на экране?
Tab следует порядку исходного HTML, а не визуальной раскладке. CSS вроде order, flex-direction: row-reverse, размещения в сетке или абсолютного позиционирования может переместить элементы на экране, не меняя их места в исходном коде. Положительный tabindex даёт тот же эффект.
Всегда ли outline: none нарушает доступность?
Только если ему нет замены. Правило, которое убирает контур и в том же блоке задаёт при фокусе видимую рамку, фон или тень, вполне допустимо, и эта проверка засчитывает его как замену.