Доступность (a11y) — не благотворительность и не «потом, если останется время». Это часть определения «продукт работает»: им должны пользоваться люди с клавиатуры, со скринридером, с увеличением. К тому же доступная разметка обычно и проще, и лучше для поиска. Продукт-инженер относится к a11y как к любому критерию качества — встроенному, а не приклеенному в конце.

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

Девяносто процентов доступности — это правильные HTML-элементы. Браузер уже знает, что button кликается и фокусируется, nav — навигация, label связан с полем. Заменяя их на div с обработчиком, ты выбрасываешь это знание и потом дорого восстанавливаешь его через ARIA.

// плохо: div ничего не сообщает и не фокусируется
<div onClick={save}>Сохранить</div>

// хорошо: button кликается, фокусируется, доступен с клавиатуры
<button onClick={save}>Сохранить</button>

Правило: сначала ищи подходящий семантический элемент, и только если его нет — добавляй ARIA. Заголовки (h1h3) идут по порядку, у полей есть label, у картинок — alt. Это фундамент, на котором держится всё остальное.

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

Всё, что доступно мышью, должно работать с клавиатуры: переход по Tab, активация по Enter/Space, понятный видимый фокус. Семантические элементы дают это бесплатно; кастомные виджеты (выпадашки, модалки) — нет, и за ними следят руками: ловушка фокуса в модалке, возврат фокуса после закрытия, закрытие по Escape.

Видимый фокус (:focus-visible) не убирают «ради красоты» — без него пользователь клавиатуры теряется на странице. Это частый и легко исправимый дефект.

ARIA — по необходимости

ARIA-атрибуты (role, aria-label, aria-expanded) добавляют семантику там, где штатных элементов не хватает. Но первое правило ARIA — не использовать ARIA, если есть нативный элемент: <button> лучше, чем <div role="button" tabindex="0"> с ручной обработкой клавиш. Неверный ARIA хуже, чем никакой: он вводит скринридер в заблуждение. ARIA уместен для динамики, у которой нет HTML-аналога — например, aria-live для уведомлений, появляющихся без перезагрузки.

Тестирование доступности

Доступность проверяема, а не «на глаз». Автоматические проверки (axe, плагины для линтера) ловят типовое — отсутствие alt, контраст, неверный ARIA. А Testing Library поощряет доступность самой своей моделью: она ищет элементы по роли и доступному имени (getByRole("button", { name: "Сохранить" })), как это делает скринридер. Если тест не может найти кнопку по роли — её, скорее всего, не найдёт и вспомогательная технология.

const button = screen.getByRole("button", { name: "Сохранить" });

То есть тесты, написанные «как пользователь», заодно проверяют доступность.

Где это в UCP

Доступность — встроенный критерий качества frontend, не финальная полировка: правильная семантика, работа с клавиатуры, ARIA только по нужде, проверки в тестах. Это та же дисциплина «качество — часть готовности», что наблюдаемость и тесты в backend. Для продукт-инженера, который ведёт продукт до пользователя, «до пользователя» включает всех пользователей — и тесты по ролям делают это проверяемым.