Alert Dialog
Overlaysimport { AlertDialog } from "bits-ui" Destructive confirmation
Cancel exits without committing; Action confirms the irreversible operation
No action taken
<script lang="ts">
import { AlertDialog } from 'bits-ui';
import { Trash2 } from '@lucide/svelte';
let result = $state('No action taken');
</script>
<div class="flex flex-col items-center gap-3">
<AlertDialog.Root>
<AlertDialog.Trigger class="btn border border-red-400/30 bg-red-500/10 text-red-200 hover:bg-red-500/20">
<Trash2 class="size-4" />
Delete environment
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Overlay class="fixed inset-0 z-[90] bg-ink-950/80 backdrop-blur-sm" />
<AlertDialog.Content
class="panel animate-pop-in fixed top-1/2 left-1/2 z-[100] w-[min(30rem,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 p-6 shadow-2xl outline-none"
>
<div class="flex items-start gap-4">
<div class="flex size-10 shrink-0 items-center justify-center rounded-full bg-red-500/15 text-red-300">
<Trash2 class="size-5" />
</div>
<div>
<AlertDialog.Title class="text-lg font-semibold text-ink-50">
Delete production environment?
</AlertDialog.Title>
<AlertDialog.Description class="mt-2 text-sm leading-6 text-ink-400">
This permanently deletes 14 services, their deployment history, and all environment secrets. This cannot be undone.
</AlertDialog.Description>
</div>
</div>
<div class="mt-6 flex justify-end gap-2">
<AlertDialog.Cancel class="btn btn-ghost" onclick={() => (result = 'Deletion cancelled')}>
Cancel
</AlertDialog.Cancel>
<AlertDialog.Action
class="btn bg-red-500 text-white hover:bg-red-400"
onclick={() => (result = 'Environment deleted')}
>
Delete permanently
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
<p class="text-xs text-ink-400" aria-live="polite">{result}</p>
</div>
Controlled state
An external control opens the alert while Cancel and Action retain distinct semantics
Analytics workspace
Current access: Editor
<script lang="ts">
import { AlertDialog } from 'bits-ui';
let open = $state(false);
let access = $state('Editor');
</script>
<div class="flex flex-col items-center gap-3">
<div class="card flex w-full max-w-md items-center justify-between gap-4 p-4">
<div>
<p class="text-sm font-medium text-ink-50">Analytics workspace</p>
<p class="mt-0.5 text-xs text-ink-400">Current access: {access}</p>
</div>
<button class="btn btn-ghost btn-sm" onclick={() => (open = true)}>Reset access</button>
</div>
<AlertDialog.Root bind:open>
<AlertDialog.Portal>
<AlertDialog.Overlay class="fixed inset-0 z-[90] bg-ink-950/80 backdrop-blur-sm" />
<AlertDialog.Content
class="panel animate-pop-in fixed top-1/2 left-1/2 z-[100] w-[min(28rem,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 p-6 shadow-2xl outline-none"
>
<AlertDialog.Title class="text-lg font-semibold text-ink-50">Reset workspace access?</AlertDialog.Title>
<AlertDialog.Description class="mt-2 text-sm leading-6 text-ink-400">
The member will lose edit access immediately and return to the Viewer role. Existing dashboards are unchanged.
</AlertDialog.Description>
<div class="mt-6 flex justify-end gap-2">
<AlertDialog.Cancel class="btn btn-ghost">Keep editor access</AlertDialog.Cancel>
<AlertDialog.Action class="btn btn-primary" onclick={() => (access = 'Viewer')}>
Reset to viewer
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
</div>
AlertDialog.Portal renders into document.body by default, so a high
z-index on the portalled overlay/content clears page-level stacking contexts. Alert dialogs trap
focus and lock body scrolling; unlike a regular dialog, initial focus lands on the content so a
screen reader announces the warning. Focus returns to the trigger when the alert closes.
Escape closes by default, but outside interaction is ignored by default to prevent accidental
confirmation dismissal. Those policies are configurable through escapeKeydownBehavior and interactOutsideBehavior on AlertDialog.Content. Use Cancel for the no-op path and Action only for the committing path; both close the alert.