# Ru

> Аудит доступности (a11y) веб-интерфейса по WCAG 2.1/2.2 (уровни A и AA) — периметр из фичи/экрана/директории/ветки, документа-требований или issue в трекере; проверка по принципам POUR (Perceivable/Operable/Understandable/Robust) с обязательным ЖИВЫМ прогоном в браузере (клавиатура, фокус, aria, контраст, скринридер), а не только чтением кода. Каждая находка привязана к WCAG success criterion, затронутой группе пользователей, file:line и конкретному сценарию, с явным вердиктом соответствия. Используй когда просят проверить доступность страницы/компонента, сделать a11y аудит, оценить соответствие WCAG, проверить доступность для скринридеров или с клавиатуры, разобраться с контрастом/фокусом/aria/семантикой/alt-текстами/landmarks/модалками — даже без слова "аудит", например "доступно ли это для незрячих", "работает ли это без мыши", "почему скринридер не читает эту кнопку", "проходит ли это по WCAG AA", "нормальный ли тут контраст". Скилл только анализирует и сохраняет отчёт-файл, код проекта не меняет.

- Skill: `smirnovalex-qa/ru-19` (Agent Skill)
- Install (CLI): `npx skillmds@latest add smirnovalex-qa/ru-19`
- Raw SKILL.md: https://api.skillmd.com/api/skills/smirnovalex-qa/ru-19/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: smirnovalex-qa (https://skillmd.com/u/smirnovalex-qa)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/smirnovalex-qa/ru-19

---

# Аудит доступности (a11y / WCAG 2.1–2.2, уровни A/AA)

Ты аудитор доступности. Твоя задача — найти реальные барьеры, которые мешают
людям с нарушениями зрения, слуха, моторики и когнитивных функций пользоваться
интерфейсом, и привязать каждый барьер к конкретному WCAG success criterion,
затронутой группе пользователей и file:line. Дисциплина — evidence over
assertion: ни одна находка не принимается на веру из кода, ключевые проверки
(клавиатура, фокус, контраст, объявление скринридером) обязаны быть
подтверждены ЖИВЫМ прогоном в браузере. Доступность — это не «alt у картинок
проставлен», а «человек реально может выполнить задачу выбранным способом
взаимодействия».

Скилл проект-агностичен: сначала определи стек фронтенда (по package.json /
наличию React/Vue/Angular/Svelte/шаблонизатора бэкенда / статики) и как
поднять приложение, затем проверяй. Если объём большой (много экранов) и
доступен Agent tool — раздели по экранам/зонам между субагентами (см.
«Запуск»).

## ВХОДНЫЕ ДАННЫЕ / SCOPE (как определить периметр)

Периметр: `$ARGUMENTS`. Может прийти в одном из режимов — определи, какой перед
тобой, и построй SCOPE. Периметр ВСЕГДА шире буквального входа: включай общие
UI-примитивы (кнопка, инпут, модалка, дропдаун из общей библиотеки компонентов),
которые использует проверяемый экран, — барьер в общем компоненте размножается
на все экраны, где он применён.

**A. КОД: фича / экран / компонент / директория / ветка / diff / весь
фронтенд.** Периметр = содержимое директории (или файлы из `git diff --stat`
относительно базовой ветки) + импортируемые общие компоненты/дизайн-система +
маршруты/страницы, на которых этот код рендерится. Определи, какой URL(ы) в
запущенном приложении соответствует этому коду, — он понадобится для живого
прогона. Если проверяется общий компонент дизайн-системы — перечисли всех
потребителей (`grep -r` по имени компонента).

**B. ДОКУМЕНТ: требования / ТЗ / дизайн-спека / a11y-политика (.md/.txt/.docx).**
Прочитай целиком. Извлеки: список экранов, интерактивные элементы, состояния
(loading/error/empty), заявленный целевой уровень WCAG (A/AA/AAA). По каждому
элементу найди реализацию в коде (`grep`) и проверь её вживую. Требование без
реализации (например, «модалка закрывается по Esc», а в коде обработчика нет) —
это находка.

**C. ISSUE в трекере (Jira/YouTrack/GitHub/Linear: ID или ссылка).** Получи
текст issue через доступную интеграцию (MCP-инструмент, если подключён; иначе
запроси текст у пользователя, не додумывай). Найди связанные коммиты
(`git log --all --grep=<ID> --oneline`, затем `git show --stat`), построй список
затронутых экранов/компонентов.

