Доступность вёрстки: минимум, который надо знать
Скринридер читает вашу кнопку как «button», клавиатура не попадает в модалку, а цвет ссылки сливается с текстом. Разбираем минимум по доступности, который закрывает большую часть проблем без переписывания проекта.

Открываете сайт, отключаете мышь и пытаетесь дойти до формы обратной связи с клавиатуры. Фокус прыгает в невидимое место, модалку невозможно закрыть, а поле «Имя» не подсвечивается при переходе Tab. Для человека, который пользуется скринридером или не может работать мышью, это не мелочь, а стена. Ниже — набор приёмов, которые закрывают большую часть таких проблем и не требуют переписывать вёрстку с нуля. Ориентир — WCAG 2.1 уровень AA, на него ссылаются и российские, и зарубежные требования к сайтам.
Формат материала — практика: вы уже верстаете, но не уверены, что делаете доступно. Разбираем конкретные ошибки и то, чем за каждую платят пользователи вспомогательных технологий. Углублённое тестирование скринридерами и ARIA для сложных виджетов (комбобоксы, деревья) — отдельная большая тема, здесь её не трогаем.
Семантика вместо div-супа
Главный источник проблем — вёрстка на <div> и <span> с навешенными обработчиками. Скринридер и клавиатура ничего не знают о том, что этот div — на самом деле кнопка. Нативный элемент даёт три вещи бесплатно: роль (скринридер объявит «кнопка»), фокусируемость (попадёт под Tab) и обработку клавиатуры (Enter и Space срабатывают сами).
Сравните два варианта одной кнопки:
<!-- Плохо: браузер не знает, что это кнопка -->
<div class="btn" onclick="submit()">Отправить</div>
<!-- Хорошо: роль, фокус и клавиатура работают из коробки -->
<button type="button" onclick="submit()">Отправить</button>
Первый вариант придётся чинить руками: добавить role="button", tabindex="0" и обработчик keydown на Enter и Space. Второй работает сразу. Правило простое: если для элемента есть подходящий HTML-тег, используйте его, а не div с ARIA-ролью.
Лучшая ARIA — это отсутствие ARIA. Первое правило спецификации ARIA прямо говорит: если можешь использовать нативный HTML-элемент с нужной семантикой, используй его, а не добавляй роль вручную.
Ссылка ведёт куда-то (<a href>), кнопка что-то делает на странице (<button>). Не путайте: ссылка без href не фокусируется, а кнопка, стилизованная под ссылку, всё равно должна быть <button>, если она не ведёт по адресу.
Клавиатура: пройдите сайт без мыши
Самая быстрая проверка доступности — отложить мышь и пройти ключевой сценарий на Tab, Shift+Tab, Enter, Space и Esc. Что проверять:
- Виден ли фокус. Если в вашем CSS есть
outline: noneбез замены — это разрыв. Убрали обводку — верните видимый индикатор через:focus-visible. - Логичен ли порядок. Фокус должен идти сверху вниз и слева направо, как читается страница. Ломается это чаще всего из-за
tabindexс положительным числом — его почти никогда не нужно ставить. - Не попадаете ли в ловушку. Открыли модалку — фокус должен остаться внутри неё, а Esc закрывать. Открыли меню — стрелки или Tab должны из него выходить.
- Достижимы ли все элементы. Кастомные выпадашки, слайдеры, вкладки на
divбезtabindexпросто пропускаются.
Про :focus-visible — он показывает обводку, когда пользователь пришёл с клавиатуры, но не при клике мышью. Это то, чего обычно хотят дизайнеры, которым мешает «рамка при клике»:
/* Убираем стандартную обводку только там, где даём свою */
button:focus-visible,
a:focus-visible {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
:focus-visible поддерживается всеми актуальными версиями Chrome, Firefox, Safari и Edge. В очень старых браузерах он просто не сработает, и обводка покажется всегда — это безопасный откат, а не ошибка.
Формы: label — не украшение
Поле без подписи скринридер прочитает как «редактируемый текст» — пользователь не поймёт, что вписывать. placeholder подписью не считается: он исчезает при вводе и часто не читается вспомогательными технологиями.
<!-- Явная связь label и input через for/id -->
<label for="email">Электронная почта</label>
<input type="email" id="email" name="email" autocomplete="email">
<!-- Или обёрткой, тогда for/id не нужны -->
<label>
Электронная почта
<input type="email" name="email" autocomplete="email">
</label>
Ошибки валидации привязывайте к полю через aria-describedby, а не просто рисуйте красный текст рядом:
<label for="phone">Телефон</label>
<input type="tel" id="phone" aria-describedby="phone-error" aria-invalid="true">
<span id="phone-error">Введите номер в формате +7...</span>
Тогда скринридер объявит ошибку сразу после названия поля. Атрибут autocomplete с корректным значением помогает автозаполнению и людям с когнитивными особенностями — это тоже требование WCAG уровня AA.
Изображения и контраст
Атрибут alt обязателен у каждого <img>. Но значение зависит от роли картинки:
- Картинка несёт смысл (график, фото товара, иконка-ссылка) —
altописывает суть:alt="График роста выручки за 2024 год". - Картинка декоративная (фоновый узор, разделитель) — пустой
alt="". Именно пустой, а не отсутствующий: тогда скринридер её пропустит. - Иконка внутри кнопки без текста — подпись даёт сама кнопка через
aria-label.
Контраст текста проверяется по числам, а не на глаз. WCAG AA требует коэффициента не ниже 4.5:1 для обычного текста и 3:1 для крупного (от 18pt или 14pt жирного). Светло-серый текст на белом фоне — самая частая провальная пара. Проверяйте контраст в DevTools: в панели инспектора элемента рядом с цветом показывается коэффициент и галочки AA/AAA.
Что чем платит: сравнение подходов
| Задача | Нативный HTML | div + ARIA |
|---|---|---|
| Кнопка | <button> — роль, фокус, Enter/Space бесплатно | role, tabindex, обработчик клавиш вручную |
| Чекбокс | <input type=checkbox> — состояние читается само | role=checkbox + aria-checked + управление стрелками |
| Ссылка-навигация | <a href> — работает в новой вкладке, копируется | onclick теряет все возможности ссылки |
| Раскрывающийся блок | <details>/<summary> — работает без JS | кнопка + aria-expanded + свой JS |
Вывод из таблицы один: ARIA нужна там, где нативного элемента нет (табы, сложные виджеты). Для базовых элементов она добавляет работу и риск ошибиться — например, aria-checked, который забыли обновить в JS, вреднее отсутствия роли.
Когда что выбирать
- Есть подходящий тег — берите его. Кнопка, ссылка, чекбокс,
details,nav,main, заголовкиh1–h6по иерархии. - Нативного элемента нет (вкладки, аккордеон с анимацией, кастомный селект) — берите ARIA-паттерн из авторского руководства WAI-ARIA, а не придумывайте роли сами.
- Динамика (уведомления, счётчики, загрузка) — область
aria-live, чтобы скринридер объявил изменение без перевода фокуса.
Что ломается чаще всего
- Пропущенные уровни заголовков. После
h1сразуh3— скринридер строит по заголовкам оглавление, дыры сбивают навигацию. Уровень отражает вложенность, а не размер шрифта. - Кнопка-иконка без подписи. «Крестик» закрытия без текста читается как «button». Добавьте
aria-label="Закрыть". - Модалка без ловушки фокуса. Открыли окно — а Tab уводит на фоновую страницу под ним. Фокус нужно держать внутри и возвращать на кнопку-открывашку при закрытии.
- Только цвет как признак. «Обязательные поля выделены красным» — не сработает для дальтоников. Дублируйте текстом или значком.
- Пустая ссылка вокруг иконки.
<a href>без текста и безaria-labelскринридер прочитает как сам URL.
Что делать дальше
Начните с трёх бесплатных проверок на своём проекте: пройдите ключевой сценарий с клавиатуры без мыши; прогоните страницы через axe DevTools или Lighthouse (вкладка Accessibility в Chrome DevTools даёт цифру и список нарушений); включите штатный скринридер (VoiceOver на macOS, NVDA на Windows) и послушайте, как читается форма. Автоматические проверки ловят около трети проблем — контраст, отсутствие alt, дубли id. Клавиатуру и логику озвучивания придётся проверять руками. Следующий шаг после базы — разобрать aria-live для динамического контента и ARIA-паттерны для кастомных виджетов из авторского руководства WAI-ARIA.
Субтитры и таймкоды: Расшифровка речи и субтитры по правилам читаемости: 42 знака в строке, две строки, минимум секунда на реплику.
Спросить за 40 ₽Источники: Web Content Accessibility Guidelines (WCAG) 2.1 — W3C, ARIA Authoring Practices Guide — W3C WAI
Частые вопросы
С чего начать, если сайт уже большой и переписывать некогда?
Обязательно ли добавлять ARIA-атрибуты везде?
А если у меня старый браузер не поддерживает :focus-visible?
Placeholder может заменить label?
Как понять, что контраст текста достаточный?
Достаточно ли пройти автоматический аудит, чтобы сайт был доступен?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.