Доступность веб-приложений (A11y): WCAG 2.2, ГОСТ Р 52872-2019 и практика
Что требуют WCAG 2.2 и ГОСТ Р 52872-2019, какие ошибки доступности самые частые, как делать семантику, фокус, формы, модальные окна и SPA доступными и как тестировать с axe и скринридерами.
Доступный интерфейс — это интерфейс, которым можно пользоваться без мыши, без идеального зрения, со скринридером, с увеличенным шрифтом и дрожащей рукой. По оценке Всемирной организации здравоохранения, значимую инвалидность имеют около 1,3 миллиарда человек — примерно каждый шестой житель планеты. В России на начало 2025 года на учёте состояли 11,1 миллиона человек с инвалидностью. Но выгоду от доступности получают не только они: человек с загипсованной рукой, пользователь на ярком солнце, пожилой клиент с увеличенным системным шрифтом, сотрудник, который быстрее работает с клавиатуры. Эта статья — практическое руководство для команд, которые делают веб-приложения: какие стандарты действуют, какие требования нарушаются чаще всего, как решать их в разметке и коде, как тестировать и как встроить доступность в процесс, чтобы не переделывать продукт перед сдачей. Стандарты: WCAG 2.2, ГОСТ и законодательство WCAG 2.2 Web Content Accessibility Guidelines — стандарт консорциума W3C. Актуальная рекомендация — WCAG 2.2, опубликованная в октябре 2023 года; в октябре 2025 года она утверждена как международный стандарт ISO/IEC 40500:2025. WCAG 3.0 находится в разработке и стандартом пока не является. Требования сгруппированы по четырём принципам: воспринимаемость, управляемость, понятность и надёжность. У каждого критерия есть уровень: A — минимальный, AA — целевой для большинства продуктов и законодательных требований, AAA — расширенный, полностью его обычно не достигают. Что изменилось в 2.2 по сравнению с 2.1: 2.4.11 Focus Not Obscured (AA) — элемент в фокусе не должен полностью перекрываться закреплённой шапкой, баннером cookie или чатом. 2.5.7 Dragging Movements (AA) — всё, что делается перетаскиванием, должно быть доступно и простыми нажатиями. 2.5.8 Target Size (Minimum) (AA) — интерактивные элементы не меньше 24×24 CSS-пикселей или с достаточным отступом до соседних. 3.2.6 Consistent Help (A) — способы получить помощь находятся в одном и том же месте на разных страницах. 3.3.7 Redundant Entry (A) — не заставлять повторно вводить уже введённые в процессе данные. 3.3.8 Accessible Authentication (Minimum) (AA) — вход без когнитивных тестов вроде запоминания или переписывания символов, если нет альтернативы: вставка пароля, менеджер паролей, вход по ссылке. Новые критерии уровня AAA: 2.4.12 (фокус не перекрыт даже частично), 2.4.13 (заметность индикатора фокуса) и 3.3.9 (доступная аутентификация без исключений). Критерий 4.1.1 Parsing удалён как устаревший. Российские требования ГОСТ Р 52872-2019 «Интернет-ресурсы и другая информация, представленная в электронно-цифровой форме. Приложения для стационарных и мобильных устройств, иные пользовательские интерфейсы. Требования доступности для людей с инвалидностью и других лиц с ограничениями жизнедеятельности» введён в действие с 1 апреля 2020 года и основан на WCAG 2.1. Это национальный стандарт: по общему правилу он применяется добровольно, если на него не ссылаются обязательные нормы или договор. На практике ГОСТ всё чаще включают в технические задания государственных и корпоративных заказчиков. Для официальных сайтов государственных органов, органов местного самоуправления и подведомственных организаций действует порядок обеспечения доступности для инвалидов по зрению, утверждённый приказом Минцифры России от 12 декабря 2022 года № 931; он заменил приказ Минкомсвязи № 483 от 2015 года. Если вы делаете сайт для госзаказчика, актуальную редакцию приказа и требования ТЗ нужно сверить до проектирования. Для коммерческих сайтов в России единого прямого требования соответствовать WCAG нет. Но если продукт работает с пользователями из Евросоюза, стоит учесть European Accessibility Act : с 28 июня 2025 года его требования применяются, в частности, к онлайн-торговле и ряду цифровых услуг, а ориентиром соответствия служат WCAG уровня AA. Для микропредприятий, оказывающих услуги, предусмотрены исключения. Какие ошибки встречаются чаще всего WebAIM ежегодно проверяет главные страницы миллиона популярных сайтов автоматическим анализатором. В отчёте WebAIM Million 2026 года самые распространённые проблемы такие: текст с недостаточным контрастом — 83,9% главных страниц; изображения без альтернативного текста — 53,1%; поля форм без подписей — 51%; пустые ссылки — 46,3%; пустые кнопки — 30,6%; не указан язык документа — 13,5%. Все шесть проблем решаются базовой разметкой и дисциплиной в дизайн-системе. С них и стоит начинать. Семантическая разметка — основа Скринридеры (NVDA, JAWS, VoiceOver, TalkBack) строят дерево доступности из HTML. Если кнопка сделана из div , скринридер не знает, что это кнопка, а клавиатура до неё не дотянется. !-- Плохо: не фокусируется, не нажимается с клавиатуры, не объявляется как кнопка -- div class="btn" onclick="submitForm()" Отправить заявку /div !-- Хорошо: всё это уже встроено -- button type="submit" Отправить заявку /button Нативная кнопка получает фокус, срабатывает по Enter и пробелу, объявляется как «кнопка» и поддерживает состояние disabled . Самодельная потребует роли, tabindex , обработчиков клавиш — и всё равно будет уступать. Структура страницы задаётся ориентирами — landmark-элементами — и иерархией заголовков. Пользователи скринридеров переходят между ними горячими клавишами, как зрячие пробегают страницу глазами. html lang="ru" ... header nav aria-label="Основное меню" ... /nav /header main h1 Заголовок страницы /h1 section aria-labelledby="cases-title" h2 id="cases-title" Кейсы /h2 ... /section /main footer ... /footer Атрибут lang нужен, чтобы скринридер читал текст с правильным произношением. Заголовки не пропускают уровни ради размера шрифта: размер задаётся стилями, а уровень — смыслом. ARIA: только когда нет нативного элемента ARIA — набор атрибутов, которые дополняют семантику для сложных компонентов: вкладок, комбобоксов, древовидных списков. Первое правило ARIA из спецификации W3C: если есть нативный HTML-элемент с нужным поведением, используйте его. Неправильная ARIA хуже её отсутствия — она сообщает скринридеру неверную информацию. Роли определяют тип элемента: role="tablist" , role="tab" , role="tabpanel" . Свойства описывают связи и характеристики: aria-label , aria-labelledby , aria-describedby , aria-controls . Состояния меняются при взаимодействии: aria-expanded , aria-selected , aria-checked , aria-invalid . Готовые паттерны с описанием клавиатурного поведения собраны в ARIA Authoring Practices Guide (APG) от W3C — это первое место, куда стоит заглянуть перед реализацией нестандартного компонента. Модальные окна: элемент dialog Раньше доступное модальное окно требовало вручную ловить фокус внутри, блокировать фон и возвращать фокус после закрытия. Сейчас это умеет нативный элемент dialog , поддерживаемый всеми актуальными браузерами: при открытии через showModal() остальная страница становится недоступной для взаимодействия, фокус переходит внутрь, Escape закрывает окно. button type="button" id="open-delete" Удалить проект /button dialog id="confirm-delete" aria-labelledby="dlg-title" aria-describedby="dlg-desc" h2 id="dlg-title" Удалить проект? /h2 p id="dlg-desc" Действие необратимо: файлы и история будут удалены. /p form method="dialog" button value="cancel" autofocus Отмена /button button value="confirm" Удалить /button /form /dialog script const opener = document.getElementById('open-delete'); const dialog = document.getElementById('confirm-delete'); opener.addEventListener('click', () = dialog.showModal()); dialog.addEventListener('close', () = { if (dialog.returnValue === 'confirm') deleteProject(); opener.focus(); }); /script Для необратимых действий фокус по умолчанию ставится на безопасную кнопку. После закрытия фокус возвращается к элементу, который открыл окно, — иначе пользователь клавиатуры оказывается в начале страницы. Клавиатура и фокус Критерий 2.1.1 требует, чтобы вся функциональность была доступна с клавиатуры. Базовые ожидания пользователей: Tab / Shift+Tab — переход между интерактивными элементами в логическом порядке; Enter — ссылки и кнопки, пробел — кнопки, чекбоксы; стрелки — перемещение внутри составного компонента: вкладок, меню, радиокнопок, списка вариантов; Escape — закрыть окно, меню, подсказку. Порядок фокуса должен совпадать с визуальным порядком. Его ломают положительные значения tabindex и перестановка элементов через CSS ( order , размещение в Grid), когда визуальная последовательность расходится с DOM. Видимый фокус Убрать обводку фокуса ради эстетики — одна из самых вредных правок. Компромисс между дизайном и доступностью — псевдокласс :focus-visible , поддерживаемый всеми современными браузерами: браузер показывает индикатор при навигации с клавиатуры и, как правило, не показывает при клике мышью. /* Плохо: фокус исчезает для всех */ button:focus { outline: none; } /* Хорошо: заметный индикатор при навигации с клавиатуры */ :focus-visible { outline: 3px solid #1a5fb4; outline-offset: 2px; } /* Закреплённая шапка не должна перекрывать элемент в фокусе (WCAG 2.4.11) */ html { scroll-padding-top: 80px; } Простая проверка: отложите мышь и пройдите основной сценарий — регистрацию, форму заявки, оплату — только клавиатурой. Каждое место, где непонятно, где фокус, или куда нельзя попасть, — дефект. Формы Поле без подписи для пользователя скринридера — это «редактируемый текст» без смысла. Подпись связывается с полем программно: label for="company" Название компании /label input id="company" name="company" autocomplete="organization" required fieldset legend Удобный способ связи /legend label input type="radio" name="contact" value="phone" Телефон /label label input type="radio" name="contact" value="email" Email /label /fieldset Ошибки проверки связываются с полем и объявляются скринридером: label for="email" Email /label input id="email" type="email" autocomplete="email" aria-invalid="true" aria-describedby="email-error" p id="email-error" Укажите адрес в формате name@company.ru /p При отправке формы с несколькими ошибками надёжный приём — показать сводку ошибок в начале формы со ссылками на поля и перевести на неё фокус. Для коротких уведомлений, которые не требуют перевода фокуса, используют область с role="status" (вежливое объявление) или role="alert" (срочное). Область должна присутствовать в DOM заранее — текст, вставленный вместе с новым контейнером, скринридеры объявляют ненадёжно. Чек-лист формы: у каждого поля видимая подпись; placeholder её не заменяет; обязательные поля отмечены текстом или символом с пояснением, а не только цветом; связанные поля сгруппированы в fieldset с legend ; у полей личных данных указан autocomplete — это требование критерия 1.3.5; ошибка объясняет, что исправить, и связана с полем; успешная отправка сообщается так, что её слышит скринридер. Цвет и контраст Требования WCAG уровня AA: обычный текст — контраст не ниже 4,5:1; крупный текст (от 18 пунктов или от 14 пунктов полужирный, то есть примерно от 24 и 18,5 CSS-пикселя) — 3:1; границы полей, иконки, индикаторы состояния и другие значимые графические элементы — 3:1 к соседним цветам (критерий 1.4.11); логотипы и декоративные элементы требованиям контраста не подчиняются. Цвет не должен быть единственным способом передать информацию (критерий 1.4.1). Нарушения цветовосприятия встречаются примерно у 8% мужчин и 0,5% женщин, чаще всего это трудности с различением красного и зелёного. Поле с ошибкой отмечается не только красной рамкой, но и текстом; линии на графике различаются не только цветом, но и маркерами или подписями. Контраст проверяется ещё в макетах — плагинами Figma, — и в браузере: инструменты разработчика Chrome показывают коэффициент контраста в палитре цвета. Если у продукта есть тёмная тема, её проверяют отдельно. Изображения и мультимедиа !-- Содержательное изображение: смысл, а не «картинка» -- img src="dashboard.png" alt="Дашборд продаж: выручка по регионам за квартал" !-- Декоративное: пустой alt, скринридер пропустит -- img src="pattern.svg" alt="" !-- Кнопка-иконка: подпись описывает действие -- button type="button" aria-label="Закрыть" svg a