ФАЗА 40. КАЧЕСТВО FRONTEND Доступность

Обязательно

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

HTML · ARIA · Contrast

Доступность начинается с правильных HTML-элементов и полного управления клавиатурой, ARIA дополняет их только при необходимости.

1. Семантический элемент несёт встроенное поведение

1 / 5

button, a, input и heading сообщают роль и доступное имя, поддерживают клавиатуру и не требуют имитации через div.

  • Ссылка ведёт на ресурс.
  • Кнопка выполняет действие.
  • Заголовки образуют структуру.
button и adiv с onclick

2. Label и структура объясняют контент

2 / 5

Связанный label называет поле, fieldset группирует, landmarks разделяют области страницы для навигации assistive tech.

  • Placeholder не заменяет label.
  • Ошибка связывается с полем.
  • Порядок заголовков логичен.
LandmarksЗаголовки и группыLabel и описание

3. Фокус виден и движется предсказуемо

3 / 5

Tab проходит интерактивные элементы, Enter и Space активируют их по роли, dialog получает и возвращает фокус.

  • Outline не удаляется без замены.
  • Tabindex положительный не нужен.
  • Ловушка фокуса ограничена модалью.
ФокусДействие клавиатуройВозврат фокуса

4. ARIA добавляет то, чего нет в HTML

4 / 5

Role, state и properties уточняют нестандартный виджет, но не исправляют отсутствие клавиатуры и поведения автоматически.

  • Нативный элемент предпочтителен.
  • aria-expanded отражает состояние.
  • aria-live объявляет изменение.
Доступный виджетRoleStateKeyboard

5. Проверка сочетает автоматику и ручной сценарий

5 / 5

Axe находит часть нарушений, клавиатура и скринридер проверяют реальный путь, контраст оценивается для текста и состояний.

  • Цвет не единственный сигнал.
  • Zoom не ломает контент.
  • Автоматика не доказывает доступность.
AxeКлавиатура и читалкаИсправление и повтор

Доступность (a11y)

← назад к разделу

Представьте, что вся навигация на вашем сайте — это <div onClick>. Для человека, который видит экран и держит мышь, всё работает. Но человек, который управляет компьютером с клавиатуры или слышит страницу через скринридер, попадает в тупик: кнопки не реагируют на Enter, у элементов нет имён, порядок фокуса хаотичен.

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

Проблема: div вместо правильных элементов

Когда разработчики вешают обработчики кликов на div или span, браузер не знает, что это интерактивный элемент. Браузер не добавит его в порядок обхода Tab, не сообщит скринридеру, что элемент — кнопка, не обработает нажатие Enter.

<!-- плохо: div ничего не сообщает -->
<div onclick="save()">Сохранить</div>

<!-- хорошо: браузер уже знает всё об этой кнопке -->
<button onclick="save()">Сохранить</button>

<button> получает фокус по Tab, срабатывает по Enter и Space, а скринридер объявляет его роль — «кнопка». Всё это бесплатно, без строчки JavaScript.

Семантическая разметка

Девяносто процентов доступности — это просто правильные HTML-элементы. Браузер знает их смысл и сам выполняет нужное поведение:

  • <nav> — навигация: скринридер позволяет перейти сразу к ней.
  • <main> — главное содержимое страницы.
  • <button> — интерактивная кнопка, фокусируется и срабатывает с клавиатуры.
  • <a href> — ссылка, открывается по Enter.
  • <label> — подпись к полю ввода; при клике по ней фокус переходит в поле.
  • <h1>–<h6> — заголовки в иерархии; нарушать порядок (h1 → h3, пропустив h2) не стоит.
  • <img alt="..."> — у каждой картинки должен быть атрибут alt; если картинка декоративная — alt="".
<label for="email">Электронная почта</label>
<input id="email" type="email" />

Правило простое: сначала ищи подходящий HTML-элемент. Только если его нет — добавляй ARIA.

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

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

  • Фокус переходит по Tab в логичном порядке.
  • Кнопки и ссылки срабатывают по Enter.
  • Кнопки (не ссылки) срабатывают и по Space.
  • Видно, какой элемент сейчас в фокусе — это стиль :focus-visible.

Последний пункт часто ломают «ради красоты»: разработчик убирает рамку outline: none, и пользователь клавиатуры перестаёт понимать, где он находится. Это один из самых распространённых и легко исправимых дефектов.

