Accessibility для разработчика: WCAG, ARIA, контраст

UI/UX5 мин чтения
  • #accessibility
  • #a11y
  • #wcag
  • #aria
  • #frontend

Доступность (accessibility, a11y) часто воспринимают как «заботу о малом меньшинстве», и это неточно. По данным ВОЗ, около 16% населения мира имеют ту или иную форму инвалидности — это сотни миллионов потенциальных пользователей. Кроме того, доступность помогает и всем остальным: людям с временными травмами (сломана рука), тем, кто на ярком солнце не видит экран, в шумной среде без звука. А в ЕС с июня 2025 года действует European Accessibility Act, который обязывает ряд продуктов быть доступными по закону — это уже не «nice to have», а юридическое требование. Разберём, что реально делать разработчику.

WCAG — ориентир

WCAG (Web Content Accessibility Guidelines) — международный стандарт от W3C. У него три уровня соответствия: A (минимум), AA (промежуточный), AAA (максимальный). Для большинства коммерческих сайтов ориентир — AA: он достижим без радикальных переделок и закрывает основные нужды. Законы (ADA в США, EAA в ЕС, 508 в госсекторе) обычно ссылаются именно на WCAG AA.

WCAG построен вокруг четырёх принципов (POUR): контент должен быть Perceivable (воспринимаемым), Operable (управляемым), Understandable (понятным) и Robust (надёжно работающим с ассистивными технологиями).

Семантический HTML решает 70% проблем

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

Замените <div onclick> на настоящую кнопку:

<!-- Плохо: div-кнопка. Screen reader скажет «текст», не «кнопка». -->
<div onclick="submit()">Отправить</div>

<!-- Хорошо: нативная кнопка. Screen reader скажет «кнопка, Отправить».
     По умолчанию в порядке таба, реагирует на Enter/Space, видна фокусом. -->
<button type="button" onclick="submit()">Отправить</button>

Связывайте <label> с инпутом:

<label for="email">Email</label>
<input id="email" type="email" name="email">
<!-- Теперь screen reader при фокусе на инпуте скажет «Email», и клик по label ставит фокус в инпут -->

Используйте заголовки по иерархии (<h1><h2><h3>), без перескоков уровней. Screen reader-пользователи часто навигируют по заголовкам (как оглавлению), прыгая между ними. Если заголовки прыгают или используются ради размера шрифта (<h3> для крупного текста не по иерархии) — навигация ломается.

И семантические ориентиры: <nav> (навигация), <main> (основной контент), <aside> (дополнительно), <footer>. Это позволяет screen reader быстро перескакивать к началу статьи, минуя навигацию и шапку.

ARIA — там, где семантики не хватает

ARIA (Accessible Rich Internet Applications) — набор атрибутов, добавляющих семантику элементам, для которых нет подходящего HTML-тега. Кастомные дропдауны, модалки, табы, live-регионы.

Примеры:

  • aria-label — подпись для элемента без видимого текста (иконка-кнопка): <button aria-label="Закрыть">×</button>.
  • aria-expanded="true/false" — состояние раскрывающегося элемента (раскрыт/свёрнут accordion).
  • aria-live="polite" / "assertive" — регион, чьи изменения озвучиваются без перезагрузки (например, счётчик новых сообщений).
  • role="dialog", role="alert", role="tablist" — семантическая роль, когда нативного тега нет.

Первое правило ARIA: «не использовать ARIA», если есть нативный HTML-элемент. Нативный <button> бесплатен: он по умолчанию кнопка, в порядке таба, реагирует на клавиатуру. ARIA на <div> (<div role="button" tabindex="0">) — это попытка пересоздать кнопку вручную, и обычно что-то забывают (фокус, реакцию на Space и Enter). Сначала ищите нативный тег, потом — ARIA.

Клавиатурная навигация