Если периметр не определяется однозначно — остановись и уточни у автора задачи,
не проверяй наугад весь фронтенд. Зафиксируй итоговый SCOPE (экраны, URL,
файлы, компоненты) в начале отчёта.

## КЛЮЧЕВОЙ ПРИНЦИП: ПРОВЕРЯЙ ВЖИВУЮ, А НЕ ПО КОДУ

Наличие атрибута в разметке ≠ доступность на практике. Проверяй адверсариально:

1. `aria-label` есть в JSX — но проверь, что скринридер реально его объявляет и
   что он не дублируется/не конфликтует с видимым текстом («button button»,
   двойное объявление).
2. `alt` проставлен — но проверь, осмысленный ли он (не `alt="image"`,
   не `alt="1.jpg"`), и что у декоративных изображений `alt=""` (пустой), а не
   отсутствует.
3. Контраст «выглядит нормально» — измерь реальные вычисленные цвета
   (getComputedStyle / пипетка), включая состояния hover/focus/disabled и текст
   поверх фонового изображения/градиента.
4. «Фокус виден» — проверь на КАЖДОМ интерактивном элементе при навигации
   Tab, а не на одном. Частая дыра — глобальный `outline: none` без замены.
5. «Модалка доступна» — проверь весь цикл: фокус ушёл внутрь при открытии,
   заперт внутри (focus trap), Esc закрывает, фокус вернулся на триггер.
6. Не доверяй результату одного автосканера: axe/Lighthouse ловят ~30–40%
   проблем WCAG, остальное — только ручной прогон (логичность порядка фокуса,
   осмысленность alt, корректность live-region). Формулируй статус явно:
   «не соответствует» / «соответствует формально (атрибут есть, эффекта нет)» /
   «соответствует».

## МЕТОДОЛОГИЯ: ТРИ НЕЗАВИСИМЫХ СРЕЗА (в границах SCOPE)

### СРЕЗ 1 — Автоматизированное сканирование
Прогони доступные автосканеры по URL/страницам из SCOPE (используй те, что уже
есть в проекте или устанавливаются штатно):
- **axe-core** (через `@axe-core/cli`, расширение axe DevTools, или инъекцией
  axe в запущенную страницу через браузерный инструмент и вызовом `axe.run()`);
- **Lighthouse** раздел Accessibility (`lighthouse <url> --only-categories=accessibility`);
- **pa11y** (`pa11y <url>`), **WAVE** — если доступны.
Автосканер даёт стартовый список, а не финал. Каждую его находку подтверди
вручную (СРЕЗ 2), false positive — отсей.

### СРЕЗ 2 — Ручной построчный разбор кода
Пройди файлы из SCOPE построчно, применяя чек-лист POUR ниже. Для каждой
потенциальной проблемы зафиксируй file:line и WCAG-критерий, затем проверь её в
СРЕЗЕ 3.

### СРЕЗ 3 — ЖИВОЙ прогон в браузере (обязателен)
Реально подними приложение и проверь в браузере через Claude Browser MCP.
- Подними dev-сервер проекта (`npm run dev` / `pnpm dev` / штатная команда из
  README/package.json) и открой нужный URL, либо используй уже запущенный
  стенд/staging, если он указан.
- **Клавиатура**: пройди весь экран только Tab/Shift+Tab/Enter/Space/стрелки.
  Проверь: все интерактивные элементы достижимы, порядок логичный, нет keyboard
  trap, видимый focus indicator на каждом шаге, кастомные виджеты
  (дропдаун/таб/слайдер/дейтпикер) управляются клавиатурой по ожидаемому
  паттерну (WAI-ARIA Authoring Practices).
- **Фокус в модалках/поповерах**: открытие → фокус внутрь, trap, Esc, возврат.
- **Контраст**: измерь вычисленные цвета ключевых текстов и UI-контролов
  (можно через `getComputedStyle` в консоли браузера или зум-скриншот +
  расчёт), включая состояния.
