Перейти к основному содержимому

Accessibility (a11y)

Цели

  • Делать интерфейс доступным для пользователей с клавиатурой, скринридерами, слабым зрением и др.
  • Избежать регрессий: правила должны быть проверяемыми (eslint, тесты, Storybook checks).
  • Сократить "a11y-долг" за счёт стандартных паттернов для форм, модалок, меню и таблиц.

Обязательные принципы

MUST

  • Используем семантический HTML по умолчанию (button, a, label, input, nav, main, header, footer).
  • Любой интерактивный элемент доступен с клавиатуры (Tab/Shift+Tab, Enter/Space).
  • Фокус видимый (не отключаем outline без замены).

SHOULD

  • Минимизировать ARIA: сначала семантика, затем ARIA.
  • Делать дизайн устойчивым к увеличению шрифтов (zoom 200%+).

Семантика и интерактивность

MUST

  • <button> - если действие происходит на текущей странице (submit, open modal, toggle).
  • <a href> - если это навигация/переход на URL.
✅ Хорошо
<button type="button" onClick={openModal}>Edit</button>
<a href="/settings">Go to settings</a>

❌ Плохо
<div onClick={openModal}>Edit</div>
<span role="button" onClick={openModal}>Edit</span>

Интерактивные элементы без семантики

MUST

  • Не делать кликабельные div/span. Если очень нужно - обязателен полный набор:
    • role
    • tabIndex=0
    • обработка клавиш Enter/Space
    • доступное имя
    • правильные состояния (disabled и т.д.)

В большом проекте правило простое: не делаем. Используем button/link или готовый компонент.


Иерархия заголвков (Heading hierarchy) (H1–H6)

Описание

Заголовки формируют "каркас" страницы для скринридеров и навигации с клавиатуры. Правильная структура помогает быстро понять, что на странице и перейти к нужному разделу.

MUST

  • Страница обязана иметь логическую иерархию заголовков: h1 -> h2 -> h3 -> ...
  • На странице должен быть ровно один h1 (основной заголовок страницы).

FORBIDDEN

  • Пропускать уровни заголовков (например: h1, затем h3).
  • Использовать заголовки только ради стилей.

Примеры

1. ✅ Хорошо
<h1>Profile</h1>

<section>
<h2>Personal information</h2>
<h3>Contact details</h3>
</section>

<section>
<h2>Security</h2>
<h3>Password</h3>
</section>


2. ❌ Плохо (пропуск уровня)
<h1>Profile</h1>
<h3>Personal information</h3>


3. ❌ Плохо (заголовок ради стиля)
<h2 class="text-xl">Buy now</h2>
<!-- это кнопка/CTA, а не заголовок секции -->


4. ✅ Правильно (стиль без заголовка)
<p class="text-xl font-semibold">Buy now</p>

Проверка

  • В DevTools включить отображение структуры заголовков (outline / headings).
  • Можно использовать screen reader (например, NVDA) и пройтись по списку заголовков, проверив порядок и смысл (если необходимо).

Accessible name (имя элемента)

MUST

  • Каждый input/select/textarea должен иметь label:
    • label htmlFor="id"> + id на input
    • либо aria-label
    • либо aria-labelledby
✅ Хорошо
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" />

✅ Допустимо (когда визуального label нет)
<input aria-label="Search" />

❌ Плохо
<input placeholder="Email" />

Forms & validation

MUST

  • Ошибки связываем с полем через aria-describedby.
  • Поле с ошибкой отмечаем aria-invalid="true".
  • Ошибка должна быть текстом (не только цвет/иконка).
  • Сообщения об ошибках и подсказки должны быть доступны скринридеру.

Если вы используется UI-library (например, MUI, AntDesign, Chakra) - они уже используют встроенные механизмы для a11y.

✅ Хорошо (концепт)
<label htmlFor="name">Name</label>
<input
id="name"
aria-invalid={hasError}
aria-describedby={hasError ? 'name-error' : undefined}
/>
{hasError && (
<p id="name-error" role="alert">
Name is required
</p>
)}

Инструменты для проверки a11y

MUST

  • ESLint: eslint-plugin-jsx-a11y включён и не игнорируется без причины.
  • Любое отключение правила - с комментарием почему или ссылкой на проблему (issue или подобное).

SHOULD

  • доступность основных страниц (минимально)

Процедуры контроля (Code Review)

MUST

  • Корректность семантики HTML.
  • Доступность интерактивных элементов с клавиатуры.
  • Наличие label у полей форм.