Семантические элементы дают всё это бесплатно. Кастомные виджеты — выпадающие меню, модальные окна, карусели — приходится реализовывать вручную:

  • в открытом модальном окне фокус должен оставаться внутри (ловушка фокуса);
  • после закрытия фокус возвращается туда, откуда окно открылось;
  • нажатие Escape закрывает окно.

ARIA — только когда нет HTML-аналога

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

<button> лучше, чем <div role="button" tabindex="0"> с ручным обработчиком клавиш. Второй вариант нужно поддерживать самостоятельно и легко сделать ошибку.

Неверный ARIA хуже, чем никакого: он вводит скринридер в заблуждение и создаёт путаницу. ARIA уместен там, где у HTML нет аналога:

<!-- Уведомление, которое появляется динамически -->
<div aria-live="polite" aria-atomic="true">
  Файл загружен успешно
</div>

<!-- Иконка-кнопка без видимого текста -->
<button aria-label="Закрыть">✕</button>

<!-- Аккордеон -->
<button aria-expanded="false" aria-controls="panel1">Раздел 1</button>
<div id="panel1" hidden>Содержимое</div>

aria-live — для областей, которые обновляются без перезагрузки страницы; скринридер прочитает изменение вслух. aria-label — когда у кнопки нет текста, только иконка. aria-expanded — чтобы сообщить состояние раскрытого/свёрнутого элемента.

Цвет и контраст

Информацию нельзя передавать только цветом — часть пользователей не различает оттенки. Если ошибка в форме выделена только красным, добавьте иконку или текст.

Текст должен иметь достаточный контраст с фоном. WCAG определяет минимальный порог: 4.5:1 для обычного текста и 3:1 для крупного (от 24px, или от 19px жирным). Проверить контраст можно в DevTools браузера или в специальных инструментах вроде Colour Contrast Analyser.

Как проверять доступность

Автоматически. Инструмент axe (браузерное расширение или плагин для тестов) ловит типичные ошибки: отсутствующий alt, плохой контраст, неверный ARIA, нарушения иерархии заголовков. Автоматика закрывает около 30–40% проблем.

В тестах. Testing Library ищет элементы так же, как это делает скринридер — по роли и доступному имени. Если тест написан через getByRole, он заодно проверяет доступность:

// Тест не найдёт кнопку, если у неё нет понятного имени
const button = screen.getByRole("button", { name: "Сохранить" });

Если элемент не находится по роли — скорее всего, он недоступен для вспомогательных технологий.

Руками. Отложите мышь и пройдите по странице клавишами. Включите скринридер (VoiceOver на macOS, NVDA или JAWS на Windows, TalkBack на Android) и послушайте, что он читает. Это быстро выявляет проблемы, которые автоматика не замечает.

правильный HTML-элемент всё работает с клавиатуры ARIA только без аналога контраст и не только цвет axe, затем руками

Порядок важен: большая часть проблем закрывается первым шагом, а ARIA идёт после того, как HTML исчерпан.

Дополнительно: при первом чтении можно пропустить

Глубже: модальное окно и dialogрасширенное

Всплывающие окна - главный источник недоступности: фокус остаётся позади оверлея, Esc не закрывает, скринридер не знает о диалоге. У браузера есть встроенное решение - элемент <dialog>: ref.current.showModal() открывает его как настоящее модальное окно (фон ::backdrop затемняет фон, Esc закрывает, фокус запирается внутри), а .close() возвращает фокус на кнопку, которая открыла. Семантика бесплатно: роль диалога, ловушка фокуса и закрытие по Escape - из коробки, без библиотек.

Коротко

  • Правильные HTML-элементы (<button>, <nav>, <label>) дают доступность бесплатно — не заменяйте их на <div>.
  • Заголовки идут по порядку, у картинок есть alt, у полей — <label>.
  • Всё, что работает мышью, работает и с клавиатуры: Tab, Enter, Space, Escape.
  • Видимый фокус (:focus-visible) не убирают — без него пользователь клавиатуры теряется.
  • ARIA добавляют только там, где у HTML нет подходящего элемента; неверный ARIA хуже, чем никакого.
  • aria-live — для динамических уведомлений; aria-label — для иконок без текста; aria-expanded — для раскрывающихся элементов.
  • Информацию нельзя передавать только цветом; контраст текста — не менее 4.5:1.
  • axe ловит ~30–40% проблем автоматически; остальное — проверка клавиатурой и скринридером.
  • Testing Library с getByRole проверяет доступность как побочный эффект обычных тестов.

Что почитать дальше