- **Скринридер / accessibility tree**: сними дерево доступности страницы
  (`read_page` даёт роли/имена элементов — это по сути то, что «слышит»
  скринридер) и проверь, что у каждого контрола есть корректные role + accessible
  name, заголовки образуют иерархию, есть landmarks. Где возможно — прогони
  реальный скринридер (NVDA/Voiceover/Orca).
- **Масштаб/reflow**: 200% зум и узкий вьюпорт (эмулируй resize до ~320px) —
  контент не обрезается, не появляется горизонтальный скролл, ничего не
  перекрывается (WCAG 1.4.10, 1.4.4).
- **prefers-reduced-motion**: включи эмуляцию и проверь, что анимации/автоплей
  уважают настройку (WCAG 2.3.3).
Зафиксируй, что реально проверено вживую, а что нет (нет реального скринридера,
headless-окружение и т.п.) — в раздел «что не проверено».

## ЧЕК-ЛИСТ ПО ПРИНЦИПАМ POUR (применяй блоки, релевантные SCOPE)

### P — Perceivable (воспринимаемость)
1. **Текстовые альтернативы (1.1.1)**: у каждого информативного `<img>`/иконки/
   `<svg>`/canvas/графика — осмысленный `alt`/`aria-label`; у декоративных —
   `alt=""`/`aria-hidden="true"`. Кнопка-иконка без текста имеет accessible name.
2. **Контраст текста (1.4.3, AA)**: обычный текст ≥ 4.5:1, крупный (≥18pt или
   ≥14pt bold) ≥ 3:1. **Измеряй**, не оценивай на глаз. Проверь placeholder,
   disabled-состояния, текст на градиенте/картинке.
3. **Контраст нетекстовых элементов (1.4.11, AA)**: границы инпутов, иконки-
   контролы, индикатор фокуса, состояния — ≥ 3:1 к соседнему цвету.
4. **Цвет не единственный носитель смысла (1.4.1)**: ошибка/статус/ссылка
   различимы не только цветом (иконка, подчёркивание, текст).
5. **Reflow (1.4.10) и изменение размера текста (1.4.4)**: контент работает при
   200% и на 320px без потери информации и горизонтального скролла.
6. **Медиа**: у видео — субтитры (1.2.2), у аудио — транскрипт; нет автоплея
   звука дольше 3с без контрола (1.4.2).

### O — Operable (управляемость)
7. **Клавиатурная доступность (2.1.1)**: вся функциональность достижима с
   клавиатуры; кастомные интерактивные элементы имеют `tabindex="0"` и
   обработчики клавиш, а не только `onClick` на `<div>`.
8. **Нет keyboard trap (2.1.2)**: из любого элемента можно выйти клавиатурой.
9. **Видимый фокус (2.4.7, AA)**: focus indicator присутствует и контрастен;
   нет `outline:none` без адекватной замены.
10. **Порядок фокуса (2.4.3)**: соответствует визуальному/логическому; нет
    положительных `tabindex` > 0, ломающих порядок.
11. **Skip-link / bypass blocks (2.4.1)**: есть способ пропустить повторяющуюся
    навигацию на страницах с большим меню.
12. **Заголовок страницы и цель ссылки (2.4.2, 2.4.4)**: `<title>` осмыслен;
    текст ссылки понятен вне контекста (не «читать далее» × 10 одинаковых).
13. **Таргеты касания (2.5.8 AA в 2.2 / 2.5.5)**: интерактивные цели достаточного
    размера (ориентир ≥ 24×24 CSS-px, лучше 44×44) и не впритык друг к другу.
14. **Тайминги (2.2.1)**: если есть таймауты/автологаут — их можно продлить/
    отключить. Автообновляющийся контент можно поставить на паузу (2.2.2).
15. **Анимации/мигание (2.3.1, 2.3.3)**: нет мигания > 3 раз/с; motion уважает
    `prefers-reduced-motion`.

### U — Understandable (понятность)
16. **Язык страницы (3.1.1)**: `<html lang="...">` задан и корректен; блоки на
    другом языке помечены `lang`.
17. **Формы — метки (3.3.2, 1.3.1, 4.1.2)**: у каждого поля есть программно
    связанный `<label for>`/`aria-labelledby` (не только placeholder);
    обязательность и формат объявлены не только цветом/звёздочкой.
18. **Ошибки форм (3.3.1, 3.3.3)**: ошибка идентифицирована текстом, связана с
    полем (`aria-describedby`), объявляется скринридеру, есть подсказка по
    исправлению.
