Angular

Доступность и Angular Aria

Модальное окно открылось, но фокус остался на кнопке под ним. Пользователь скринридера не понимает, что появился диалог, табом уходит на ссылки за оверлеем, а сообщение об успешной отправке формы проговаривается только тем, кто его видит. Для миллионов людей с нарушениями зрения и моторики такой интерфейс просто не работает. Angular даёт два уровня инструментов: CDK a11y с готовыми утилитами (ловушка фокуса, живой анонсер) и Angular Aria - набор доступных по умолчанию примитивов, в которые правильные ARIA-атрибуты и клавиатура встроены изначально.

  • Государственные и банковские сайты обязаны соответствовать WCAG по закону (в ряде стран это требование регуляторов)
  • Angular Material построен поверх CDK a11y: его меню, диалоги и табы доступны с клавиатуры и для скринридеров из коробки
  • FocusTrap из CDK удерживает фокус внутри открытого модального окна - без этого таб уводит пользователя за пределы диалога
  • LiveAnnouncer проговаривает динамические сообщения (товар добавлен в корзину) тем, кто не видит экран
  • Навигация с клавиатуры критична для людей, не использующих мышь, и заодно ускоряет работу опытных пользователей

Предварительные знания

  • Привязки в шаблоне: property binding, атрибутные привязки, обработка событий
  • Базовая семантика HTML: button, nav, заголовки, label у полей формы
  • Идея о том, что часть пользователей работает без мыши и со скринридером
  • Шаблоны и привязки

Доступность в Angular: от CDK к Angular Aria

Когда команда Angular строила Material, ей понадобились переиспользуемые низкоуровневые утилиты доступности, и так появился пакет @angular/cdk/a11y с FocusTrap, LiveAnnouncer и FocusMonitor (примерно 2018 год). Эти инструменты годами лежали в основе доступности Material. Следующий шаг - Angular Aria, набор headless-примитивов для построения собственных доступных компонентов (меню, табы, аккордеон, listbox), где паттерны WAI-ARIA Authoring Practices и клавиатурная навигация встроены, а внешний вид остаётся за разработчиком.

Основы a11y: семантика прежде ARIA

Доступность - это пригодность интерфейса для людей с разными возможностями: незрячих и слабовидящих (скринридеры), тех, кто не пользуется мышью (только клавиатура), с нарушениями моторики или восприятия цвета. Отраслевой ориентир - стандарт WCAG (Web Content Accessibility Guidelines). Первое и главное правило доступности парадоксально: лучший ARIA-атрибут - тот, который не понадобился, потому что использован правильный семантический элемент.

Элемент button бесплатно объявляет роль кнопки скринридеру, попадает в порядок табуляции и срабатывает по Enter и Пробелу. Чтобы повторить это поведение на div, пришлось бы вручную добавить role, tabindex и обработчики клавиш - три потенциальных источника ошибок вместо одного правильного тега. Поэтому семантика стоит прежде ARIA.

ЗадачаСемантический элементЧто даёт бесплатно
ДействиеbuttonРоль, фокус, Enter/Пробел
Переходa с hrefРоль ссылки, фокус, переход
НавигацияnavОриентир для скринридера
Поле формыlabel + inputСвязь подписи и поля, клик по подписи

Кликабельный div без role, tabindex и обработки клавиш невидим для скринридера и недоступен с клавиатуры. Это самая частая ошибка доступности. Прежде чем тянуться к ARIA, стоит спросить: нет ли HTML-элемента, который уже делает нужное.

Почему кнопку-действие лучше делать через button, чем через div с обработчиком click и подходящим стилем?

ARIA-атрибуты через привязки шаблона

Когда семантики HTML не хватает - например, у кастомного компонента или для передачи состояния, - на помощь приходят ARIA-атрибуты. Они сообщают вспомогательным технологиям роль, состояние и свойства элемента: aria-label даёт доступное имя, aria-expanded показывает, раскрыт ли блок, aria-pressed - нажата ли кнопка-переключатель. В Angular динамические значения ARIA задаются через атрибутную привязку с префиксом attr.

Здесь aria-expanded привязан к сигналу isOpen и меняется вместе с состоянием панели, поэтому скринридер всегда знает, раскрыта она или нет. aria-controls связывает кнопку с панелью по id. У иконочной кнопки без текста aria-label даёт доступное имя Закрыть - иначе скринридер прочитал бы лишь символ. Префикс attr нужен, потому что aria-атрибуты живут в DOM как атрибуты, а не как свойства.

Главное правило ARIA: не дублировать и не ломать семантику. Ставить role button на настоящий button бессмысленно, а role button на div без клавиатуры - вредно, потому что скринридер объявит кнопку, которой нельзя пользоваться. ARIA дополняет семантику там, где её нет, но не подменяет правильный элемент.

