Новое в журнале. Доступность веб-интерфейсов (a11y): чек-лист для разработчика
Разборы и проверки

Доступность веб-интерфейсов (a11y): чек-лист для разработчика

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

5 ноября 20269 мин чтенияПрактикам
Доступность веб-интерфейсов (a11y): чек-лист для разработчика
4.5:1минимальный контраст для обычного текста по WCAG
7 пунктовминимальный чек-лист, который проверяется руками за один проход страницы

Доступность (accessibility, сокращённо a11y — между «a» и «y» в слове ровно 11 букв) — это не чек-бокс «для людей с инвалидностью», а базовое качество интерфейса: он должен одинаково работать с мышью, клавиатурой, скринридером и при увеличении масштаба страницы. Пользователь с временно сломанной рукой, работающий в метро с одной рукой на поручне, или человек с плохим зрением на ярком солнце — все они оказываются в той же ситуации, что и пользователь скринридера: мышь недоступна или неудобна, а интерфейс либо справляется с этим, либо нет.

На практике задача распадается на две зоны ответственности. Разработчик проверяет код: семантическую разметку, ARIA-атрибуты там, где нативного HTML не хватает, порядок фокуса, контраст уже выбранных цветов. Но системные решения — палитра с достаточным контрастом, читаемые состояния hover/focus/disabled для каждого компонента, визуальная иерархия, которая работает и при увеличении шрифта, — закладываются на уровне дизайна интерфейса. Если его проектирует внешняя команда, доступность должна быть явным требованием технического задания, а не доработкой, которую вспоминают после первой жалобы пользователя.

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

Что такое доступность (a11y) и почему это не только про инвалидность

Стандарт, на который ссылаются почти все практические рекомендации по доступности, — WCAG (Web Content Accessibility Guidelines), разрабатываемый консорциумом W3C. Актуальная версия 2.2 описывает требования по четырём принципам: контент должен быть воспринимаемым, управляемым, понятным и устойчивым к разным технологиям (robust). Каждый принцип раскладывается на конкретные, формально проверяемые критерии успеха трёх уровней строгости — A, AA и AAA, где AA — практический минимум, на который ориентируется подавляющее большинство продуктовых команд.

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

WCAG и ARIA — что обязан знать разработчик

ARIA (Accessible Rich Internet Applications) — это набор HTML-атрибутов, которые добавляют браузеру и скринридеру информацию о роли, состоянии и свойствах элемента там, где этой информации не хватает в самой разметке. ARIA не меняет внешний вид и поведение элемента — она только описывает его для вспомогательных технологий.

Главное правило ARIA: не используйте её, если есть нативный HTML-элемент

Первое правило спецификации ARIA (так называемое No ARIA is better than Bad ARIA) звучит буквально так: если задачу решает нативный HTML-элемент или атрибут с уже встроенной семантикой и поведением, используйте его вместо того, чтобы пересоздавать то же самое через div и ARIA-атрибуты. Тег button уже доступен с клавиатуры, реагирует на Enter и пробел, сообщает скринридеру свою роль и состояние — всё это пришлось бы вручную добавлять через role="button", tabindex="0" и обработчики нажатия клавиш, если бы кликабельный элемент сделали на div.

<!-- Неправильно: нужно руками достраивать клавиатуру, фокус и роль -->
<div class="btn" role="button" tabindex="0" onclick="submit()">Отправить</div>

<!-- Правильно: всё перечисленное уже встроено в тег -->
<button class="btn" type="submit">Отправить</button>

Базовые атрибуты, которые реально нужны (aria-label, aria-expanded, aria-hidden, aria-live)

Четыре ARIA-атрибута закрывают большинство практических задач интерфейса:

  • aria-label — текстовая подпись для элемента без видимого текста, например кнопки-иконки без подписи («крестик» закрытия модального окна).
  • aria-expanded — сообщает скринридеру, раскрыт ли сейчас аккордеон, выпадающее меню или дропдаун (true/false).
  • aria-hidden="true" — скрывает декоративный элемент (иконку-дублёр текста, фоновую SVG) от скринридера, чтобы он не зачитывал визуальный шум.
  • aria-live="polite" — помечает область, где контент меняется динамически (тост-уведомление, счётчик ошибок формы), чтобы скринридер озвучил изменение без переключения фокуса пользователя.