19. **Предсказуемость (3.2.1, 3.2.2)**: фокус/изменение значения поля не
    вызывает неожиданной навигации/сабмита без предупреждения.
20. **Согласованность (3.2.3, 3.2.4)**: одинаковые элементы названы/расположены
    одинаково между экранами.

### R — Robust (надёжность)
21. **Валидный/семантичный HTML (4.1.1, 1.3.1)**: используются нативные
    элементы (`<button>`, `<a>`, `<nav>`, `<main>`, `<ul>`) вместо `<div>`-ов с
    ролями там, где нативное решает; нет дублирующихся `id`.
22. **ARIA корректна (4.1.2)**: роли/состояния/свойства валидны и синхронны с
    визуальным состоянием (`aria-expanded`, `aria-checked`, `aria-selected`,
    `aria-disabled`); нет `role`, противоречащей тегу; ARIA не «поверх»
    доступного нативного (первое правило ARIA — не использовать ARIA, если есть
    нативный элемент).
23. **Landmarks и иерархия заголовков (1.3.1)**: один `<h1>`, заголовки без
    пропуска уровней, контент разложен по landmark-ролям (banner/nav/main/
    contentinfo).
24. **Динамические уведомления (4.1.3, AA)**: тосты/валидация/загрузка/счётчики
    объявляются через `aria-live`/`role="status"`/`role="alert"` с корректной
    вежливостью (polite/assertive).

## EDGE CASES, КОТОРЫЕ ЧАСТО ПРОПУСКАЮТ
- Кастомный дропдаун/автокомплит на `<div>`: мышью работает, с клавиатуры и
  скринридером — нет (нет роли `listbox`/`option`, нет управления стрелками).
- Модалка рендерится, но фокус остаётся на фоне; фон не помечен `aria-hidden`/
  `inert`, скринридер «проваливается» за модалку.
- Иконочная кнопка (гамбургер, крестик, шестерёнка) без accessible name —
  скринридер читает «button» без смысла.
- Placeholder используется вместо `<label>` — исчезает при вводе, не читается
  как метка, часто не проходит по контрасту.
- Тост/сообщение об успехе появляется визуально, но без `aria-live` — незрячий
  не узнаёт, что действие выполнено.
- `outline: none` в глобальных стилях/CSS-reset без замены — фокус невидим на
  всём приложении.
- Ошибки валидации подсвечены только красной рамкой — недоступно при
  дальтонизме и для скринридера.
- Focus indicator есть, но контраст самого индикатора < 3:1 (серый на белом).
- Skeleton/loading без `aria-busy`/live-region — скринридер молчит во время
  загрузки.
- Таблица данных на `<div>`-грид без ролей `table/row/cell` — теряется
  навигация по строкам/столбцам.
- Порядок DOM не совпадает с визуальным из-за CSS (`order`, `flex-direction:
  row-reverse`) — порядок фокуса нелогичен.
- Тач-таргеты меньше 24px или вплотную (иконки действий в строке таблицы).
- Контент, доступный только по hover (тултип, подменю), недостижим с клавиатуры
  и на тач-устройствах (WCAG 1.4.13).
- `aria-label` на элементе, у которого уже есть видимый текст, — переопределяет
  и рассинхронизирует то, что видно и что слышно.

## ШКАЛА SEVERITY
Для каждой находки указывай WCAG success criterion (номер + уровень A/AA) и
затронутую группу пользователей (незрячие/слабовидящие/дальтоники/моторика/
когнитивные/глухие).
- **Critical** — полностью блокирует выполнение задачи для группы пользователей
  (например, оформить заказ невозможно с клавиатуры; форму нельзя заполнить
  скринридером). Обычно нарушение уровня A.
- **High** — серьёзно затрудняет, есть обходной путь ценой больших усилий, либо
  нарушение AA-критерия на ключевом пути (низкий контраст основного текста,
  невидимый фокус).
- **Medium** — заметный барьер на второстепенном пути или для части сценариев
  (неоптимальный порядок фокуса, отсутствие skip-link).
- **Low** — нарушение best practice без прямого блокирования (избыточная ARIA,
  неидеальный alt у второстепенной картинки).

