Как семантично сверстать попап
Что проверяют
Знаете ли вы нативный <dialog> и понимаете ли, что семантика попапа это прежде всего управление фокусом и понятность для скринридера, а не теги ради тегов. Следом спрашивают, чем модальное окно отличается от немодального и как закрыть попап кликом по фону.
Как отвечать
Базовый выбор сегодня <dialog>:
showModal(): открывает модальное окно в top layer. Остальная страница становится инертной, фокус переходит внутрь, Esc закрывает окно, фон стилизуется через::backdrop. Методshow()открывает немодальный вариант;- имя окна:
aria-labelledbyссылается на заголовок внутри,aria-describedbyна пояснение. Скринридер объявит диалог и его название; - начальный фокус: атрибут
autofocusна нужном элементе. В подтверждении удаления это кнопка «Отмена», а не «Удалить»; - закрытие:
<form method="dialog">внутри закрывает окно без скриптов и кладёт значение нажатой кнопки вdialog.returnValue. Крестик без текста получаетaria-label; - клик по фону: по умолчанию окно так не закрывается. Его ловят обработчиком на самом
dialog, сравниваяevent.target, или атрибутомclosedby="any"там, где браузер его поддерживает.
Для немодальных подсказок и меню подходит Popover API: атрибут popover и кнопка с popovertarget дают открытие, закрытие по Esc и по клику мимо без строчки JS. Разметку на div с role="dialog" и aria-modal="true" упомяните как запасной путь, где ловушку фокуса и inert на остальной странице придётся писать самим.
Где спотыкаются
Открывают окно атрибутом open вместо showModal(). Получается немодальный блок без фона, без блокировки страницы и без закрытия по Esc.
Пишут dialog { display: flex }. Это перебивает стандартное скрытие, и закрытое окно остаётся на странице. Правило нужно вешать на dialog[open].
Не блокируют прокрутку под модалкой, и страница уезжает при скролле внутри окна. Хватает одной строки вроде body:has(dialog[open]) { overflow: hidden }.