Многие пользователи не пользуются мышью: слепые (только клавиатура + screen reader), люди с моторными нарушениями. Всё, что делается мышью, должно быть достижимо с клавиатуры:

  • Tab перемещает между интерактивными элементами в логическом порядке.
  • Shift+Tab — назад.
  • Enter/Space активируют кнопки/ссылки.
  • Стрелки работают в сложных виджетах (radio-группы, табы, combobox).

Главное правило: не убирайте focus-ring без замены. outline: none без альтернативы — типичный грех: пользователь с клавиатурой не видит, где находится фокус. Если не нравится дефолтный синий outline, замените на стилизованный :focus-visible, но не убирайте совсем.

/* Плохо */
button { outline: none; }

/* Хорошо — стилизованный фокус только при клавиатурной навигации */
button:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

Контраст

Текст должен быть читаемым. WCAG AA требует контраста минимум 4.5:1 для обычного текста и 3:1 для крупного (18pt+ или 14pt bold). Проверяйте через WebAIM Contrast Checker или инструменты в DevTools.

Серый текст на белом часто проваливает порог: #999 на белом — контраст 2.85, не проходит AA. Дизайнеры любят «приглушённые» цвета, но это часто убивает читаемость для людей с нарушениями зрения и при ярком освещении.

Конкретные грабли

display: none и visibility: hidden. Оба скрывают элемент и от screen reader. Если визуально спрятали, но хотите, чтобы скринридер прочитал — используйте приём «visually hidden»: позиционирование за пределами экрана или clip, но не display: none.

tabindex больше 0. tabindex="5" ломает естественный порядок таба — фокус будет прыгать не по DOM-порядку. Используйте tabindex="0" (добавить в порядок) или tabindex="-1" (можно программно поставить фокус, но не через Tab). Положительные tabindex почти всегда ошибка.

Картинки без alt. Информативные картинки должны иметь осмысленный alt. Декоративные — alt="" (пустой), чтобы скринридер их пропустил, а не зачитывал имя файла.

Только цвет для различения статусов. «Красная точка — ошибка, зелёная — ок» — дальтоники (около 8% мужчин) не различат. Добавьте иконку, текст или подчёркивание. У ссылки должен быть underline, не только цвет (иначе отличить от обычного текста нельзя).

Отсутствие <title> у страниц и у <iframe>. Title — первое, что озвучивает скринридер при загрузке. На SPA с маршрутами обновляйте document.title при навигации.

Инструменты проверки

  • Axe DevTools (браузерное расширение) — автоматический аудит страницы.
  • Lighthouse (встроен в Chrome DevTools) — включает секцию Accessibility.
  • Ручная проверка клавиатурой — отключите мышь и попробуйте пройти по основному сценарию только на Tab/Enter.
  • Screen reader — NVDA (Windows, бесплатно), VoiceOver (встроен в macOS/iOS), попробуйте попользоваться своим продуктом вслепую. Это самое отрезвляющее упражнение.

Автоматика ловит ~30–40% проблем. Остальное — ручная проверка клавиатурой и screen reader. Не надейтесь на инструмент, который «решит a11y».

Короткое summary

Доступность — это про сотни миллионов пользователей с инвалидностью плюс временные ограничения (травмы, солнце, шум) плюс юридическое требование в ЕС/США. Ориентир — WCAG AA. Семантический HTML (<button>, <label>, иерархия заголовков, <nav>/<main>) решает 70% проблем бесплатно. ARIA добавляет семантику кастомным виджетам, но первое правило ARIA — не использовать его, если есть нативный тег. Клавиатурная навигация: всё доступно через Tab, не убирайте focus-ring без замены. Контраст 4.5:1 для текста. Грабли: display:none прячет от скринридера, tabindex > 0 ломает порядок, картинки без alt, только цвет для статусов. Автоматика (axe, Lighthouse) ловит треть проблем, остальное — ручная проверка клавиатурой и screen reader.

Что почитать