# Доступность

Цель: WCAG 2.1 AA на всех страницах и на всех языках, включая языки с письмом справа налево.

## Что проверяется автоматически

- **axe в e2e** (`apps/web/e2e/*.spec.ts`, помощник `e2e/axe.ts`). Правила WCAG 2.0/2.1/2.2 A и AA и best-practice проверяются на главной, статье, странице поиска, часах сердца, анатомии, офлайн-странице, страницах встраивания и конструкторе кода. Страницы встраивания и главная проверяются и в тестовой RTL-локали (сборка с `HA_TEST_LOCALES=1`). В этой сборке отключены только `html-lang-valid` и `valid-lang`, потому что код псевдолокали `qps-plocm` axe не знает. В продакшен-сборке они включены.
- **Lighthouse CI** (`apps/web/lighthouserc.json`). Проверяет главную, статьи, анатомию, часы сердца, поиск, конструктор и модули встраивания (ЭКГ, статья, 3D-модель) с мобильной эмуляцией, по три прогона, по медиане. Каждая категория должна набрать не меньше 90, LCP не больше 2500 мс, CLS не больше 0,1. У модулей встраивания нет проверки SEO: они намеренно закрыты от индексации (`noindex`).
- **Объём JS** (`pnpm --filter @heart-atlas/web js-budget`). Скрипт считает JS, который может загрузить страница статьи (gzip, по `dist/`), и проверяет, что он не больше 100 КБ. Страница анатомии с 3D в этот бюджет не входит.

Автоматические проверки находят примерно треть проблем. Остальное проверяется руками по списку ниже перед каждым релизом и после больших изменений интерфейса.

## Ручная проверка

Нужны клавиатура без мыши и экранный диктор: VoiceOver (macOS, Safari) или NVDA (Windows, Firefox или Chrome). Проверяйте на `ru`, `en` и, если меняли раскладку, на сборке с RTL-локалью.

### Все страницы

- [ ] Первое нажатие Tab показывает ссылку «Перейти к содержанию». Enter переносит фокус на `<main>`.
- [ ] Фокус виден на каждом интерактивном элементе, порядок обхода совпадает с визуальным.
- [ ] Переключатель языка объявляется как навигация, у текущего языка есть `aria-current`.
- [ ] Медицинское предупреждение в подвале читается диктором.
- [ ] При увеличении до 200 % и ширине 320 px нет горизонтальной прокрутки и ничего не обрезано.
- [ ] Тёмная тема: текст и ссылки контрастны (axe проверяет только светлую).

### Поиск

- [ ] Форма в шапке объявляется как «поиск», поле подписано.
- [ ] На странице поиска диктор один раз объявляет число результатов, без промежуточного «поиск…».
- [ ] По результатам можно пройти Tab; «Показать ещё» работает с клавиатуры.

### Статья

- [ ] Переключатель «Для пациентов / Для студентов» работает с клавиатуры, диктор объявляет нажатую кнопку.
- [ ] Плашка «врачом не проверено» читается до текста статьи.
- [ ] Ссылки на источники `[1]` объявляются как «источник 1» и переносят к списку источников.

### Часы сердца

- [ ] Анимация не стартует сама при `prefers-reduced-motion: reduce`.
- [ ] Кнопки «Пуск / Пауза» и «Звук» работают с клавиатуры. Звук не включается сам.
- [ ] Слайдер ЧСС меняется стрелками, диктор читает значение.
- [ ] У ЭКГ и диаграммы Виггерса есть текстовое описание, которое меняется вместе с ритмом и пресетом.
- [ ] Переключатель «норма / болезнь» — группа радиокнопок, описание пресета связано с группой.

### Анатомия (3D)

- [ ] До загрузки модели показан постер с текстовой альтернативой.
- [ ] Модель вращается и масштабируется с клавиатуры, фокус не застревает в canvas.
- [ ] Выбор структуры возможен без мыши, название выбранной структуры объявляется.

### Встраивание и офлайн

- [ ] В конструкторе кода все поля подписаны. Ошибка в ЧСС объявляется, «Код скопирован» объявляется при каждом копировании.
- [ ] Во фрейме модуля ссылка «открыть на сайте» открывается в новой вкладке, ссылки на источники остаются во фрейме.
- [ ] Офлайн-страница читается целиком, ссылка на главную работает.
- [ ] Уведомление о новой версии объявляется диктором, кнопка «Обновить» доступна с клавиатуры.

## Журнал ручных проверок

Записывайте сюда дату, кто проверял, браузер и диктор, и найденные проблемы (со ссылкой на issue).

| Дата | Кто | Браузер и диктор | Результат |
|---|---|---|---|
| — | — | — | Ручная проверка с диктором ещё не проводилась |
