С чего начать проверку

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

Аудит доступности — не один прогон анализатора, а три независимых прохода: клавиатурой, автоматикой и скринридером. Каждый из них видит то, чего не видят два других.

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

Шаг 1. Пройти сайт только клавиатурой

Пятнадцать минут, ноль установок, самая высокая отдача из трёх шагов. Уберите руку с мыши — буквально, положите её на колено — и попробуйте выполнить главный сценарий сайта: найти товар, положить в корзину, оформить.

КлавишаЧто делает
Tab / Shift+Tabвперёд и назад по интерактивным элементам
Enterссылка, кнопка, отправка формы
Пробелкнопка, чекбокс, прокрутка страницы
Стрелкирадиокнопки, селекты, вкладки, меню
Escзакрыть модалку, выпадающий список

За чем следить:

  • Фокус видно? Если после нажатия Tab вы не понимаете, где находитесь, — это провал критерия 2.4.7. Обычно виноват один и тот же кусок CSS, который кто-то когда-то скопировал из «сброса стилей».
  • Порядок логичен? Фокус должен идти сверху вниз и слева направо, а не прыгать из подвала в шапку. Прыжки — почти всегда следствие position: absolute или order во flex, которые визуально переставили элементы, не тронув DOM.
  • Нет ловушек? Из модального окна и виджета чата фокус должен уметь выйти по Tab или Esc. Классическая ловушка: открыли модалку, а Tab уводит фокус на страницу под ней — вы «печатаете» вслепую в невидимую форму.
  • Достижимо всё? Карусели, кастомные селекты, «три точки» с меню, крестики закрытия — самые частые места, куда с клавиатуры просто не попасть.
  • Есть ли skip link? Ссылка «Перейти к содержимому» экономит клавиатурному пользователю тридцать нажатий Tab сквозь меню на каждой странице.
<body>
  <a class="skip-link" href="#main">Перейти к содержимому</a>
  <header>…навигация…</header>
  <main id="main" tabindex="-1">…контент…</main>
</body>
/* Ссылка спрятана, пока на неё не пришёл фокус */
.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  left: 8px;
  top: 8px;
  z-index: 100;
  padding: 8px 16px;
  background: #ffffff;
}

/* Так делать нельзя: убивает фокус для всех */
*:focus { outline: none; }

/* Так правильно: заметная рамка тем, кто пришёл с клавиатуры */
:focus-visible {
  outline: 3px solid #0b57d0;
  outline-offset: 2px;
}

Два бытовых нюанса. На macOS в Safari и Firefox обход по Tab всех элементов надо сначала включить (Системные настройки → Клавиатура → «Навигация с клавиатуры»), иначе фокус будет прыгать только по полям ввода, и вы решите, что сайт сломан. А чтобы видеть, куда фокус уходит на самом деле, удобно бросить в консоль браузера три строки:

// Вставьте в консоль DevTools и нажимайте Tab:
// в консоль будет печататься элемент, на котором сейчас фокус
document.addEventListener('focusin', () => {
  console.log(document.activeElement);
});

Это спасает, когда фокус улетает в скрытый элемент: визуально «ничего не подсвечено», а в консоли видно, что фокус сидит в закрытом меню.

Шаг 2. Прогнать автоматический анализатор

Теперь самое дешёвое. Два инструмента покрывают почти все потребности:

  • axe DevTools — расширение для Chrome/Firefox от Deque, отраслевой стандарт. Даёт список нарушений с номером критерия WCAG, подсветкой элемента и объяснением, как чинить.
  • Lighthouse — уже встроен в Chrome DevTools, вкладка Lighthouse, категория Accessibility. Под капотом использует тот же axe-core, но проверяет меньше правил.
  • WAVE — расширение, которое рисует иконки проблем прямо поверх страницы. Удобно показывать дизайнеру и менеджеру: видно, где болит.

Для регулярных прогонов и CI есть консольные версии:

# Lighthouse только по разделу «Доступность», с отчётом в браузере
npx lighthouse https://example.com --only-categories=accessibility --view

# axe-core по странице, с фильтром по нужным уровням WCAG
npx @axe-core/cli https://example.com --tags wcag2a,wcag2aa

# pa11y — удобен для списка страниц и порога «сколько ошибок допустимо»
npx pa11y https://example.com --standard WCAG2AA

Автоматика отлично ловит то, что выражается числом или наличием атрибута: контраст, отсутствующий alt, поле без <label>, отсутствующий lang, дублирующиеся id, несуществующие ARIA-атрибуты, пропуски в иерархии заголовков. Это буквально топ причин из отчёта WebAIM Million — то есть за десять минут вы снимаете самый массовый пласт проблем. Начинать надо именно с них.

Шаг 3. Включить скринридер

Самый пугающий шаг — и самый отрезвляющий. Ставить ничего не нужно:

ПлатформаСкринридерКак включить
macOSVoiceOverCmd + F5
WindowsNVDA (бесплатный, самый популярный) или встроенный «Экранный диктор»скачать nvaccess.org / Win + Ctrl + Enter
iOS / AndroidVoiceOver / TalkBackв настройках специальных возможностей

