Доступное модальное окно
Обновлено 14 октября 2026 г. · 6 мин чтения
Всплывающие окна подписки, быстрый просмотр, таблицы размеров, наложения «добавлено в корзину», формы входа, настройки cookie — интернет-магазины полны модальных окон, и они — одна из самых частых причин, по которой пользователи клавиатуры и экранных дикторов застревают. На этой странице показан правильный паттерн. Она входит в серию доступных UI-паттернов.
Когда использовать модальное окно
В APG диалог определён как «окно, наложенное на основное окно или другое диалоговое окно», и сказано: «окна под модальным диалогом неинтерактивны» — пользователи не могут взаимодействовать со страницей, пока диалог не закрыт. Используйте модальное окно, когда пользователь должен завершить задачу или отклонить её, прежде чем идти дальше: подтвердить удаление, войти, выбрать вариант в быстром просмотре. Для информации, которая не блокирует задачу (сообщение «Добавлено в корзину»), обычно менее навязчиво немодальное сообщение или сообщение о состоянии.
Применимые критерии WCAG
- 2.1.1 Клавиатура (A) и 2.1.2 Отсутствие клавиатурной ловушки (A): окном можно пользоваться и закрыть его с клавиатуры. Удерживать фокус внутри модального окна нормально, пока пользователь может его покинуть.
- 2.4.3 Порядок фокуса (A): фокус перемещается в окно, а потом возвращается на кнопку, открывшую его.
- 2.4.11 Фокус не скрыт (минимум) (AA): W3C отмечает, что правильно сделанное модальное окно всегда проходит, а немодальные наложения могут скрыть элемент в фокусе.
- 4.1.2 Имя, роль, значение (A): роль dialog и имя.
- 1.3.1 Информация и взаимосвязи (A): заголовок — это heading, структура есть в коде.
- 1.4.10 Перекомпоновка (AA): окно помещается на узком экране и при масштабе 400 % без двумерной прокрутки.
- 2.2.1 Регулируемое время (A): всплывающие окна не должны закрываться по таймеру раньше, чем пользователи успеют их прочитать.
Пример кода
<button type="button" id="open-size-guide">Таблица размеров</button>
<dialog id="size-guide" aria-labelledby="size-guide-title">
<h2 id="size-guide-title">Таблица размеров</h2>
<table>…</table>
<button type="button" id="close-size-guide">Закрыть</button>
</dialog>
<script>
const dialog = document.getElementById('size-guide');
document.getElementById('open-size-guide')
.addEventListener('click', () => dialog.showModal());
document.getElementById('close-size-guide')
.addEventListener('click', () => dialog.close());
</script>
Что делает нативный элемент, согласно стандарту HTML:
showModal()помещает диалог в верхний слой, блокирует документ так, что остальное становится неинтерактивным, запоминает ранее сфокусированный элемент и выполняет шаги фокусировки диалога, учитывающие атрибутautofocus.- Запрос на закрытие (например, Escape) вызывает отменяемое событие
cancel, а затем закрывает диалог. Атрибутclosedbyуправляет тем, закрывают ли его ещё и щелчки снаружи. close()возвращает фокус на ранее сфокусированный элемент, если диалог был модальным.
Если вы делаете диалог сами с помощью ARIA, следуйте APG: у контейнера role="dialog", aria-modal="true" и aria-labelledby, указывающий на видимый заголовок; aria-describedby необязателен и лучше опустить, когда содержимое включает списки или таблицы. Тогда вам придётся самим делать фон неинтерактивным, удерживать Tab внутри диалога, обрабатывать Escape и возвращать фокус.
Работа с клавиатуры
Из шаблона диалога (модального) APG:
| Клавиша | Действие |
|---|---|
| Tab | Следующий элемент внутри диалога, до которого можно дойти по Tab; с последнего — снова на первый |
| Shift + Tab | Предыдущий элемент внутри диалога; с первого — на последний |
| Escape | Закрывает диалог |
Фокус при открытии: «фокус перемещается на элемент внутри диалога», как правило на первый элемент, получающий фокус. Фокус при закрытии: он «возвращается на элемент, который вызвал диалог», если только этот элемент больше не существует — тогда на другой элемент, сохраняющий логичный порядок работы. После «Удалить товар», например, фокус может перейти на заголовок корзины.
Частые ошибки в интернет-магазинах
- Всплывающее окно подписки при загрузке страницы, которое забирает фокус, не имеет имени и кнопки закрытия, доступной с клавиатуры.
- Закрывающий «×» без доступного имени или значок закрытия в виде
div. - Фокус остаётся за наложением: Tab перемещает по странице под тёмным фоном.
- Нет Escape и нет видимого способа закрыть на мобильном, когда диалог длиннее экрана.
- Фокус теряется после закрытия и перепрыгивает в начало страницы вместо кнопки «Быстрый просмотр».
- Диалоги быстрого просмотра, перестраивающие страницу товара с дублирующимися ID и вложенными формами.
- Баннеры cookie, которые не модальны и при этом не убраны с дороги, закрывая элемент в фокусе внизу экрана, — по мнению W3C, это нарушение 2.4.11.
Что находит автоматика и что остаётся для ручной проверки
| Автоматическая (правило axe-core) | Что находит |
|---|---|
aria-dialog-name |
role="dialog" или alertdialog без имени |
button-name |
Кнопки закрытия без имени |
aria-hidden-focus |
Элементы, получающие фокус, внутри содержимого, скрытого через aria-hidden="true" |
aria-hidden-body |
aria-hidden="true" на элементе body |
aria-valid-attr-value |
aria-labelledby, указывающий на несуществующий ID |
color-contrast, target-size |
Низкий контраст и мелкие цели внутри открытого диалога |
Все они — карточки в нашей библиотеке правил. Еженедельное сканирование видит страницу такой, какой она загружается: закрытое окно не проверяется, автоматически открывающееся — проверяется. С помощью расширения для браузера можно открыть окно, а затем запустить проверку. Ручная проверка: откройте окно с клавиатуры, пройдите по нему Tab до конца и обратно, нажмите Escape, посмотрите, куда попадает фокус, увеличьте до 400 % и послушайте, как его объявляет экранный диктор. Шаги описаны в материале как проверить WCAG.
Запустите бесплатную проверку одной страницы. Это автоматическая проверка, а не полный аудит.
Частые вопросы
Что делает модальное окно доступным?
При открытии фокус перемещается в окно, Tab и Shift + Tab остаются внутри него, Escape закрывает его, фокус возвращается на кнопку, которая его открыла, содержимое позади неинтерактивно (inert), а у окна есть имя из видимого заголовка.
Стоит ли использовать HTML-элемент dialog?
Для модального окна элемент dialog, открытый через showModal(), выполняет значительную часть работы: по стандарту HTML он помещается в верхний слой, делает остальную часть документа неинтерактивной, управляет начальным фокусом и закрывается по запросу на закрытие, например по Escape. Проверьте его в браузерах и с экранными дикторами, которыми пользуются ваши покупатели.
Куда должен попасть фокус при открытии окна?
В APG сказано, что фокус перемещается на элемент внутри окна, как правило на первый элемент, получающий фокус. Для длинного текста лучше сфокусироваться на начале содержимого. В элементе dialog это выбирается атрибутом autofocus.
Является ли баннер cookie модальным окном?
Только если он сделан как модальное окно. Немодальный баннер не должен скрывать элементы в фокусе: W3C говорит, что закреплённый баннер cookie нарушает 2.4.11 «Фокус не скрыт», если он полностью закрывает компонент, получающий фокус.
Может ли автоматическая проверка проверить модальное окно?
Отчасти. Она находит окна без имени, кнопки закрытия без имени и элементы, получающие фокус, скрытые через aria-hidden. Проверить перемещение фокуса, ловушку фокуса или Escape она не может, а закрытое окно не проверяется вовсе.
Источники
- W3C WAI, APG Dialog (Modal) pattern: https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
- WHATWG, HTML Living Standard, the dialog element: https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element
- W3C, Understanding 2.4.11 Focus Not Obscured (Minimum): https://www.w3.org/WAI/WCAG22/Understanding/focus-not-obscured-minimum.html