Перейти к содержимому
v2.5.0
English
Русский
Документация

Dialog

Dialog — составной компонент для модальных оверлеев с частями корня, триггера и содержимого.

Именование

DialogContent не задаёт себе имя сам. Назвать диалог — ваша задача, и это обязательно: диалог без имени недоступен, потому что скринридеры объявляют его просто как «диалог».

Рекомендуемый паттерн:

  • видимый заголовок с id, на который ссылается aria-labelledby на DialogContent
  • aria-label на DialogContent, если видимого заголовка нет
  • aria-describedby на предложение, которое объясняет последствия, чтобы его объявили вместе с именем
confirm-action.tsx · 88 строк
import { useId, useState, type CSSProperties } from 'react';
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Heading,
  Icon,
  IconButton,
  Text,
} from '@vega-ui/react';

Работа с клавиатурой

ОбъявлениеПереведите фокус на демо и нажмите клавишу из таблицы.
КлавишаДействие
EnterSpaceНа триггере: открывает диалог. Фокус получает сам диалог или дочерний элемент с autoFocus.
TabShift + TabПереносит фокус на следующий или предыдущий элемент управления внутри диалога. Страница за ним инертна, и фокус на неё не попадает.
EscapeЗакрывает диалог и возвращает фокус на элемент, который его открыл. С closedBy='none' ничего не делает.
Каждая строка проверена в настоящем браузере: Playwright нажимает клавишу в Chromium и смотрит, где оказались фокус и активный элемент.

Во вложенных диалогах Escape закрывает только верхний.

Поведение фокуса

  • при открытии фокус должен попадать в диалог
  • при закрытии фокус должен возвращаться на осмысленный триггер
  • в стеке вложенных диалогов восстановление фокуса должно оставаться предсказуемым

Управление фокусом обеспечивает нативный <dialog>: пока диалог открыт, остальная страница инертна, а при закрытии фокус возвращается на элемент, который был в фокусе до открытия. В Safari клик мышью не переводит фокус на кнопку, поэтому после закрытия диалога, открытого мышью, фокус возвращается на body страницы; при активации с клавиатуры он возвращается на триггер.

При открытии фокус получает сам диалог, поэтому ничего внутри не подсвечивается, а скринридеры объявляют его заголовок; Tab переводит фокус на первый контрол. Чтобы сфокусировать дочерний элемент, поставьте на него autoFocus — например, на первое поле формы.

Семантика для скринридеров

  • браузер представляет диалог как role=dialog с aria-modal, а имя он получает из вашего aria-labelledby или aria-label
  • триггер выставляет aria-haspopup='dialog' и aria-expanded
  • содержимое должно восприниматься как единый модальный блок
  • контролы закрытия должны оставаться явными, когда закрытие важно; у DialogClose нет метки по умолчанию, поэтому кнопке-иконке внутри него нужен aria-label; библиотека не поставляет встроенных строк

Семантика формы

Если в диалоге есть поля, относитесь к нему как к обычной границе формы и перепроверяйте отправку с клавиатуры, закрытие и возврат фокуса в реальном сценарии.

Кнопки в <form method="dialog"> закрывают диалог через onChangeOpen(false), а value кнопки отправки становится returnValue диалога — как у нативного <dialog>. Это работает и с closedBy='none', когда Escape и подложка диалог не закрывают.

Риски доступности

  • открытие диалога без aria-labelledby или aria-label
  • вложенные диалоги без повторной проверки восстановления фокуса
  • длинное содержимое в диалоге без рабочей стратегии прокрутки
  • закрытие важных сценариев только кликом снаружи