Дедлайн Европейского закона о доступности прошёл в июне 2025 года. Для частных компаний продающих услуги в ЕС соответствие WCAG 2.1 AA стало юридическим требованием. Большинство ошибок доступности невидимы при обычной разработке: они всплывают когда пользователь клавиатуры не может добраться до модального окна, скринридер объявляет кнопку с иконкой просто как «кнопка», или поле формы не даёт никакого контекста о том что вводить. Находить их нужно комбинируя клавиатурное тестирование и базовое использование скринридера с автоматическими сканами axe-core в Playwright CI.
Почему доступность важна в 2026 году
Примерно 15% населения планеты живут с той или иной формой инвалидности. Это не маленький edge-case: это пользовательский сегмент больше чем большинство крупнейших демографических групп любой компании. Но ещё недавно доступность воспринималась как дизайнерская опция или юридическая галочка для государственных сайтов. Это меняется.
Дедлайн Европейского закона о доступности (EAA) прошёл в июне 2025 года. Частные компании продающие продукты или услуги в ЕС теперь обязаны соответствовать требованиям доступности или рискуют регуляторными санкциями и штрафами. В Великобритании, Канаде и Австралии свои пересекающиеся нормы. В США ADA применяется к цифровым продуктам через судебные иски уже много лет. WCAG 2.1 AA: международный эталон на который ссылается большинство этих законодательных актов.
Юридический риск реален, но не единственная причина обращать на это внимание. Недоступные приложения имеют более высокий показатель отказов, генерируют больше обращений в поддержку, и создают репутационные риски. Checkout-поток который нельзя пройти с клавиатуры блокирует пользователей не способных работать мышью. Форма с непомеченными полями бесполезна для пользователя скринридера. Это не гипотетика. Такие паттерны появляются в продакшн-приложениях каждый день, часто годами прежде чем кто-то замечает.
QA-инженеры умеющие находить такие проблемы до релиза действительно ценны. Инструменты существуют. Техники поддаются изучению. Проблема только в осведомлённости.
WCAG 2.1: что означает POUR на практике
WCAG 2.1 (Web Content Accessibility Guidelines) строится вокруг четырёх принципов, сокращённых как POUR: Perceivable (Воспринимаемость), Operable (Управляемость), Understandable (Понятность), Robust (Устойчивость).
Perceivable означает что пользователи воспринимают весь контент хотя бы одним органом чувств. Изображениям нужны текстовые альтернативы. Видео нужны субтитры. Контент не может полагаться только на цвет для передачи смысла («обязательные поля показаны красным» это ошибка, если красный цвет единственный индикатор). Operable означает что вся функциональность доступна без мыши. Клавиатурная навигация должна достигать каждого интерактивного элемента. Пользователям должно хватать времени на выполнение задач (без автоматического истечения сессий без предупреждения). Ничто не мигает более трёх раз в секунду (риск эпилептических приступов). Understandable означает что интерфейс ведёт себя предсказуемо, а сообщения об ошибках объясняют что пошло не так. Форма говорящая «Invalid input»: ошибка. «Email must include an @ symbol»: нет. Robust означает что контент интерпретируется широким диапазоном вспомогательных технологий, включая нынешние и будущие. Вот где важен семантический HTML: устойчив; стилизованный под кнопку, как правило, нет.
Соответствие уровня AA, требуемое EAA и большинством юридических норм, означает что выполнены все критерии успеха уровней A и AA. Вместе они охватывают: у изображений есть alt-текст (A), цвет не единственный визуальный индикатор (A), контрастность обычного текста не менее 4.5:1 (AA), вся функциональность доступна с клавиатуры (A), индикаторы фокуса видны (AA), поля форм имеют программно привязанные метки (A).
Как тестировщику не нужно запоминать всю спецификацию. Нужно знать какие критерии нарушаются чаще всего и как их тестировать.
Ручное тестирование доступности: клавиатура и скринридеры
Ни один автоматический инструмент не найдёт все ошибки доступности. Ручное тестирование с клавиатурой и скринридером незаменимо.
Клавиатурная навигация тестируется первой. Отключи мышь (или pointer events в DevTools) и перемещайся по приложению только с помощью Tab, Shift+Tab, Enter, Space и стрелок. Каждый интерактивный элемент (ссылки, кнопки, поля форм, дропдауны, модальные окна, datepicker-ы) должен быть досягаем и управляем. Порядок переходов по Tab должен следовать логической последовательности чтения: как правило, слева направо и сверху вниз. Фокус не должен застревать (нажимаешь Tab и ничего не происходит), за исключением намеренного ограничения внутри модального окна, где фокус должен быть заперт до его закрытия.
Самый распространённый сбой клавиатурной навигации: разработчик строит модальный диалог, но Tab изнутри него уходит на фоновую страницу. Модальное окно визуально присутствует, но пользователь клавиатуры теперь взаимодействует с контентом позади него, не зная об этом.
Индикаторы фокуса: тоже часть этого теста. У каждого сфокусированного элемента должен быть видимый индикатор: стандартный браузерный контур, кастомный стиль, что угодно. Дизайны полностью убирающие :focus-стили через outline: none в CSS создают невидимый фокус, что делает клавиатурную навигацию полностью неработоспособной. Ищи это в каждом review спринта.
Тестирование со скринридером сложнее и требует практики, но даже базовые знания дают огромную ценность. Два самых распространённых бесплатных скринридера: NVDA (Windows, бесплатно) и VoiceOver (macOS и iOS, встроен). JAWS распространён в корпоративных средах но стоит денег.
Установи NVDA и открой приложение. Отключи монитор если хочешь полного погружения. Перемещайся по Tab к интерактивным элементам, используй режим просмотра NVDA (стрелки) для чтения статичного контента. Слушай что объявляет скринридер. Кнопка «Click here» ничего не говорит пользователю. Кнопка «Submit payment» говорит точно что произойдёт. Кнопка с иконкой без aria-label объявляет свою роль как «button» но ничего о своей функции. Непомеченное поле формы объявляется как «edit» без контекста о том что вводить.
Не нужно становиться продвинутым пользователем скринридера чтобы находить значимые баги. Потрать 30 минут на навигацию по основным пользовательским сценариям с NVDA. Проблемы делающие работу неудобной или невозможной выплывут быстро.
Комбинация клавиатурного тестирования и базового использования скринридера найдёт большинство серьёзных ошибок доступности в типичном веб-приложении.
Автоматизированное тестирование доступности с axe-core и Playwright
Автоматические инструменты не заменят ручное тестирование, но они надёжно находят определённый набор проблем (отсутствие alt-текста, недостаточный цветовой контраст, отсутствие меток форм, некорректное использование ARIA) постоянно и в масштабе. Запуск в CI означает что регрессии находятся до того как они доходят до тестировщиков.
Наиболее широко используемый движок: axe-core от Deque Systems. Он лежит в основе браузерных расширений (axe DevTools), Lighthouse и ряда интеграций с фреймворками. Для команд уже использующих Playwright, пакет @axe-core/playwright добавляет сканирование доступности с минимальной настройкой.
Базовый Playwright-тест сканирующий страницу на нарушения доступности:
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('homepage should have no automatically detectable accessibility violations', async ({ page }) => {
await page.goto('/');
const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
Тест падает если axe находит нарушения. На практике, особенно в существующей кодовой базе, лучше начинать с логирования а не с блокировки: фиксируй нарушения, исправляй постепенно, добавляй ассерции по мере расчистки бэклога.
Для более точного сканирования можно охватить конкретные компоненты и отфильтровать по уровню WCAG:
test('checkout form meets WCAG 2.1 AA', async ({ page }) => {
await page.goto('/checkout');
const results = await new AxeBuilder({ page })
.include('#checkout-form')
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
if (results.violations.length > 0) {
const violationDetails = results.violations.map(v => ({
id: v.id,
impact: v.impact,
description: v.description,
nodes: v.nodes.map(n => n.html),
}));
console.log('Violations:', JSON.stringify(violationDetails, null, 2));
}
expect(results.violations).toEqual([]);
});
Фильтр .withTags() ограничивает сканирование конкретными критериями WCAG. wcag2a, wcag2aa и wcag21aa вместе покрывают уровень соответствия AA. Добавь эти тесты в CI-пайплайн рядом с существующими функциональными тестами.
axe-core автоматически находит примерно 30–40% проблем WCAG. Это не повод его пропускать: автоматически найти треть проблем в масштабе: это реальная ценность. Но зелёный скан axe не сертифицирует доступность. Это пол, не потолок.
Что не находят автоматические инструменты
Здесь живут самые критичные ошибки доступности. Поэтому ручное тестирование не заменить ничем.
Осмысленный alt-текст: самый наглядный пример. Автоматический инструмент обнаруживает что у элемента ![]()
нет атрибута alt. Это бинарная проверка. Определить точный ли и осмысленный ли alt-текст он не может. Изображение линейного графика показывающего падение выручки с описанием alt="chart" проходит автоматическую проверку. Пользователь скринридера слышит «chart» и не имеет никакой информации. Ошибка семантическая, а не структурная.
Логический порядок чтения. Порядок в DOM определяет как скринридеры обходят страницу. Визуальные стили могут отображать элементы совершенно иначе чем они существуют в DOM. Зрячий пользователь читает кнопку «Add to cart» после описания продукта потому что она расположена ниже. Если в DOM кнопка стоит до заголовка продукта, пользователь скринридера слышит кнопку прежде чем понимает с каким товаром взаимодействует. Автоматические инструменты частично обнаруживают перестановки, но сложные layout-ы требуют человека для оценки реального опыта прослушивания.
Цветовой контраст в контексте частично автоматизируется но может давать ложные результаты. axe сканирует computed styles, но перекрывающиеся элементы, фоновые изображения и градиентные фоны могут обмануть сканер. Проверяй контраст вручную с браузерным colour checker-ом или инструментами вроде WebAIM Contrast Checker когда видишь сложный визуальный дизайн.
Доступные имена для кнопок с иконками: постоянная ошибка которую инструменты находят лишь частично. содержащий только SVG-иконку может пройти автоматическую проверку если у иконки есть элемент title. Но реально ли этот title объявляется, и насколько чётко он звучит в контексте, слышишь только со скринридером.
Управление фокусом после динамических изменений контента почти полностью ручное. Когда открывается модальное окно, фокус должен перейти в него. Когда закрывается, фокус должен вернуться к элементу запустившему его. Когда SPA-навигация меняет страницу, фокус должен переместиться в логическую стартовую точку, обычно или цель skip-link. Ни один инструмент надёжно не тестирует этот поток. Нужно самому пройти его по Tab.
Типичные ошибки доступности которые находят QA-инженеры
Эти паттерны появляются снова и снова в продуктах любого масштаба. Зная их, узнаёшь с первого взгляда.
Отсутствие меток форм встречается чаще всего. Видимый placeholder в поле ввода не заменяет доступную метку. Когда пользователь начинает вводить, placeholder исчезает, и пользователь скринридера вернувшись к полю слышит только «edit» без контекста. Исправление: элемент привязанный через for/id, или атрибут aria-label или aria-labelledby. Найти их: переходить по Tab к каждому полю формы и слушать что говорит NVDA.
Несемантические интерактивные элементы появляются когда разработчик строит кликабельный или вместо . Элемент может выглядеть как кнопка и реагировать на клик мышью, но Tab его не достигает, Enter и Space не работают, и скринридеру он объявляется как обычный текст а не кнопка. Любой интерактивный элемент кроме нативных HTML-контролов (, , и т.д.) нуждается в явных role, tabindex и обработчиках событий клавиатуры. Это дорогостоящие исправления; находить их на раннем этапе экономит значительный рефакторинг.
Плохое управление фокусом в модальных окнах и drawer-ах встречается почти в каждом кастомном диалоговом компоненте. Модальное окно открывается, но фокус остаётся на кнопке которая его запустила (теперь позади оверлея). Tab уходит на фоновую страницу. При закрытии фокус падает в начало документа вместо возврата к триггеру. Тестируй каждое модальное окно: открывай через Tab+Enter и навигируй исключительно клавиатурой.
Обновления динамического контента без объявления сильно задевают SPA. Пользователь отправляет форму, появляется сообщение об успехе, но оно возникает в другой части DOM без смены фокуса и без live region. Пользователь скринридера не знает что отправка прошла успешно. ARIA live regions (role="status" или aria-live="polite") решают это, но только если кто-то догадался их добавить.
Keyboard trap-ы в кастомных виджетах. Кастомные дропдауны, datepicker-ы и редакторы богатого текста часто захватывают фокус клавиатуры или не реализуют стандартные взаимодействия (стрелки для навигации по дропдауну, Escape для закрытия). Тестируй каждый кастомный интерактивный компонент только с клавиатуры.
Как писать баг-репорты по доступности
Баг-репорты по доступности требуют больше контекста чем стандартные UI-баги, потому что влияние зависит от того какая вспомогательная технология затронута и какой критерий WCAG нарушен. Размытый репорт («скринридер здесь не работает») не даёт разработчику ничего конкретного.
Хороший баг-репорт по доступности включает:
Нарушенный критерий WCAG. Каждая ошибка соответствует критерию. Отсутствие метки нарушает WCAG 1.3.1 (Info and Relationships) и 4.1.2 (Name, Role, Value). Отсутствие индикатора фокуса нарушает WCAG 2.4.7 (Focus Visible). Укажи ID критерия и название. Это говорит разработчику точно что входит в требования, и документирует пробел в соответствии для любого аудита.
Вспомогательная технология и браузер. «NVDA 2024.1 + Chrome 124 on Windows 11»: конкретно. «Screen reader»: нет. Разные комбинации AT/браузер ведут себя по-разному, и разработчикам нужен конкретный стек для воспроизведения.
Ожидаемое vs фактическое объявление. Для багов со скринридером: напиши что AT объявило и что должно было объявить. «Объявлено: "button". Ожидалось: "Submit payment, button"». Это и есть описание ошибки.
Шаги воспроизведения использующие AT, а не мышь. «Нажать Tab до поля email, слушать объявление NVDA», а не «кликнуть на поле email». Шаги должны воспроизводиться только с затронутым способом взаимодействия.
Оценка влияния. Используй уровни влияния WCAG: critical (блокер для пользователей AT), serious (значительные трудности), moderate (некоторые трудности), minor. Отсутствие метки у модального окна: critical. Неоптимальная структура заголовков: minor или moderate. Влияние определяет приоритет исправления.
Не помечай баги по доступности как «nice to have» если они не мелкие на самом деле. Форма которую нельзя отправить с клавиатуры: блокер для пользователей клавиатуры. Та же серьёзность что и кнопка checkout не работающая для зрячих пользователей мышью. Применяй те же стандарты серьёзности что и к любому функциональному сбою.
Как применить это уже в понедельник
Не нужно проводить полный аудит продукта за один спринт. Начни с малого и выработай привычку.
На этой неделе: установи NVDA (бесплатно, Windows) или включи VoiceOver (macOS: Cmd+F5). Открой одну страницу приложения, лучше форму или поток с модальным окном. Пройди по Tab. Слушай что NVDA объявляет для каждого элемента. Оформи всё что звучит непонятно или молчит.
В этом спринте: добавь пакет @axe-core/playwright в проект. Напиши один тест сканирующий самую критичную страницу: вход, checkout или онбординг. Запусти в CI. Пока не блокируй билд, начни с логирования нарушений чтобы команда видела базовый уровень.
В течение следующего месяца: добавь проверку клавиатурной навигации в definition of done для новых функций. Включи «протестировано только с клавиатурой» в чеклист PR. Для каждой функции с формами или модальными окнами трать десять минут на тестирование порядка Tab и управления фокусом.
Для баг-репортов по доступности: создай шаблон в баг-трекере с полями для критерия WCAG, затронутой AT и ожидаемого объявления. Заполнить шаблон занимает тридцать секунд. Наличие этой информации в репорте экономит час переписки с разработчиком.
Дедлайн EAA прошёл. Компании не занявшиеся доступностью работают с юридическими рисками. QA-инженеры умеющие находить, документировать и отслеживать ошибки доступности напрямую снижают этот риск. Что важнее: исправления которые ты ловишь до продакшена достигают пользователей которым они нужны. Пользователь скринридера завершающий оформление заказа без посторонней помощи, пользователь клавиатуры заполняющий форму без застревания: вот к чему всё это. Инструменты только средство.
Начни с одного клавиатурного теста. Дальше само пойдёт.
→ See also: Тестирование доступности с Playwright: автоматизированные a11y проверки | События клавиатуры и мыши в Playwright | Основы тестирования удобства использования: что нужно знать QA-инженерам