Подробное описание всех ролей и атрибутов, включая менее частые случаи вроде составных виджетов (комбобокс, таб-панель, дерево), стоит смотреть в справочнике ARIA на MDN — там же разобраны готовые паттерны для типовых компонентов.

Чек-лист перед релизом: 7 пунктов, которые проверяются руками

Полный аудит доступности занимает дни, но большинство реальных блокеров ловится за один проход страницы перед мержем pull request. Ниже — минимальный набор, который укладывается в 10–15 минут ручной проверки.

Семантика и клавиатурный фокус

  1. Пройдите интерфейс только клавиатурой (Tab, Shift+Tab, Enter, Esc) — все интерактивные элементы должны быть достижимы и иметь видимое кольцо фокуса.
  2. Порядок перехода по Tab логичен и совпадает с визуальным порядком элементов на странице, а не разбросан по DOM-дереву.
  3. Заголовки идут по иерархии без пропуска уровней (после h1 — h2, а не сразу h4), и на странице ровно один h1.
Иконки фокуса, скринридера и переключателя контраста вокруг окна браузера как символ проверки доступности интерфейса

Контраст и типографика

  1. Контраст обычного текста к фону — не ниже 4.5:1, крупного текста и графических элементов интерфейса — не ниже 3:1 (проверяется за секунды инструментом вроде WebAIM Contrast Checker).
  2. Состояние фокуса видно без поддержки цвета — например, дополнительным контуром или подчёркиванием, а не только сменой оттенка.

Формы и сообщения об ошибках

  1. Каждое поле формы связано с подписью через label for или обёрнуто в label — клик по тексту подписи должен ставить фокус в поле.
  2. Сообщение об ошибке связано с полем через aria-describedby и не передаётся одним только цветом рамки — рядом есть текст с описанием проблемы.

Семь пунктов — не предел доступности, а порог «не стыдно выпускать в прод». Прошедшая этот чек-лист страница не гарантированно полностью доступна, но закрывает подавляющее большинство сценариев, которые реально блокируют пользователя.

Как автоматизировать часть проверок, не внедряя тяжёлый инструмент

Не весь чек-лист обязательно гонять руками при каждом pull request. Библиотека axe-core и её обёртки (в том числе встроенная в DevTools вкладка Lighthouse) ловят автоматически то, что формально проверяется по DOM: отсутствующий alt у изображения, незаполненный label, недостаточный контраст, дублирующиеся id, элементы с интерактивной ролью без клавиатурной доступности.

// пример точки интеграции axe-core в существующие e2e-тесты
import { injectAxe, checkA11y } from "axe-playwright";

test("форма регистрации доступна", async ({ page }) => {
  await page.goto("/signup");
  await injectAxe(page);
  await checkA11y(page, null, { detailedReport: true });
});

Такой тест встраивается в уже существующий набор e2e-тестов без отдельной инфраструктуры и падает в CI так же, как любой другой упавший сценарий — разработчик видит конкретный список нарушений с указанием элемента, а не абстрактное «что-то не так с доступностью». Автоматика закрывает примерно треть всех реальных проблем доступности; логику фокуса, порядок чтения экрана и осмысленность текста ошибок она не проверяет — это остаётся за ручным проходом по чек-листу выше.

Типичные анти-паттерны, которые незаметно ломают доступность

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

  • Клик только по иконке без текста и без aria-label — скринридер объявляет пустую кнопку без единого слова о её назначении.
  • Placeholder вместо label — текст-подсказка внутри поля исчезает, как только пользователь начинает печатать, а скринридер в части браузеров вообще не связывает его с полем как подпись.
  • Модальное окно без ловушки фокуса (focus trap) — Tab уводит фокус «за» открытое окно, на элементы страницы позади, которые визуально скрыты.
  • Цвет как единственный носитель смысла — красная рамка поля без текста ошибки рядом ничего не сообщает пользователю с дальтонизмом или скринридером.
  • div с onclick вместо кнопки или ссылки — элемент кликается мышью, но недостижим с клавиатуры и не объявляется скринридером как интерактивный.

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

