Dialog
Dialog временно останавливает работу с основным экраном, удерживает фокус внутри панели и возвращает его после закрытия. Панель собрана как остров: плашка заголовка сверху, тело с собственным отступом и прокруткой, полоса действий внизу.
Статус: experimental · в системе с 2026-07 · интерактивность: always (всегда)
Когда использовать
- Локальная задача требует сосредоточиться и завершить действие до возврата на экран.
- Необратимое или дорогое действие требует явно назвать объект и последствие.
Не использовать:
- Короткое пояснение не содержит действий — используй Tooltip.
- Небольшая интерактивная панель не должна блокировать экран — используй Popover.
- Сообщение относится к конкретной области — используй Alert.
Альтернативы:
Обычная задача
Dialog с trigger, описанием, содержимым и одним главным действием.
<!--
🟢
Базовый Dialog объясняет локальную задачу и предлагает явное завершение.
Крестик, Escape, внешний клик и кнопка действия закрывают панель.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Button from "$lib/ui/Button.svelte";
import Dialog from "$lib/ui/Dialog.svelte";
</script>
<Dialog
title={t("Параметры отчёта")}
description={t("Настройки применятся только к следующему экспорту.")}
>
{#snippet trigger({ props })}
<Button {...props}>{t("Открыть параметры")}</Button>
{/snippet}
<p>{t("Выберите параметры в форме продукта и подтвердите изменение.")}</p>
{#snippet actions({ close })}
<Button variant="accent" onclick={close}>{t("Готово")}</Button>
{/snippet}
</Dialog>
Управляемое состояние
Внешний сценарий открывает панель и читает bindable open.
Диалог закрыт.
<!--
🟢
Управляемый пример связывает open Dialog с внешним состоянием страницы.
Отдельная кнопка открывает панель, а закрытие синхронно обновляет подпись.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Button from "$lib/ui/Button.svelte";
import Dialog from "$lib/ui/Dialog.svelte";
let open = $state(false);
</script>
<Button onclick={() => (open = true)}>{t("Открыть управляемый Dialog")}</Button>
<p aria-live="polite">{open ? t("Диалог открыт.") : t("Диалог закрыт.")}</p>
<Dialog
bind:open
title={t("Управляемое состояние")}
description={t("Родитель читает и изменяет bindable-проп open.")}
>
<p>{t("Состояние остаётся единым для trigger, Escape и программного закрытия.")}</p>
{#snippet actions({ close })}
<Button onclick={close}>{t("Закрыть")}</Button>
{/snippet}
</Dialog>
Подтверждение разрушения
Alertdialog называет объект и не закрывается случайным внешним кликом.
Проект существует.
<!--
🟢
AlertDialog подтверждает действие с реальной ценой ошибки.
Заголовок называет объект, а danger-кнопка повторяет конкретный результат.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Button from "$lib/ui/Button.svelte";
import Dialog from "$lib/ui/Dialog.svelte";
let result = $state(t("Проект существует."));
</script>
<Dialog
role="alertdialog"
size="s"
title={t("Удалить проект «Atlas»?")}
description={t("Участники потеряют доступ, восстановить проект не получится.")}
>
{#snippet trigger({ props })}
<Button tone="danger" {...props}>{t("Удалить проект")}</Button>
{/snippet}
{#snippet actions({ close })}
<Button onclick={close}>{t("Отмена")}</Button>
<Button
variant="accent"
tone="danger"
onclick={() => {
result = t("Проект удалён.");
close();
}}
>
{t("Удалить проект")}
</Button>
{/snippet}
</Dialog>
<p aria-live="polite">{result}</p>
Положение, булавка, стрелки
position="top", pinnable с закреплением без затемнения, persistent, footerVertical и стрелки onprev / onnext по бокам панели.
Модально
<!--
🟢
Положение, булавка и стрелки (эталон: Vue-легаси structures/modal
position / pinnable / closePrevent / footerVertical / prev-block,
next-block): панель у верха, закрепление без затемнения, переход между
сущностями стрелками по бокам.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Button from "$lib/ui/Button.svelte";
import Dialog from "$lib/ui/Dialog.svelte";
let open = $state(false);
let pinned = $state(false);
let index = $state(1);
const total = 5;
</script>
<div style="display: flex; gap: var(--sp-2); align-items: center;">
<Dialog bind:open bind:pinned title={`${t("Кампания")} ${index} ${t("из")} ${total}`} position="top" pinnable footerVertical onprev={() => (index = Math.max(1, index - 1))} onnext={() => (index = Math.min(total, index + 1))}>
{#snippet trigger({ props })}
<Button {...props} variant="accent">{t("Открыть карточку")}</Button>
{/snippet}
{#snippet children()}
<p style="margin: 0; font-size: var(--fs-s);">{t("Стрелки по бокам листают сущности, булавка в шапке закрепляет панель у угла и снимает затемнение.")}</p>
{/snippet}
{#snippet actions({ close })}
<Button variant="accent" onclick={close}>{t("Сохранить")}</Button>
<Button variant="default" onclick={close}>{t("Отмена")}</Button>
{/snippet}
</Dialog>
<span aria-live="polite">{pinned ? t("Закреплено") : t("Модально")}</span>
</div>