Доступность в Angular c помощью CDK A11y на реальных кейсах с FocusTrap и FocusMonitor

Kate

Administrator
Команда форума
Мы привыкли слышать, что Angular - это фреймворк, который решает массу задач из коробки: свой CLI, встроенная сборка приложений, автоматическая миграция на новые версии с помощью schematic, работа с HTTP, DI, реактивные формы, работа с состоянием - все это удобные инструменты для разработчика. Обычно я сравниваю его с коробкой автомат: сел и поехал, не отвлекаясь на переключение передач.

Но в мире веба мы всегда должны думать о пользователях. И один из разделов, который заботится о них, называется доступность (Accessibility, A11y в англоязычной среде). И тут Angular позаботился о нас и дал мощнейший инструмент из коробки под названием CDK A11y. Предлагаю ознакомиться с концепцией доступности и изучить применение этого инструмента в Angular.

9da71e2ee9db16bbed004c1a1939c1f2.gif

Что такое доступность​

Существует множество способов взаимодействия с сайтом: мышь, клавиатура, голосовое управление, touch-устройства и т.д. Доступным будет считаться тот сайт, который максимально покрывает все варианты взаимодействия с ним. Независимо от пользовательских предпочтений, возможность дойти от точки действия А к Б не будет являться для него проблемой. Попробуйте навигацию клавиатурой по Github - в нем неплохо учтены пользовательские кейсы доступности.

Помимо способов взаимодействия с сайтом, в A11y можно выделить отдельное направление про контент на сайте. Сюда будут входить хорошие шрифты, цвета (возможны высокие контрасты для людей с нарушением зрения), скрытый/вспомогательный контент.

Также к доступности можно отнести: скорость загрузки сайта (возможно, есть пользователи, у которых ограничена пропускная способность интернета). А также работу сайта на различных устройствах.

Тема доступности очень важна для пользователей, ведь это связано с удобством. Это как доступная среда на улице: вход в подъезд без ступеней и лишних препятствий, велодорожки, светофоры и т.д. Более того, в некоторых странах доступность начинает входит в стандарты сайта по умолчанию, и в случае отсутствия, к владельцу могут быть применены штрафы. Кроме того, Google при индексации сайтов может учитывать и проверять состояние доступности на сайте.

CDK A11y в Angular​

Начнем с FocusTrap. Это API предназначено, чтобы захватывать фокус в рамках определенного участка интерфейса. Такой захват нужен в случае, если нужно обеспечить навигацию через TAB.

Обычно выделяют следующие кейсы в интерфейсе для использования захвата фокуса:

  • навигация в модальных окнах;
  • навигация в меню;
  • навигация в datepicker;
  • различные пользовательские формы ввода, например, форма login;
Пример реализации захвата в календаре от Material.

Material Datepicker & FocusTrap
Material Datepicker & FocusTrap
Использовать cdkFocusTrap просто - достаточно обернуть необходимую область для захвата и при перемещении по tab мы не будем вываливаться за пределы области.

Area 1
<div cdkTrapFocus class="example-cdkTrapFocus">
<button>button 1</button>
<button>button 2</button>
<button>button 3</button>
<button>button 4</button>
</div>

Area 2
<div class="example-cdkTrapFocus">
<button>button 1</button>
<button>button 2</button>
<button>button 3</button>
<button>button 4</button>
</div>
FocusMonitor - это сервис, который предоставляет работу с фокусом. Он дает две возможности:

1. С помощью Monitor можно подписаться на Observable и получать состояния DOM элемента. Он отдаёт события focus и blur и возвращает, с помощью чего был установлен фокус (мышь, клавиатура, touch-устройство или установка через код)

В каких кейсах можно использовать FocusMonitor? Где угодно! Например:

  • скрытие / открытие tooltip, пример из material;
  • выполнение логики для перерисовки визуальных частей компонента таблиц при взаимодействии с компонентами сортировки, пример из material;
  • мониторинг списка с выделением, пример из material;
Пример использования в коде:

export class FocusMonitorExample implements AfterContentInit, OnDestroy {
....
constructor(
private _element: ElementRef<HTMLElement>,
private _focusMonitor: FocusMonitor
) {}

ngAfterContentInit(): void {
this._focusMonitor?.monitor(this._element)
.pipe(takeUntil(this._destroyed))
.subscribe(origin => {
if (origin === 'keyboard' || origin === 'program') {
// выполняем логику компонента
}
});
}

ngOnDestroy() {
this._focusMonitor?.stopMonitoring(this._element);
}
}
2. Он позволяет устанавливать фокус на указанные элементы через метод focusVia. При этом, в него можно передать параметр, с помощью чего был установлен фокус (мышь, клавиатура, через touch, через код)

В каких кейсах можно использовать FocusMonitor для установки фокуса:

  • кнопка в форме - кнопка, которая получает фокус. Такую кнопку можно нажать сразу, без перевода на неё фокуса с помощью клавиатуры. Отличный кейс, когда требуется показывать на сайте onboarding/tutorial с кнопкой «далее»;
  • поле ввода - при создании письма в gmail фокус, фокус сразу попадает в поле ввода почты получателя;
  • чекбоксы, радиобаттоны на форме, можно управлять фокусом элемента по умолчанию;
  • элементы меню;
Использование в коде:

export class FocusMonitorViaExample {
...
@ViewChild('btn') buttonElement: ElementRef;
constructor(
private _focusMonitor: FocusMonitor
) {}

focusElement() {
this._focusMonitor.focusVia(this.buttonElement, 'program');
}
}
Если всё так просто – зачем нам FocusMonitor?

Может показаться, что задачи сервиса довольно простые: обрабатывать события blur, focus или устанавливать фокус. Но данный сервис избавляет нас от рутины и потенциальных багов, которые могут возникнуть в ходе работы приложения. Можно выделить следующие плюсы:

  • Переиспользуемый код. Так или иначе мы бы сами сделали что-то подобное для своих приложений, чтобы не дублировать код.
  • Сервис работает в разном окружении, если приложение запущено за пределами браузера, эти кейсы предусмотрены (тесты, ssr и т.д.).
  • Обработка ShadowRoot. Если элемент будет находиться внутри ShadowRoot, то обработчики focus/blur нужно привязывать к его корню, иначе мы не получим события.
  • Оптимизация работы приложения. Используется runOutsideAngular для работы с нативными событиями addEventListener, чтобы лишний раз не тревожить Zone.js.
  • Предусматривает мониторинг дочерних элементов.
Поэтому CDK FocusMonitor очень полезен в ваших приложениях. Многие задачи, с которыми вы можете столкнуться, уже решены в нём.

Заключение​

Мы рассмотрели понятие доступности. Узнали, какие инструменты существуют в Angular CDK. Подробно рассмотрели FocusMonitor и FocusTrap API для работы с доступностью. Но это далеко не весь функционал, который предоставляет нам CDK. Тема довольно обширная и выходит за рамки одной статьи. Если у вас есть опыт работы с остальным API доступности, напишите в комментариях, интересен ваш опыт и видение в этом направлении.

 
Сверху