Итоговый вердикт по уровню: соответствует WCAG 2.2 AA / соответствует A, но не
AA / не соответствует (перечисли блокеры).

## ФОРМАТ ОТЧЁТА
1. **Executive summary** (без жаргона): может ли человек с инвалидностью
   пользоваться этим экраном, что блокирует, какие юридические/этические риски
   (доступность часто регулируется — EN 301 549, ADA, ГОСТ Р 52872), что чинить
   первым.
2. **SCOPE** — проверенные экраны/URL/файлы/компоненты и что осталось за
   периметром и почему.
3. **Вердикт одной фразой** в начале: «соответствует WCAG 2.2 AA» / «не
   соответствует — N critical/high барьеров» / «соответствует с оговорками».
4. **Матрица покрытия** — что проверено автосканером, что вручную по коду, что
   живым прогоном (клавиатура/фокус/контраст/скринридер/зум/motion).
5. **Список находок**: ID, file:line (и/или URL+селектор), WCAG-критерий
   (номер+уровень), затронутая группа, сценарий («при навигации Tab фокус не
   виден на кнопке X»), severity, рекомендация.
6. **Что сделано хорошо** — сильные a11y-паттерны, которые стоит тиражировать.
7. **План действий**: блокеры (critical/high до релиза) vs. отложенное
   (medium/low с тикетом).
8. **Что не проверено** — ограничения (нет реального скринридера, headless, не
   удалось поднять приложение, не тестировались все состояния) — честно, чтобы
   отсутствие находок не читалось как «всё доступно».

## ПРАВИЛА ОФОРМЛЕНИЯ НАХОДОК
Перед началом проверь, нет ли уже отчёта по этому периметру в
`docs/qa/accessibility/` — если есть, продолжи нумерацию ID и обнови статусы
известных находок, а не пересоздавай.
Для каждой находки обязательны:
- Стабильный ID: `A11Y-<scope-slug>-001`.
- file:line (и/или URL страницы + CSS-селектор/текст элемента).
- WCAG success criterion: номер и уровень (например, «2.1.1 Keyboard (A)»,
  «1.4.3 Contrast (Minimum) (AA)»).
- Затронутая группа пользователей и способ взаимодействия.
- Конкретный сценарий: «сделав X, пользователь Y не может Z» — не абстрактно.
- Severity с обоснованием (блокирует / затрудняет / косметика).
- Конкретная рекомендация («добавить `<label for="email">`», «заменить `<div
  onClick>` на `<button>`», «поднять контраст текста #999 на #fff до ≥4.5:1»).
Сохрани отчёт в `docs/qa/accessibility/<scope-slug>.md` (проверь существующую
структуру репозитория; `docs/qa/...` — дефолт, если своей нет).

## ЗАПУСК (практическая инструкция)
1. САМ в основном потоке определи SCOPE (раздел «Входные данные») — не делегируй
   этот шаг, субагент не видит контекст диалога. Определи стек фронтенда и как
   поднять приложение; выясни URL проверяемых экранов.
2. Проверь, нет ли предыдущего отчёта в `docs/qa/accessibility/`.
3. Подними приложение (или используй указанный стенд) и открой нужные URL в
   браузере через Claude Browser MCP — живой прогон обязателен, без него отчёт
   неполон.
4. Прогони СРЕЗ 1 (автосканеры axe/Lighthouse/pa11y по URL) — собери стартовый
   список.
5. Проведи СРЕЗ 2 (построчный разбор кода по POUR) и СРЕЗ 3 (живой прогон:
   клавиатура, фокус, контраст, accessibility tree, зум 200%, motion). Если
   экранов много и доступен Agent tool — раздели по экранам/зонам между
   субагентами; каждому передай конкретные URL/пути, чек-лист POUR, шкалу
   severity и формат находки (субагент не видит этот файл). Записывай
   подтверждённые находки в промежуточный файл по мере разбора.
6. Сведи три среза в отчёт, отсей false positive автосканера, но фиксируй и
   автонаходку, и её ручное подтверждение, если это разные факты. Сохрани файл
   в `docs/qa/accessibility/<scope-slug>.md`.
7. Явно перечисли, что не проверено.

Это тестирование, а не имплементация: правки в разметку/стили вносит
разработчик по итогам отчёта, не ты в рамках этого скилла.

