Урок по треку HTML и CSS 7 мин

Доступность (a11y) за 7 минут

Клавиатура, фокус, ARIA, альтернативный текст, контраст и скринридеры.

Кому это нужно

Доступность (a11y, 11 букв между a и y) значит, что интерфейсом могут пользоваться люди со скринридером, только с клавиатуры, с плохим зрением, с тремором, на маленьком экране под солнцем. Стандарт, на который ссылаются, это WCAG (сейчас актуальна версия 2.2), уровни A, AA, AAA. Обычно цель AA.

Четыре принципа WCAG: контент должен быть воспринимаемым, управляемым, понятным и надёжным (POUR).

Нативные элементы прежде всего

Лучший ARIA это его отсутствие. <button>, <a href>, <input>, <select>, <details>, <dialog> уже умеют фокус, клавиатуру и сообщают роль. Первое правило ARIA: если есть нативный элемент с нужным поведением, используй его.

Сравни:

<!-- плохо: нет фокуса, нет Enter/Space, роль "текст" -->
<div class="btn" onclick="save()">Сохранить</div>

<!-- хорошо -->
<button type="button" onclick="save()">Сохранить</button>

Чтобы div стал доступной кнопкой, нужно добавить role="button", tabindex="0" и обработку Enter и Space. Проще взять <button>.

Клавиатура и фокус

  • Всё интерактивное достижимо Tab и работает с клавиатуры.
  • Порядок фокуса совпадает с порядком в DOM. Поэтому визуальная перестановка через order или grid-area может запутать.
  • tabindex="0" добавляет элемент в естественный порядок, tabindex="-1" делает фокусируемым только программно (el.focus()), положительные значения ломают порядок, их избегают.
  • Не убирай обводку outline: none без замены. Используй :focus-visible: стиль показывается при навигации с клавиатуры и не мешает при клике мышью.
button:focus-visible {
  outline: 3px solid #2563eb;
  outline-offset: 2px;
}
  • Модалка: при открытии фокус переходит внутрь, Tab не уходит за её пределы, Escape закрывает, после закрытия фокус возвращается на кнопку-открывашку. Нативный <dialog> с showModal() делает инертным фон и закрывается по Escape.
  • Ссылка «Перейти к содержимому» в начале страницы помогает пропустить меню.

ARIA: роли, состояния, имена

ARIA меняет только то, что сообщается вспомогательным технологиям. Поведение и стили не появляются.

  • Роль: role="dialog", role="tab", role="alert".
  • Состояние: aria-expanded="true", aria-pressed, aria-selected, aria-checked, aria-current="page".
  • Доступное имя: текст внутри, <label>, aria-label="Закрыть", aria-labelledby="id". У кнопки-иконки без текста имя задают через aria-label.
  • Описание: aria-describedby указывает на подсказку или ошибку поля.
  • aria-hidden="true" прячет элемент от скринридера, но не от мыши и Tab. Нельзя ставить на фокусируемые элементы.

Живые области: aria-live="polite" зачитывает изменения, когда скринридер освободится, role="alert" (по сути assertive) прерывает сразу. Так сообщают «Товар добавлен в корзину» или ошибку отправки. Область должна быть в DOM до изменения текста.

Как скрыть

СпособВидно глазамиВидно скринридеру
display: none, hiddenнетнет
visibility: hiddenнетнет
aria-hidden="true"данет
класс .visually-hiddenнетда
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

Зрение и восприятие

  • Контраст текста по WCAG AA: не меньше 4.5:1 для обычного текста и 3:1 для крупного (от 24px или от 18.66px жирным). Для иконок и границ полей 3:1.
  • Цвет не единственный носитель смысла: ошибку поля подкрепляют текстом или иконкой.
  • Масштаб до 200% без потери контента; размеры шрифта в rem, чтобы уважать настройки пользователя.
  • prefers-reduced-motion: уменьшать анимации для тех, кто попросил.
  • Альтернативный текст у картинок, субтитры у видео.

Формы

  • У каждого поля видимый <label>.
  • Ошибка связана с полем: aria-invalid="true" и aria-describedby на текст ошибки.
  • Обязательность: атрибут required (скринридер скажет «обязательное»).
  • autocomplete помогает заполнять поля людям с моторными трудностями.

Как проверять

Пройти страницу только клавиатурой. Включить скринридер (VoiceOver на macOS, NVDA на Windows). Lighthouse и axe находят часть проблем автоматически, примерно треть: контраст, alt, подписи. Логику фокуса и смысл текстов проверяет человек.

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

  • outline: none без замены.
  • Иконки-кнопки без доступного имени.
  • aria-label на div без роли: у обычного div имя игнорируется.
  • Плейсхолдер вместо подписи.
  • Модалка, из которой фокус убегает на фон.

Что спросят на собесе

  • Что такое доступность и как её проверить.
  • Почему <button> лучше <div onclick>.
  • Чем aria-hidden отличается от display: none и как скрыть текст только визуально.
  • Как сделать доступную модалку и где должен быть фокус.
  • Что такое aria-live и когда он нужен.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.