Перейти к содержимому
Pandus

Доступное модальное окно

Обновлено 14 октября 2026 г. · 6 мин чтения

Всплывающие окна подписки, быстрый просмотр, таблицы размеров, наложения «добавлено в корзину», формы входа, настройки cookie — интернет-магазины полны модальных окон, и они — одна из самых частых причин, по которой пользователи клавиатуры и экранных дикторов застревают. На этой странице показан правильный паттерн. Она входит в серию доступных UI-паттернов.

Когда использовать модальное окно

В APG диалог определён как «окно, наложенное на основное окно или другое диалоговое окно», и сказано: «окна под модальным диалогом неинтерактивны» — пользователи не могут взаимодействовать со страницей, пока диалог не закрыт. Используйте модальное окно, когда пользователь должен завершить задачу или отклонить её, прежде чем идти дальше: подтвердить удаление, войти, выбрать вариант в быстром просмотре. Для информации, которая не блокирует задачу (сообщение «Добавлено в корзину»), обычно менее навязчиво немодальное сообщение или сообщение о состоянии.

Применимые критерии WCAG

Пример кода

<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.

Только если он сделан как модальное окно. Немодальный баннер не должен скрывать элементы в фокусе: 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