Диалог
Диалог — модальное окно поверх страницы: его завершают или закрывают, прежде чем вернуться к странице. tk-dialog вмещает короткую задачу, tk-confirm-dialog подтверждает одно действие.
<tk-button onclick="document.getElementById('dialog').show()">Rename project</tk-button>
<tk-dialog id="dialog" heading="Rename project">
<tk-field label="Project name"><tk-input value="Aurora"></tk-input></tk-field>
<tk-button slot="footer" onclick="this.closest('tk-dialog').close()">Cancel</tk-button>
<tk-button slot="footer" variant="primary" onclick="this.closest('tk-dialog').close()">Rename</tk-button>
</tk-dialog>
<tk-button tone="danger" onclick="document.getElementById('confirm').show()">Delete project</tk-button>
<tk-confirm-dialog id="confirm" heading="Delete project?" message="This permanently deletes 'Aurora' and all its data." confirm-label="Delete project" danger></tk-confirm-dialog>Анатомия
| Часть | tk-dialog |
tk-confirm-dialog |
|---|---|---|
| заголовок | heading: заголовок и доступное имя, обязателен |
heading: конкретный вопрос, обязателен |
| кнопка закрытия | встроена, справа вверху | нет; выход — «Отмена» |
| тело | слот по умолчанию; прокручивается | message: одна строка о последствиях |
| подвал | слот footer: ваши кнопки, по правому краю |
«Отмена» и кнопка подтверждения, встроены |
| подложка | затемняет страницу и блокирует её | так же |
part dialog |
панель | панель |
Варианты
<tk-button tone="danger" onclick="document.getElementById('delete').show()">Delete project</tk-button>
<tk-confirm-dialog id="delete" heading="Delete project?" message="This permanently deletes Aurora and all its data." confirm-label="Delete" danger></tk-confirm-dialog>tk-dialog: задача с содержимым. Тело и подвал заполняете вы.tk-confirm-dialog: подтверждает одно действие. Настраивается только атрибутами: нет слотов, кнопки закрытия и прокрутки, ширина всегда 320px.confirm-labelобязателен: без него будет только «Отмена».danger: для действия, которое удаляет или которое нельзя отменить. Кнопка подтверждения становитсяопасной кнопкой , красноватой с красным текстом, а фокус встаёт на «Отмену». Безdangerкнопка подтверждения —primary, и фокус на ней.
Если потеря велика, добавьте трение в tk-dialog: держите опасную кнопку отключённой, пока
пользователь не отметит
Размеры
size |
Ширина | Для чего |
|---|---|---|
small |
320px | короткие решения с небольшим содержимым |
medium (по умолчанию) |
480px | большинство форм и задач |
large |
640px | широкое содержимое, например форма в две колонки или таблица |
- Диалог не выходит за окно и оставляет
space-16(16px) с каждой стороны, вплоть до окна 320 × 256px. - Длинное содержимое прокручивается в теле, а заголовок и подвал стоят на месте. У края, за которым есть ещё содержимое, оно плавно гаснет. Вбок ничего не прокручивается: возьмите размер шире.
- У
tk-confirm-dialogнетsize: ширина 320px и прокрутки нет, поэтому пишите коротко.
Открытие и закрытие
<tk-button onclick="document.getElementById('rename').show()">Rename project</tk-button>
<tk-dialog id="rename" heading="Rename project">
<form id="rename-form">
<tk-field label="Project name"><tk-input name="name" value="Aurora" autofocus></tk-input></tk-field>
</form>
<tk-button slot="footer" onclick="this.closest('tk-dialog').close()">Cancel</tk-button>
<tk-button slot="footer" variant="primary" type="submit" form="rename-form">Rename</tk-button>
</tk-dialog>const dialog = document.getElementById('rename');
dialog.querySelector('form').addEventListener('submit', (event) => {
event.preventDefault();
// save the name, then:
dialog.close('renamed');
});show()открывает диалог, всегда модально.close(value)закрывает его, а в обработчикеcloseзначение лежит вreturnValue. Фокус возвращается к элементу, который открыл диалог.- Подвал находится вне вашей формы, поэтому дайте кнопке отправки
formс id формы.<form method="dialog">диалог не закрывает: вызовитеclose()при отправке. - Поставьте
autofocusна первое поле.
| Как закрыть | tk-dialog |
tk-confirm-dialog |
|---|---|---|
Esc |
закрывает | закрывает, как «Отмена» |
| кнопка закрытия | закрывает | — |
| клик по подложке | закрывает, если в диалоге нет <form> |
ничего не делает |
| «Отмена» | закрывает; подключаете вы | закрывает |
| главное действие | подключаете вы | закрывает с returnValue "confirm" |
- Закрытие ничего не сохраняет. Любой выход, кроме главного действия, оставляет
returnValueравным""вtk-dialogи"cancel"вtk-confirm-dialog. persistentотключаетEscи подложку. Кнопка закрытия и кнопки диалога работают по-прежнему. Оставьте это для редких случаев, например принятия юридических условий.- Не больше одного подтверждения поверх одного диалога.
Escзакрывает верхний.
Несохранённые изменения
Esc и клик по подложке сначала отправляют событие cancel, и preventDefault() оставляет
диалог открытым. Так можно спросить, прежде чем введённое пропадёт. Кнопка закрытия и
«Отмена» cancel не отправляют.
<tk-confirm-dialog id="discard" heading="Discard changes?" message="Your edits will be lost." cancel-label="Continue editing" confirm-label="Discard changes" danger></tk-confirm-dialog>
<script>
const dialog = document.getElementById('rename');
const discard = document.getElementById('discard');
dialog.addEventListener('cancel', (event) => {
if (!hasUnsavedChanges()) return;
event.preventDefault();
discard.show();
});
discard.addEventListener('close', () => {
if (discard.returnValue === 'confirm') dialog.close();
});
</script>Ожидание ответа сервера
preventDefault() на клике по кнопке подтверждения держит tk-confirm-dialog открытым, пока
идёт ваш запрос.
const deleteDialog = document.getElementById('delete');
deleteDialog.addEventListener('click', async (event) => {
const button = event.composedPath().find((node) => node.dataset?.action === 'confirm');
if (!button) return;
event.preventDefault();
button.loading = true;
await deleteProject();
button.loading = false;
deleteDialog.close('confirm');
});Когда использовать
tk-dialog— для короткой задачи, которую нужно закончить прежде всего остального, например небольшой формы или решения, которому нужен контекст.tk-confirm-dialog— перед редким действием, которое нельзя отменить: удалить, сбросить, отключить.- Не подтверждайте частое действие, которое можно отменить. Выполните его и предложите
отмену в
Toast . - Длинному сценарию, большой форме, повторяющейся задаче и содержимому, которое держат открытым, нужна страница.
- Никогда не открывайте диалог при загрузке страницы и ради информации, с которой ничего не сделать.
- Для панели, которая выезжает с края, берите
Sheet . Чтобы выбрать значение, беритеSelect илиCombobox : они не блокируют страницу.
Текст
- Заголовок: глагол и объект, конкретный вопрос или утверждение: «Удалить проект?», «Переименовать проект». Никогда «Вы уверены?» или «Ошибка».
- Повторяйте то, что открыло диалог: пункт меню «Удалить проект…» открывает «Удалить проект?» с кнопкой «Удалить».
- Сообщение: последствия, и только если они что-то добавляют. Полные предложения, главное в начале. Не объясняйте кнопки.
- Кнопка подтверждения называет действие: «Удалить», «Закрыть доступ». Никогда «ОК», «Да» или «Готово».
- «Отмена» остаётся «Отменой». Единственное исключение — пара для несохранённых изменений: «Продолжить редактирование» и «Сбросить изменения».
- Обычно в подвале две кнопки, максимум три. Одна
primary, справа, и «Отмена» сразу перед ней. - Заглавная буква только в начале.
Подробнее в
Можно / нельзя
| ✅ Можно | ❌ Нельзя |
|---|---|
| Подтверждать редкое действие, которое нельзя отменить | Подтверждать частое действие; лучше предложите отмену |
| «Удалить» на кнопке подтверждения | «ОК» или «Да» |
| В подтверждении только заголовок, одна строка и две кнопки | Поля или длинный текст в подтверждении |
Спрашивать «Сбросить изменения?» через cancel |
Включать persistent, чтобы защитить форму |
| Не больше одного подтверждения поверх диалога | Ставить диалоги друг на друга, чтобы пройти шаги |
| Открывать диалог, когда его просят | Открывать его при загрузке страницы |
Связанные
Button : кнопки подвала и опасный тон.Icon button : встроенная кнопка закрытия.Menu : её опасные пункты открывают подтверждение.Sheet : та же модальная панель, но выезжает с края.Toast : отмена для действий, которые вы не подтверждаете.Checkbox иInput : для подтверждения с дополнительным трением.
API
Атрибуты
tk-dialog:
| Атрибут | Тип | По умолчанию | Что делает |
|---|---|---|---|
heading |
string | нет | заголовок и доступное имя; обязателен |
size |
small | medium | large |
medium |
ширина 320 / 480 / 640px |
persistent |
boolean | false |
Esc и клик по подложке не закрывают диалог |
backdrop-blur |
boolean | false |
размывает страницу под подложкой |
tk-confirm-dialog:
| Атрибут | Тип | По умолчанию | Что делает |
|---|---|---|---|
heading |
string | нет | вопрос; обязателен |
message |
string | нет | строка о последствиях; читается как описание |
confirm-label |
string | нет | текст кнопки подтверждения; обязателен |
cancel-label |
string | Cancel |
текст кнопки «Отмена» |
danger |
boolean | false |
опасная кнопка подтверждения; фокус встаёт на «Отмену» |
persistent |
boolean | false |
Esc не закрывает диалог |
backdrop-blur |
boolean | false |
размывает страницу под подложкой |
backdrop-blur выключен по умолчанию, потому что размытие нагружает браузер. Где браузер не
умеет размывать, подложка просто затемняет.
Слоты
| Слот | Содержимое |
|---|---|
| по умолчанию | тело tk-dialog |
footer |
кнопки tk-dialog, обычно tk-button |
У tk-confirm-dialog слотов нет.
Методы и свойства
| Имя | Что делает |
|---|---|
show() |
открывает диалог модально |
close(returnValue?) |
закрывает его; без значения закрывает как отказ |
returnValue |
значение последнего закрытия: "confirm" или "cancel" у диалога подтверждения |
open |
только для чтения, true, пока диалог открыт; атрибут open повторяет его, но ничего не открывает |
События
| Событие | Когда |
|---|---|
close |
после закрытия любым способом; смотрите returnValue |
cancel |
Esc или клик по подложке просят закрыть; preventDefault() оставляет диалог открытым |
Оба приходят на сам элемент и не всплывают, поэтому слушайте их на диалоге. У
tk-confirm-dialog cancel приходит только от Esc.
CSS
::part(dialog): панель.
Токены
| Часть | Токены |
|---|---|
| подложка | surface/backdrop; с backdrop-blur ещё blur-backdrop (8px) |
| панель | surface/raised, shadow-3, radius-20; в контрастной теме рамка 1px border/default |
| заголовок | text/default, Heading Medium (20/24) |
| тело, сообщение | text/default, Body Medium (16/24) |
| отступы | space-16 вокруг областей, space-8 между кнопками |
| кнопки | токены |
В тёмной и контрастной теме подложка темнее. Значения по темам:
Доступность
Встроено:
- Нативный модальный
<dialog>: по странице за ним нельзя кликнуть, до неё не доходит Tab, её не читает скринридер. role="dialog", у подтвержденияrole="alertdialog"; имя даёт заголовок.messageподтверждения служит его описанием.- При открытии фокус переходит внутрь, остаётся там и при закрытии возвращается к тому, что открыло диалог.
- Первый фокус: в
tk-dialog— элемент сautofocus, иначе заголовок. Вtk-confirm-dialog— «Отмена» приdanger, иначе кнопка подтверждения. - Пока диалог открыт, страница не прокручивается.
- Диалог появляется с коротким проявлением и масштабированием; при уменьшенной анимации — только проявление.
На вашей стороне:
- Поставьте
autofocusна первое поле формы. Никогда не начинайте со ссылки. - Если после закрытия элемента, открывшего диалог, уже нет, переведите фокус сами в
обработчике
close. - Живые области кладите внутрь диалога, а не на страницу за ним.
- Назовите объект в заголовке и действие на кнопке подтверждения.
| Клавиша | Действие |
|---|---|
Esc |
закрывает верхний слой: сначала открытое меню, select или combobox внутри диалога, потом сам диалог |
Tab / Shift+Tab |
перемещает фокус только внутри диалога |
Известные ограничения
- Пока диалог открыт, скринридеры показывают его шапку и подвал как ориентиры страницы (banner и contentinfo).