Первые пять минут будет громко и непонятно — это нормально, регулятор скорости речи существует. Минимальная программа: закройте глаза (или выключите монитор) и пройдите ту же задачу, что и в шаге 1. Слушайте не «красиво ли», а три вещи:

  • Есть ли у элемента имя. Если вместо «Закрыть, кнопка» звучит просто «кнопка» — у неё нет доступного имени: иконка без aria-label.
  • Верна ли роль. Если «В корзину» озвучено как обычный текст, а не как «кнопка», значит перед вами тот самый <div> из первого урока.
  • Объявляются ли изменения. Нажали «Добавить в корзину» — счётчик изменился визуально, а скринридер промолчал. Пользователь не знает, сработало ли. Это критерий 4.1.3, лечится живой областью:
<!-- Текст, попавший сюда, будет озвучен без перевода фокуса -->
<div role="status" aria-live="polite">Товар добавлен в корзину</div>

<!-- Иконочная кнопка без текста обязана иметь доступное имя -->
<button type="button" aria-label="Закрыть диалог">
  <svg aria-hidden="true" focusable="false">…</svg>
</button>

Полезно освоить ровно одну «продвинутую» команду — список заголовков или ссылок (в NVDA это Insert + F7, в VoiceOver — ротор по Ctrl + Option + U). Именно так реальные пользователи и навигируют: не слушают страницу подряд, а прыгают по структуре. Если в вашем списке заголовков сплошные «Заголовок 3» и ни одного осмысленного — навигация по сайту невозможна, хотя автопроверка была зелёной.

Как это работает: почему автоматика ловит меньше половины

Оценки расходятся (от трети до примерно половины проблем в зависимости от методики), но вывод у всех один: анализатор не заменяет человека. Причина не в качестве инструментов, а в природе критериев. Их можно разделить на два класса:

  • Машинно-проверяемые: есть ли атрибут, каково число. «Контраст 3.1:1 меньше 4.5:1» — тут машина безупречна и не устаёт.
  • Требующие понимания смысла: «текстовая альтернатива описывает изображение», «порядок фокуса логичен», «назначение ссылки понятно». Машина проверяет наличие, человек — осмысленность.

Отсюда фундаментальный разрыв. Атрибут alt="IMG_20240517.jpg" формально удовлетворяет критерию 1.1.1: альтернатива есть. Двадцать ссылок «Подробнее» подряд проходят автопроверку, а в списке ссылок скринридера превращаются в двадцать одинаковых строк. Модалка с идеальным контрастом и всеми aria-label может намертво держать фокус — axe об этом не узнает, потому что не нажимает Tab.

ПроблемаАвтопроверкаКлавиатураСкринридер
Контраст текста 3:1данетнет
Картинка без altданетда
alt="photo1.png"нетнетда
Ловушка фокуса в модалкенетдада
Нелогичный порядок Tabнетдачастично
Кнопка-div без роличастичнодада
Добавление в корзину не объявленонетнетда

Посмотрите на колонки: ни одна не покрывает таблицу целиком. Поэтому три шага — не «уровни зрелости», а три разных сенсора, и нужны все три.

Частые ошибки

  • Гнаться за «100» в Lighthouse. Сотня означает «не найдено автоматически обнаружимых нарушений на этой странице», а не «сайт доступен». Страница, состоящая из одних <div> без единого интерактивного элемента, легко берёт 100 баллов.
  • Проверять только главную. Барьеры живут в формах, корзине, личном кабинете и модалках — там, где логика, а не в баннере с котом.
  • Чинить симптом, а не причину. Увидели «кнопка без имени» — не спешите вешать aria-label на <div>. Сначала спросите, почему это не <button>.
  • Проверять скринридером «как удобно вам». Вы читаете страницу подряд, реальный пользователь — прыгает по заголовкам и ссылкам. Проверяйте так, как ходят они.
  • Ставить оверлей вместо исправлений. Виджет, который «включает доступность одной строчкой скрипта», не превращает <div> в кнопку и не пишет за вас осмысленный alt.

Итоги

Программа первых тридцати минут на любом проекте:

[ ] 10 мин — пройти главный сценарий только клавиатурой:
            фокус видно / порядок логичен / Esc закрывает / ловушек нет
[ ] 10 мин — прогнать axe DevTools или Lighthouse на 3–5 ключевых страницах,
            выписать нарушения контраста, alt, label, lang
[ ] 10 мин — включить VoiceOver или NVDA, послушать одну форму:
            у всех ли кнопок есть имя, объявляются ли ошибки и успех
  • Клавиатура — самый честный и дешёвый тест, начинайте с него.
  • Автопроверка (axe, Lighthouse, WAVE) снимает массовый пласт: контраст, alt, label, lang — но это лишь часть проблем.
  • Скринридер показывает то, что не видит ни один анализатор: бессмысленные имена, немые кнопки, необъявленные изменения.
  • Автоматика не заменяет человека, потому что половина критериев требует понимания смысла, а не проверки атрибута.
  • Зелёный отчёт — это начало разговора о доступности, а не его конец.
Проверьте себя
1. Lighthouse показал 100 баллов в разделе Accessibility. Что это означает?
AСайт полностью соответствует WCAG на уровне AA
BНа этой странице не найдено автоматически обнаружимых нарушений — но проблемы с фокусом, смыслом alt и объявлением изменений могли остаться
CСайт соответствует уровню AAA
DПроверять клавиатурой и скринридером уже не нужно
2. Почему alt="IMG_20240517.jpg" проходит автоматическую проверку, но остаётся проблемой?
AАнализатор не умеет читать атрибут alt у картинок
BТакой alt слишком длинный, и скринридер его обрезает
CМашина проверяет наличие текстовой альтернативы, а её осмысленность может оценить только человек
DПроблемы нет: имя файла — валидная альтернатива по WCAG