Где заканчивается код и начинается работа дизайна интерфейса

Чек-лист из этой статьи закрывает то, что физически реализуется в разметке и коде компонента: семантику, ARIA, порядок фокуса, связку поля и ошибки. Но часть задач доступности в принципе не решается на уровне отдельного pull request, потому что это системные решения, принятые ещё до того, как разработчик открыл редактор.

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

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

Частые вопросы

Доступность сайта обязательна по закону в России?

Жёсткого закона по образцу американского ADA или европейского EAA для обычных коммерческих сайтов в России нет. Обязательные требования доступности распространяются в первую очередь на государственные и муниципальные сайты. Но для бизнеса это не повод игнорировать a11y: часть аудитории реально пользуется клавиатурой и скринридерами, и доступный интерфейс расширяет аудиторию независимо от юридических обязательств.

Чем ARIA отличается от обычного семантического HTML и когда её вообще нужно добавлять?

Семантический HTML (button, nav, header, label) уже сообщает браузеру и скринридеру роль и состояние элемента — это первый и бесплатный уровень доступности. ARIA — это атрибуты, которые добавляют недостающую семантику туда, где нативного HTML-элемента с нужным поведением просто не существует: кастомный таб-переключатель, модальное окно, живое уведомление. Если задачу решает тег button или label, добавлять поверх него ARIA не нужно и зачастую вредно.

Какой минимальный контраст текста считается доступным по WCAG?

Для обычного текста уровень AA требует соотношение контраста не менее 4.5:1 к фону, для крупного текста (от 18pt или 14pt жирным) — не менее 3:1. Те же 3:1 действуют для графических элементов интерфейса и состояний фокуса. Проверить конкретную пару цветов можно инструментом вроде WebAIM Contrast Checker за несколько секунд, не дожидаясь ручного тестирования.

Можно ли проверить доступность автоматически, без ручного тестирования скринридером?

Частично да. Автоматические инструменты вроде axe-core или Lighthouse надёжно ловят формальные нарушения: отсутствующий alt, низкий контраст, незаполненный label, дублирующиеся id. По разным оценкам это покрывает около трети всех проблем доступности — остальное (логичен ли порядок фокуса, понятен ли текст сообщения об ошибке на слух) требует ручной проверки клавиатурой и хотя бы базового прогона скринридером.

С чего начать, если у большого существующего сайта с доступностью вообще никак?

Не с полного аудита всего сайта разом — это быстро демотивирует и редко доводится до конца. Начните с прогона автоматического сканера по ключевым пользовательским путям (главная, форма регистрации, оформление заказа) и зафиксируйте список находок по приоритету. Дальше чините в порядке влияния на пользователя: сначала то, что полностью блокирует задачу (недоступная кнопка отправки формы), а не то, что просто формально нарушает правило.

Ломает ли доступность производительность или скорость загрузки страницы?

Нет, и это частый миф. ARIA-атрибуты — это текстовые атрибуты HTML, которые практически не влияют на вес страницы и время рендера. Семантическая вёрстка (nav, main, button вместо div с обработчиком клика) в среднем даже компактнее и предсказуемее для браузера, чем эквивалентная конструкция на div. Единственное исключение — некорректно настроенный aria-live, который может провоцировать лишние перерасчёты у скринридера, но это ошибка реализации, а не свойство доступности как таковой.

Нужен ли в команде отдельный специалист по доступности, или это зона фронтенд-разработчика?

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

Пять категорий принципов и критериев успеха, упомянутые выше, — пересказ действующего стандарта WCAG 2.2 консорциума W3C; там же разобраны все уровни A/AA/AAA по каждому критерию отдельно.

Код, который не стыдно передать

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

Случайный разбор