Компонент может пройти визуальный review и всё равно не дать человеку закончить действие. Типичный симптом: панель настроек открывается мышью, но с клавиатуры непонятно, куда попал фокус; после сохранения меняется текст, а состояние элемента не имеет объявленного имени, роли или статуса. Цена ошибки не в «плохом ARIA». Человек не может уверенно выбрать настройку, исправить неверный ввод или понять, завершилось ли действие.
В апреле 2022 года я бы не начинал с набора атрибутов. Сначала надо описать один сценарий как контракт: какой элемент получает фокус, какое у него имя и роль, что меняется после действия и куда возвращается фокус. Это связывает новую для автора область доступности с предыдущими статьями о рендеринге: видимый кадр — не весь интерфейс. Отдельно важная граница: пример ниже — учебная in-memory модель. Он не запускает DOM, браузер, клавиатуру или ассистивную технологию и не доказывает, что конкретный screen reader что-то произнёс.
Один сценарий вместо общего чек-листа
Возьмём панель «Настроить уведомления». В исходном состоянии фокус стоит на кнопке открытия. После открытия — на первом варианте канала. Неверное значение не меняет сохранённый канал и возвращает маршрут на элемент, где можно исправиться. Корректное сохранение закрывает панель, возвращает фокус на trigger и формирует declared status. Последнее слово принципиально: команда заявляет, какое состояние должна передавать разметка, но не подменяет этим наблюдение реальной программы чтения с экрана.
| Точка | Симптом без контракта | Проверяемое свойство | Действие |
|---|---|---|---|
| Открыть панель | фокус остаётся на декоративной иконке | имя «Настроить уведомления», роль button, следующий focus target | использовать нативную кнопку или реализовать полный button contract |
| Выбрать канал | видна галочка, но неясно, что выбрано | имя группы, role radiogroup, один declared checked value | связать визуальный выбор с тем же state |
| Ошибка | сообщение есть только цветом или рядом с мышью | invalid branch не меняет saved value и имеет named focus target | сохранить причину и вернуть маршрут к исправлению |
| Сохранить | панель исчезает, фокус теряется | return target и declared status | вернуть фокус на trigger, оставить способ отменить изменение |
Сначала семантика, затем внешний вид
Нативный button, input и label дают часть поведения сразу. Если проект заменяет кнопку на div ради стиля, он берёт на себя не только role. Потребуются name, focusability, активация с клавиатуры, состояние и тестируемый порядок. WAI-ARIA Authoring Practices прямо отделяет role от поведения: роль не превращает произвольный элемент в полноценную нативную кнопку. Поэтому минимальное решение часто проще: оставить нативный host, а class и layout строить вокруг него.
Это не означает, что любой native element закрывает задачу. У кнопки может быть пустое или двусмысленное имя, состояние может обновляться в другом месте, а фокус — уходить после unmount. WCAG 2.1 задаёт проверяемые критерии для keyboard, focus order, name/role/value и status messages. Они полезны как карта свойств, но не назначают имя вашего state store и не выбирают момент закрытия панели. Эти решения остаются у компонента и должны быть видны в его тесте.
Учебная fixture: проверяем порядок, а не браузер
В пакете есть короткая детерминированная модель. Она хранит channel, savedChannel, pendingUndo и focus как локальные строки. После открытия пробует недопустимое значение carrier-pigeon, затем выбирает sms, сохраняет и отменяет сохранение. Значения preferences-trigger и notification-email — проектные labels маршрута, а не id из существующей страницы. Это позволяет проверить смысл изменения без выдуманного browser test.
node web/scripts/upgrade-2022-04.mjs --verify-fixture
// PASS fixture: 16/16 assertions
// Модель проверяет declared name, role, focusRoute, invalid branch и undo.
// Она не создаёт DOM, не нажимает Tab и не получает речь screen reader.
Шестнадцать assertions охраняют именно границу модели: DOM не создан, клавиатурное событие не отправлено, ассистивная технология не наблюдалась. Остальные assertions проверяют name/role, declared focus route, вывод selected state из channel, invalid branch, возврат к trigger и одноразовый undo. Если в рефакторинге invalid branch начнёт менять saved value, команда увидит поломку правила. Если же реальная страница не показывает focus ring, fixture не даст ложного PASS: для этого нужна отдельная проверка в поддерживаемом браузере.
Маршрут: симптом → причина → проверка → действие
- Симптом. Компонент выглядит законченным, но Tab не даёт предсказуемой точки входа, выбор не имеет понятного имени или после save фокус исчезает.
- Причина. Визуальное состояние и semantic contract принадлежат разным веткам кода; роль добавили после разметки, а return focus не описали.
- Проверка семантики. Для trigger, группы вариантов, каждого выбора и статуса запишите name, role и изменяемое state. Если свойство нельзя назвать, его пока нельзя надёжно проверить.
- Проверка маршрута. Нарисуйте путь: вход, invalid branch, успешное закрытие. Для реальной страницы пройдите его клавиатурой в поддерживаемых браузерах и отдельно зафиксируйте результат; fixture этого не делает.
- Действие. Сначала замените ложный custom control нативным либо добавьте полный contract. Затем сделайте один небольшой test на return focus и один на invalid branch.
- Откат. Изменение состояния должно сохранять предыдущее значение до commit. В модели undo возвращает
emailи одноразово очищает pending action; реальный продукт всё равно должен отдельно определить срок и UX отмены.
Объявление — это данные, а не обещание результата
После сохранения компоненту полезно сформировать состояние вроде «Канал уведомлений сохранён: SMS». Но нельзя записать в отчёт «screen reader произнёс фразу», если никто не проверял конкретную комбинацию браузера и технологии. В fixture есть только declaredAnnouncement и отметка declared-not-observed. Она проверяет, что код не потерял сообщение при переходе, но не моделирует очередь live region, язык интерфейса, настройки пользователя или фактическую речь.
Для production-проверки это превращается в маленький план. Сначала посмотреть DOM и computed accessibility tree выбранного браузера; затем пройти Tab, Shift+Tab, Enter и Space там, где они предусмотрены паттерном; затем проверить выбранные сочетания assistive technology и браузера с заранее записанным ожидаемым состоянием. Результат такой проверки — новый артефакт с версиями и ограничениями. Его нельзя заранее вывести из ARIA-атрибута или из учебной модели.
Ограничение и следующий шаг
Этот маршрут покрывает один control contract, а не доступность продукта. Он не измеряет контраст, не проверяет локализацию, touch target, виртуальный курсор, порядок во всех модальных окнах и не заменяет исследование с пользователями. Даже полное прохождение WCAG-критериев не позволяет заявить, что опыт удобен для каждого человека. Но контракт убирает более раннюю и дешёвую ошибку: компонент перестаёт быть «визуально готовым» без имени, маршрута и обратимого результата.
Следующий практический шаг — выбрать один часто используемый компонент, собрать таблицу его name/role/state/focus и зафиксировать один keyboard route до любого редизайна. Если найдётся несоответствие, исправляйте его малым обратимым изменением: native host, явный return target или связанный status. После изменения повторите реальную проверку и добавьте её результат рядом с кодом, не заменяя его общим словом «доступно».
Историческая граница апреля 2022
Статья опирается на WCAG 2.1 Recommendation от 5 июня 2018 года и WAI-ARIA Authoring Practices 1.2 от 29 ноября 2021 года. В декабре 2021-го WAI-ARIA 1.2 имела статус Candidate Recommendation Draft; Recommendation 2023 года здесь не используется как будто она уже существовала. Учебные labels, undo policy и status text — решения пакета, а не требования W3C и не отчёт о production-дефекте.
Проверяемые источники
- WCAG 2.1, W3C Recommendation от 5 июня 2018 года — неизменяемая нормативная версия. В ней есть проверяемые критерии Keyboard, Focus Order, Name, Role, Value и Status Messages; она не описывает поля учебной fixture.
- WAI-ARIA Authoring Practices 1.2, W3C Group Note от 29 ноября 2021 года — датированное официальное руководство с паттернами кнопок, клавиатурной навигацией и фокусом. Это guidance, а не запись о ручной проверке конкретного компонента.
- WAI-ARIA 1.2, W3C Candidate Recommendation Draft от 8 декабря 2021 года — неизменяемая версия периода. Она фиксирует статус Candidate Recommendation Draft; материал не приписывает апрелю 2022 будущую Recommendation 2023 года.