Неверный или устаревший ARIA хуже отсутствия ARIA. aria-expanded, забытый в значении false при открытой панели, прямо вводит пользователя скринридера в заблуждение. Поэтому состояние ARIA привязывают к реальному состоянию компонента, а не проставляют вручную один раз.

Иконочная кнопка отображает только символ крестика без текста. Как сделать её понятной для скринридера в Angular?

CDK a11y: FocusTrap и LiveAnnouncer

Часть задач доступности решается не атрибутами, а поведением, и для них есть пакет @angular/cdk/a11y. Две ключевые утилиты: FocusTrap удерживает фокус внутри области (модального окна), чтобы Tab не уводил пользователя за оверлей, и LiveAnnouncer проговаривает скринридеру динамические сообщения, которых нет в фокусе. Обе доступны как директива/сервис и решают проблемы, недостижимые одной разметкой.

Директива cdkTrapFocus замыкает Tab и Shift+Tab внутри диалога: дойдя до последней кнопки, фокус возвращается на первую, а не уходит на элементы за оверлеем. Флаг cdkTrapFocusAutoCapture ставит фокус внутрь при открытии. Роль dialog, aria-modal и aria-labelledby сообщают скринридеру, что это модальное окно и как оно называется. Без ловушки фокуса диалог недоступен с клавиатуры.

LiveAnnouncer кладёт текст в скрытую aria-live область, и скринридер проговаривает его, не перемещая фокус. Параметр polite означает дождаться паузы в речи; assertive прервёт немедленно для срочных сообщений. Это единственный способ сообщить незрячему пользователю о событии, которое визуально видно, но не получает фокус.

Какую проблему доступности модального окна решает FocusTrap (cdkTrapFocus) из CDK a11y?

Angular Aria и навигация с клавиатуры

Для построения собственных доступных компонентов - меню, табов, аккордеона, listbox - есть Angular Aria, набор headless-примитивов. Headless означает: примитив несёт поведение и доступность (правильные роли, ARIA-состояния, клавиатурную навигацию по паттернам WAI-ARIA), но не навязывает внешний вид. Разработчик получает корректную доступность бесплатно и стилизует компонент как угодно. Это снимает классическую дилемму между кастомным дизайном и доступностью.

Клавиатурная навигация - сердце доступных интерактивных компонентов, и у неё есть устоявшиеся соглашения. Tab переходит между виджетами, стрелки перемещают фокус внутри одного виджета (между пунктами меню, вкладками), Enter и Пробел активируют, Escape закрывает. Эти паттерны и встроены в примитивы Angular Aria, поэтому набор пунктов меню сразу управляется стрелками без ручного кода.

КлавишаОжидаемое действие
Tab / Shift+TabПереход между виджетами по порядку
СтрелкиПеремещение фокуса внутри виджета (меню, табы)
Enter / ПробелАктивация элемента в фокусе
EscapeЗакрытие меню, диалога, выпадающего списка

Проверить доступность с клавиатуры можно без специальных инструментов: убрать руку с мыши и пройти весь сценарий только клавиатурой. Если до элемента нельзя добраться Tab, активировать Enter или закрыть Escape - компонент недоступен. Примитивы Angular Aria закрывают эти паттерны по умолчанию, но самописные виджеты стоит проверять именно так.

Что означает headless-природа примитивов Angular Aria для построения доступного меню или табов?

Связь с другими темами

Урок про то, чтобы интерфейс работал для всех. Связи:

  • Шаблоны и привязки — ARIA-атрибуты и роли задаются через привязки в шаблоне компонента
  • Тестирование — Playwright и harness обращаются к элементам по роли и имени, проверяя доступность
  • Строгая типизация — Доступность - слой качества рядом с типобезопасностью в модуле архитектуры

Итог

  • Доступность (a11y) делает интерфейс пригодным для людей со скринридером, без мыши и с другими особенностями; ориентир - стандарт WCAG
  • Семантический HTML - первый и главный инструмент: правильный элемент несёт роль и поведение бесплатно
  • ARIA-атрибуты дополняют семантику там, где её не хватает, но не заменяют правильный элемент
  • CDK a11y даёт FocusTrap (удержание фокуса в диалоге), LiveAnnouncer (озвучивание динамики) и FocusMonitor
  • Angular Aria - headless-примитивы доступных компонентов с встроенными ARIA и клавиатурой, оформление за разработчиком
  • Клавиатурная навигация (Tab, стрелки, Enter, Escape) обязательна: всё, что доступно мышью, должно работать с клавиатуры

Связанные уроки

  • ng-04-templates-binding — ARIA-атрибуты ставятся через привязки в шаблоне, нужна модель template binding
  • ng-40-testing — Playwright обращается к элементам по роли и имени, поэтому доступность проверяется e2e-тестами
Доступность и Angular Aria

0

1

Войти