Введение
Сегодня веб-приложения стали сложными интерактивными системами, напоминающими полноценные десктоп приложения. Однако за красивыми интерфейсами и богатым функционалом часто скрывается важный аспект, которому уделяется недостаточно внимания — доступность (accessibility). Для миллионов пользователей с ограниченными возможностями это означает, что они не могут полноценно взаимодействовать с сайтом, выполнить базовые действия или получить информацию.
Проблема особенно ярко выражена в современных одностраничных приложениях (SPA), кастомных компонентах, модальных окнах и прочих динамических UI-решениях, которые не учитывают, как с ними будут работать вспомогательные технологии. Например:
Экранные читалки (screen readers), которые зачитывают структуру и контент страницы вслух.
Управление с клавиатуры для пользователей, не использующих мышь.
Специальные устройства ввода и навигации: Трекеры глаз, голосовое управление и тд.
Одним из ключевых инструментов, помогающих обеспечить доступность современных интерфейсов, является WAI-ARIA — набор дополнительных HTML-атрибутов, описывающих поведение и структуру интерфейсов для вспомогательных технологий. Эти атрибуты предоставляют способ «общения» между разработчиком и инструментами доступности, позволяя объяснить: «это не просто <div>
, это кнопка, этот элемент сейчас открыт, это вкладка номер два из трёх и так далее.
Что такое WAI-ARIA
WAI-ARIA расшифровывается как Web Accessibility Initiative — Accessible Rich Internet Applications (инициатива по обеспечению доступности веб-сайтов — доступные многофункциональные интернет-приложения). Это стандарт, разработанный организацией W3C в рамках инициативы Web Accessibility Initiative (WAI). Его цель — обеспечить доступность веб-приложений и динамических интерфейсов для всех пользователей, включая тех, кто использует вспомогательные технологии.
HTML уже содержит множество встроенных семантических элементов: <button>
, <nav>
, <header>
, <main>
и т.д. Они распознаются читалками и обеспечивают базовую доступность «из коробки».
Основные компоненты WAI-ARIA
WAI-ARIA состоит из трёх ключевых компонентов: ролей, свойств/состояний и live-регионов. Вместе они позволяют описать интерфейсные элементы и их поведение так, чтобы вспомогательные технологии могли правильно их интерпретировать.
-
Роли (
role
)ARIA-роли объясняют, что из себя представляет элемент, особенно если это неочевидно из разметки. Например,
<div>
сам по себе ничего не говорит читалке — но если мы добавимrole="button"
, он будет восприниматься как кнопка.Примеры ролей:
button
— интерактивная кнопка;dialog
— модальное окно;navigation
— область навигации;checkbox
,radio
,switch
— элементы выбора;menu
,menuitem
,menubar
— контекстные и выпадающие меню;tab
,tablist
,tabpanel
— вкладки и связанная структура;tooltip
— всплывающая подсказка.
Зачем они нужны:
Уточняют назначение элемента (особенно если это
<div>
или<span>
).Позволяют читалкам анонсировать правильную роль (например, «Кнопка, Войти»).
Помогают реализовать поведение, ожидаемое пользователями вспомогательных технологий.
Но, если есть возможность использовать нужный HTML-элемент, всегда лучше сделать это (например,
<button>
вместо<div role="button">
). -
Свойства и состояния (
aria-*
)ARIA-свойства и состояния помогают описывать текущее поведение и контекст элемента.
Свойства:
aria-label
— текстовая подпись элемента (вспомогательная альтернатива label).aria-labelledby
— указывает на ID элемента, чей текст используется как подпись.aria-describedby
— указывает на ID элемента, который содержит дополнительное описание.
Состояния:
aria-expanded="true/false"
— открыт ли раскрывающийся список.aria-checked="true/false/mixed"
— состояние чекбокса.aria-selected="true/false"
— выбрана ли вкладка или элемент.aria-disabled="true"
— доступен ли элемент.
-
Live-регионы (
aria-live
)Live-регионы используются для оповещения инструментов доступности о том, что часть контента была динамически изменена, и эту информацию важно озвучить пользователю.
Атрибуты:
aria-live="polite"
— Инструмент доступности дождётся паузы, прежде чем озвучить изменение.
Пример: Уведомление об успешно отправленной форме, появляющееся внизу страницы. Не требует немедленного внимания и может быть озвучено, когда пользователь закончит текущую задачу.
aria-live="assertive"
— Прерывает текущую озвучку и сразу сообщает об изменении.
Пример: Модальное окно с сообщением об ошибке или критическом сбое. Пользователь должен услышать это немедленно, даже если в текущий момент зачитывается другой контент.
aria-atomic="true"
— Заставляет читалку озвучить весь элемент целиком, даже если изменилась только его часть.
Пример: Счетчик уведомлений, который меняется с «Количество уведомлений: 3» на «Количество уведомлений: 4». Без этого атрибута может быть зачитано просто “4”, что не даст нужного контекста.
aria-relevant="additions removals text"
— Определяет, какие изменения должны быть озвучены: добавления, удаления или изменения текста.
Пример: Чат или список комментариев. Можно настроить,чтобы зачитывались только новые сообщения (additions) или также удаленные (removals), если это важно.
Особенности в SPA/React:
В одностраничных приложениях (SPA), изменения в интерфейсе происходят динамически без полной перезагрузки страницы. Такие изменения могут быть незаметны для инструментов доступности, если они не сопровождаются реальными изменениями в DOM или не помечены как значимые с помощью ARIA-атрибутов.
Например, React может «перерисовать» компонент, изменив визуальное содержимое, но если при этом не произошло явного обновления DOM (например, текст остался тем же или элемент был заменен без уведомления об изменении), инструмент доступности может не озвучить эти изменения пользователю.
Чтобы такие обновления были восприняты вспомогательными технологиями:
Используйте live-регионы (
aria-live
) там, где содержимое обновляется динамически и важно, чтобы пользователь это услышал;Учитывайте, что виртуальный DOM не всегда сразу приводит к видимым изменениям в реальном DOM — поэтому важно тестировать такие сценарии с инструментами доступности.
WAI-ARIA предоставляет мощный инструментарий для описания логики интерфейса в терминах, понятных инструментам доступности и другим технологиям. Но с большой силой приходит большая ответственность: использование атрибутов должно быть осознанным, соответствующим спецификации и протестированным в реальных условиях.
Доступность и управление с клавиатуры
Веб-доступность — это не только про экранные читалки. Один из важнейших ее аспектов — возможность полноценно управлять интерфейсом с клавиатуры. Это критично для пользователей с ограниченной подвижностью, людей, использующих ассистивные устройства, и даже разработчиков, которые предпочитают клавиатуру мыши.
Если интерфейс невозможно использовать без мыши — он считается недоступным.
Почему это важно
Мышь — не универсальный инструмент. Некоторые пользователи вообще не могут её использовать.
Клавиатурная доступность — требование стандарта WCAG (Web Content Accessibility Guidelines).
Отсутствие поддержки клавиатуры = недоступность читалке (она тоже полагается на клавишные события).
Даже если вы используете <button>
или <a>
, стоит убедиться, что кастомные компоненты (вкладки, меню, модалки, селекты и т.п.) имеют полноценную клавиатурную навигацию.
Основные клавиши и паттерны управления
Есть определенные ожидаемые стандарты поведения, которые пользователи знают и интуитивно ожидают:
Tab
— переход к следующему интерактивному элементу.Shift + Tab
— назад.Enter
/Space
— активация элемента (нажатие кнопки, открытие выпадашки).Arrow Keys
— перемещение в пределах компонента (вкладки, меню, радиогруппы).Escape
— закрытие модальных окон или dropdown’ов.Home
/End
— перемещение к первому/последнему элементу в списке.
Как ARIA помогает реализовать клавиатурную навигацию
WAI-ARIA не только даёт семантику элементам, но и определяет поведенческие паттерны для интерактивных компонентов. Эти паттерны описаны в ARIA Authoring Practices Guide (APG), и если вы следуете им, ваш компонент становится понятным и управляемым.
Что важно учитывать:
Используйте
tabindex="0"
для элементов, которые должны быть включены в порядок фокусировки с клавиатуры, иtabindex="-1"
— для тех, которые не должны фокусироваться по Tab, но на которые фокус может быть установлен программно.ARIA-роли должны сочетаться с корректной клавиатурной логикой — только одного
role="menu"
недостаточно.Всегда обрабатывайте события клавиатуры (
keydown
,keypress
) и обеспечивайте ожидаемое поведение.Используйте
aria-activedescendant
при создании виртуального фокуса внутри списков (например, автокомплитов).
Инструменты для тестирования доступности
Чтобы сделать интерфейс доступным, важно не только писать правильный код, но и проверять, как он воспринимается вспомогательными технологиями. К счастью, существует множество инструментов, которые помогают найти проблемы с доступностью на раннем этапе — в браузере, в редакторе кода и даже в CI/CD.
Расширения для браузера
-
Axe DevTools (Deque)
Самое популярное расширение.
Подсвечивает элементы с ошибками и объясняет, как их исправить.
Есть интеграции для Cypress, Jest, Puppeteer.
-
Lighthouse (встроен в Chrome DevTools)
Проверяет доступность, производительность и SEO.
Показывает основные ошибки (например, отсутствие
alt
, неправильный контраст, проблемы с фокусом).Подходит для быстрой проверки страницы целиком.
-
WAVE (WebAIM)
Показывает визуальные метки на странице, где есть проблемы.
Особенно полезен для визуального анализа и презентаций.
Экранные читалки
Автоматические проверки находят лишь ~30–40% проблем. Чтобы действительно проверить доступность, нужно услышать интерфейс.
-
Для Windows:
NVDA (бесплатный, популярный среди разработчиков).
JAWS (платный, более распространен в организациях).
-
Для macOS:
VoiceOver — встроен в macOS. Включается через Cmd + F5 или в "Системных настройках".
-
Что тестировать
Читается ли нужный текст?
Описана ли роль и состояние элементов (например, "Кнопка, Развернута")?
Работает ли навигация клавишами (
Tab
,Enter
, стрелки)?Сообщаются ли динамические изменения?
Линтеры и плагины для IDE
-
ESLint плагины:
eslint-plugin-jsx-a11y — анализирует JSX на предмет доступности (некорректные роли, отсутствие alt, интерактивные элементы без обработчиков клавиш и т.д.).
-
VS Code плагины:
Accessibility Insights for Web — от Microsoft. Даёт подробный отчёт и интеграцию с DevTools.
axe Accessibility Linter — встраивает анализатор прямо в редактор.
CI/CD интеграции
-
Axe CI
Поддерживает GitHub Actions, CircleCI, Travis, Jenkins.
Автоматически валидирует страницы и компоненты, можно задавать порог ошибок.
-
Pa11y CI
Лёгкий open-source инструмент.
Позволяет тестировать страницы из командной строки.
Генерирует отчеты в формате HTML.
-
Lighthouse CI
Интеграция Lighthouse в пайплайн.
Может сравнивать метрики с предыдущими сборками (в т.ч. по доступности).
Реализация компонента Tabs
Компонент Tabs часто встречается в интерфейсах — будь то переключение между профилями, категориями товаров или секциями настроек. Но важно, чтобы такие вкладки были доступными, то есть удобными для пользователей клавиатуры и экранных читалок. Реализуем простой переключатель вкладок с котами. Начнем с верстки:
<div class="tabs" id="cats-tabs">
<h3 class="tabs__title" id="tabs-title">Коты</h3>
<div class="tabs__buttons" role="tablist" aria-labelledby="tabs-title">
<button
class="tabs__button is-active"
id="tab-2"
type="button"
role="tab"
aria-controls="tabpanel-2"
aria-selected="true"
>
Барсик
</button>
<button
class="tabs__button"
id="tab-1"
type="button"
role="tab"
aria-controls="tabpanel-1"
aria-selected="false"
tabindex="-1"
>
Снежок
</button>
<button
class="tabs__button"
id="tab-3"
type="button"
role="tab"
aria-controls="tabpanel-3"
aria-selected="false"
tabindex="-1"
>
Рыжик
</button>
</div>
<div
class="tabs__content is-active"
id="tabpanel-1"
role="tabpanel"
aria-labelledby="tab-1"
tabindex="0"
>
<img src="./images/images.jpeg" width="500" height="350" />
</div>
<div
class="tabs__content"
id="tabpanel-2"
role="tabpanel"
aria-labelledby="tab-2"
tabindex="0"
>
<img src="./images/images2.jpg" width="500" height="350" />
</div>
<div
class="tabs__content"
id="tabpanel-3"
role="tabpanel"
aria-labelledby="tab-3"
tabindex="0"
>
<img src="./images/images3.jpeg" width="500" height="350" />
</div>
</div>
Мы используем role="tablist"
, role="tab"
и role="tabpanel"
— это важно для доступности. Атрибуты aria-selected
, aria-controls
, aria-labelledby
помогают инструментам доступности понять, что и с чем связано.
Также необходимо написать стили, который будет скрывать неактивные вкладки через display: none
, а активную подсвечивать, и скрипт который позволит полностью управлять нашими вкладками через клавиатуру:
const ACTIVE_CLASS = 'is-active';
class AccessibleTabs {
constructor(root) {
this.root = root;
this.buttons = Array.from(root.querySelectorAll('.tabs__button'));
this.panels = Array.from(root.querySelectorAll('.tabs__content'));
this.maxButtonIndex = this.buttons.length - 1;
this.state = new Proxy(
{ activeIndex: this.buttons.findIndex(btn => btn.classList.contains(ACTIVE_CLASS)) },
{
set: (target, key, value) => {
target[key] = value;
this.refreshTabsDom();
return true;
}
},
);
this.attachEvents();
}
refreshTabsDom() {
this.buttons.forEach((btn, idx) => {
const isActive = idx === this.state.activeIndex;
btn.classList.toggle(ACTIVE_CLASS, isActive);
btn.setAttribute('aria-selected', String(isActive));
btn.setAttribute('tabindex', isActive ? '0' : '-1');
});
this.panels.forEach((panel, idx) => {
panel.classList.toggle(ACTIVE_CLASS, idx === this.state.activeIndex);
});
}
attachEvents() {
this.buttons.forEach((btn, idx) => {
btn.addEventListener('click', () => {
this.state.activeIndex = idx;
});
});
this.root.addEventListener('keydown', e => this.handleKeydown(e));
}
handleKeydown({code, metaKey}) {
const nextTabIndex = {
ArrowLeft: metaKey ? 0 : this.state.activeIndex - 1,
ArrowRight: metaKey ? this.maxButtonIndex : this.state.activeIndex + 1,
Home: 0,
End: this.maxButtonIndex,
};
if (Object.keys(nextTabIndex).includes(code)) {
this.switchTab(nextTabIndex[code]);
this.buttons[newIndex].focus();
}
}
switchTab(newIndex) {
if (newIndex < 0) newIndex = this.maxButtonIndex;
if (newIndex > this.maxButtonIndex) newIndex = 0;
this.state.activeIndex = newIndex;
this.buttons[newIndex].focus();
}
}
Результат:

Вывод
Доступность — это не «фишка» и не «дополнительная опция». Это базовое качество современного веб-приложения. Если ваш интерфейс не работает с клавиатурой или не читается экранными читалками, значит, он недоступен для миллионов пользователей.
Полезные ссылки:
A11Y Style Guide — примеры доступных компонентов
Deque University — курс по доступности от создателей Axe
Vitaly_js
Полностью согласен.
А такой вопрос есть, вы сталкивались с тем, что бы для тестирования вам необходимо было еще все это обвешивать тестовыми идишниками?
Просто для примера, я смотрю на ваш пример и что-то с лету в голову не приходит кейса, когда что-то подобное было нужно...