Skip to content
edmi-ui

Alert Dialog

A blocking confirm for destructive or irreversible actions. No close icon; clicking outside does nothing.

StackBlitz ↗StackBlitz ↗StackBlitz ↗
Terminal window
npx shadcn@latest add @edmi-ui/alert-dialog
Terminal window
pnpm dlx shadcn@latest add @edmi-ui/alert-dialog
Terminal window
yarn dlx shadcn@latest add @edmi-ui/alert-dialog
Terminal window
bunx shadcn@latest add @edmi-ui/alert-dialog

Pick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.

Terminal window
npx shadcn-vue@latest add @edmi-ui/alert-dialog
Terminal window
pnpm dlx shadcn-vue@latest add @edmi-ui/alert-dialog
Terminal window
yarn dlx shadcn-vue@latest add @edmi-ui/alert-dialog
Terminal window
bunx shadcn-vue@latest add @edmi-ui/alert-dialog

Pick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.

Terminal window
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/alert-dialog.json
Terminal window
pnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/alert-dialog.json
Terminal window
yarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/alert-dialog.json
Terminal window
bunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/alert-dialog.json

Pick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.

import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>
Close index
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Close this index?</AlertDialogTitle>
<AlertDialogDescription>
Holders can still withdraw, but no one can join. This cannot be
undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction variant="destructive">
Close index
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<script setup lang="ts">
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
</script>
<template>
<AlertDialog>
<AlertDialogTrigger as-child>
<Button variant="outline">Close index</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Close this index?</AlertDialogTitle>
<AlertDialogDescription>
Holders can still withdraw, but no one can join. This cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction variant="destructive">Close index</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</template>
<script lang="ts">
import * as AlertDialog from "$lib/components/ui/alert-dialog";
import { Button } from "$lib/components/ui/button";
</script>
<AlertDialog.Root>
<AlertDialog.Trigger>
{#snippet child({ props })}
<Button variant="outline" {...props}>Close index</Button>
{/snippet}
</AlertDialog.Trigger>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>Close this index?</AlertDialog.Title>
<AlertDialog.Description>
Holders can still withdraw, but no one can join. This cannot be undone.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
<AlertDialog.Action variant="destructive">Close index</AlertDialog.Action>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>
registry/ui/alert-dialog.tsx
"use client";
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
import { cn } from "cn";
import type * as React from "react";
import { Button } from "@/registry/edmi/ui/button";
import {
type Elevation,
SurfaceProvider,
useElevation,
} from "@/registry/edmi/ui/elevation";
// ✦ depth (v4): overlay role. Natural level is floating in layered mode; flat otherwise.
const overlayElevation = {
sunken: "",
flat: "",
raised: "border-transparent shadow-raised",
floating: "border-transparent shadow-floating",
};
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
}
function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
return (
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
);
}
function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
return (
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
);
}
function AlertDialogOverlay({
className,
...props
}: AlertDialogPrimitive.Backdrop.Props) {
return (
<AlertDialogPrimitive.Backdrop
data-slot="alert-dialog-overlay"
className={cn(
"fixed inset-0 isolate z-50 bg-overlay transition-opacity duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0",
className,
)}
{...props}
/>
);
}
function AlertDialogContent({
className,
size = "default",
elevation,
children,
...props
}: AlertDialogPrimitive.Popup.Props & {
size?: "default" | "sm";
/** ✦ depth: flat 0, raised +1 (bevel), floating +2 (bevel + drop). */
elevation?: Elevation;
}) {
const level = useElevation(elevation, "overlay");
return (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Popup
data-slot="alert-dialog-content"
data-size={size}
className={cn(
"group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-2xl border border-border bg-popover p-[22px] text-popover-foreground outline-none transition-[opacity,scale] duration-150 data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:scale-95 data-[starting-style]:opacity-0 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm",
overlayElevation[level],
className,
)}
{...props}
>
<SurfaceProvider level={level}>{children}</SurfaceProvider>
</AlertDialogPrimitive.Popup>
</AlertDialogPortal>
);
}
function AlertDialogHeader({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-header"
className={cn(
"grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
className,
)}
{...props}
/>
);
}
function AlertDialogFooter({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
className,
)}
{...props}
/>
);
}
function AlertDialogMedia({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-media"
className={cn(
"mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
className,
)}
{...props}
/>
);
}
function AlertDialogTitle({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
return (
<AlertDialogPrimitive.Title
data-slot="alert-dialog-title"
className={cn(
"text-base leading-snug font-semibold tracking-[-0.2px] sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
className,
)}
{...props}
/>
);
}
function AlertDialogDescription({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
return (
<AlertDialogPrimitive.Description
data-slot="alert-dialog-description"
className={cn(
"text-[13px] text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
className,
)}
{...props}
/>
);
}
function AlertDialogAction({
className,
...props
}: React.ComponentProps<typeof Button>) {
return (
<Button
data-slot="alert-dialog-action"
className={cn(className)}
{...props}
/>
);
}
function AlertDialogCancel({
className,
variant = "outline",
size = "default",
...props
}: AlertDialogPrimitive.Close.Props &
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
return (
<AlertDialogPrimitive.Close
data-slot="alert-dialog-cancel"
className={cn(className)}
render={<Button variant={variant} size={size} />}
{...props}
/>
);
}
export {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogTitle,
AlertDialogTrigger,
};
registry/ui/alert-dialog/AlertDialog.vue
<script setup lang="ts">
import type { AlertDialogEmits, AlertDialogProps } from 'reka-ui'
import { AlertDialogRoot, useForwardPropsEmits } from 'reka-ui'
const props = defineProps<AlertDialogProps>()
const emits = defineEmits<AlertDialogEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<AlertDialogRoot v-slot="slotProps" data-slot="alert-dialog" v-bind="forwarded">
<slot v-bind="slotProps" />
</AlertDialogRoot>
</template>
registry/ui/alert-dialog/AlertDialogAction.vue
<script setup lang="ts">
import type { AlertDialogActionProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import type { ButtonVariants } from '@/registry/edmi/ui/button'
import { reactiveOmit } from '@vueuse/core'
import { AlertDialogAction } from 'reka-ui'
import { cn } from '@/registry/edmi/lib/utils'
import { buttonVariants } from '@/registry/edmi/ui/button'
const props = defineProps<AlertDialogActionProps & {
class?: HTMLAttributes['class']
variant?: ButtonVariants['variant']
size?: ButtonVariants['size']
}>()
const delegatedProps = reactiveOmit(props, 'class', 'variant', 'size')
</script>
<template>
<AlertDialogAction
data-slot="alert-dialog-action"
v-bind="delegatedProps"
:class="cn(buttonVariants({ variant, size }), props.class)"
>
<slot />
</AlertDialogAction>
</template>
registry/ui/alert-dialog/AlertDialogCancel.vue
<script setup lang="ts">
import type { AlertDialogCancelProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import type { ButtonVariants } from '@/registry/edmi/ui/button'
import { reactiveOmit } from '@vueuse/core'
import { AlertDialogCancel } from 'reka-ui'
import { cn } from '@/registry/edmi/lib/utils'
import { buttonVariants } from '@/registry/edmi/ui/button'
const props = withDefaults(defineProps<AlertDialogCancelProps & {
class?: HTMLAttributes['class']
variant?: ButtonVariants['variant']
size?: ButtonVariants['size']
}>(), {
variant: 'outline',
size: 'default',
})
const delegatedProps = reactiveOmit(props, 'class', 'variant', 'size')
</script>
<template>
<AlertDialogCancel
data-slot="alert-dialog-cancel"
v-bind="delegatedProps"
:class="cn(buttonVariants({ variant, size }), props.class)"
>
<slot />
</AlertDialogCancel>
</template>
registry/ui/alert-dialog/AlertDialogContent.vue
<script setup lang="ts">
import type { AlertDialogContentEmits, AlertDialogContentProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import {
AlertDialogContent,
AlertDialogOverlay,
AlertDialogPortal,
useForwardPropsEmits,
} from 'reka-ui'
import { cn } from '@/registry/edmi/lib/utils'
import { type Elevation, provideSurface, useElevation } from '@/registry/edmi/ui/elevation'
defineOptions({
inheritAttrs: false,
})
const props = withDefaults(defineProps<AlertDialogContentProps & { class?: HTMLAttributes['class'], size?: 'default' | 'sm', elevation?: Elevation }>(), {
size: 'default',
elevation: undefined,
})
const emits = defineEmits<AlertDialogContentEmits>()
// ✦ depth (v4): overlay role. Natural level is floating in layered mode; flat otherwise.
const overlayElevation = {
sunken: '',
flat: '',
raised: 'border-transparent shadow-raised',
floating: 'border-transparent shadow-floating',
}
const level = useElevation(() => props.elevation, 'overlay')
provideSurface(() => level.value)
const delegatedProps = reactiveOmit(props, 'class', 'size', 'elevation')
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<AlertDialogPortal>
<AlertDialogOverlay
data-slot="alert-dialog-overlay"
class="fixed inset-0 isolate z-50 bg-overlay data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/>
<AlertDialogContent
data-slot="alert-dialog-content"
:data-size="size"
v-bind="{ ...$attrs, ...forwarded }"
:class="
cn(
'group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-2xl border border-border bg-popover p-[22px] text-popover-foreground outline-none duration-150 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm',
overlayElevation[level],
props.class,
)
"
>
<slot />
</AlertDialogContent>
</AlertDialogPortal>
</template>
registry/ui/alert-dialog/AlertDialogDescription.vue
<script setup lang="ts">
import type { AlertDialogDescriptionProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { AlertDialogDescription } from 'reka-ui'
import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = reactiveOmit(props, 'class')
</script>
<template>
<AlertDialogDescription
data-slot="alert-dialog-description"
v-bind="delegatedProps"
:class="cn('text-[13px] text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground', props.class)"
>
<slot />
</AlertDialogDescription>
</template>
registry/ui/alert-dialog/AlertDialogFooter.vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="alert-dialog-footer"
:class="cn('flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end', props.class)"
>
<slot />
</div>
</template>
registry/ui/alert-dialog/AlertDialogHeader.vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="alert-dialog-header"
:class="cn('grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]', props.class)"
>
<slot />
</div>
</template>
registry/ui/alert-dialog/AlertDialogMedia.vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="alert-dialog-media"
:class="cn('mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*=size-])]:size-6', props.class)"
>
<slot />
</div>
</template>
registry/ui/alert-dialog/AlertDialogTitle.vue
<script setup lang="ts">
import type { AlertDialogTitleProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { AlertDialogTitle } from 'reka-ui'
import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = reactiveOmit(props, 'class')
</script>
<template>
<AlertDialogTitle
data-slot="alert-dialog-title"
v-bind="delegatedProps"
:class="cn('text-base leading-snug font-semibold tracking-[-0.2px] sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2', props.class)"
>
<slot />
</AlertDialogTitle>
</template>
registry/ui/alert-dialog/AlertDialogTrigger.vue
<script setup lang="ts">
import type { AlertDialogTriggerProps } from 'reka-ui'
import { AlertDialogTrigger } from 'reka-ui'
const props = defineProps<AlertDialogTriggerProps>()
</script>
<template>
<AlertDialogTrigger data-slot="alert-dialog-trigger" v-bind="props">
<slot />
</AlertDialogTrigger>
</template>
registry/ui/alert-dialog/index.ts
export { default as AlertDialog } from "./AlertDialog.vue";
export { default as AlertDialogAction } from "./AlertDialogAction.vue";
export { default as AlertDialogCancel } from "./AlertDialogCancel.vue";
export { default as AlertDialogContent } from "./AlertDialogContent.vue";
export { default as AlertDialogDescription } from "./AlertDialogDescription.vue";
export { default as AlertDialogFooter } from "./AlertDialogFooter.vue";
export { default as AlertDialogHeader } from "./AlertDialogHeader.vue";
export { default as AlertDialogMedia } from "./AlertDialogMedia.vue";
export { default as AlertDialogTitle } from "./AlertDialogTitle.vue";
export { default as AlertDialogTrigger } from "./AlertDialogTrigger.vue";
src/lib/registry/ui/alert-dialog/alert-dialog-action.svelte
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
import {
buttonVariants,
type ButtonVariant,
type ButtonSize,
} from "$lib/registry/ui/button/index.js";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
variant = "default",
size = "default",
...restProps
}: AlertDialogPrimitive.ActionProps & {
variant?: ButtonVariant;
size?: ButtonSize;
} = $props();
</script>
<AlertDialogPrimitive.Action
bind:ref
data-slot="alert-dialog-action"
class={cn(buttonVariants({ variant, size }), className)}
{...restProps}
/>
src/lib/registry/ui/alert-dialog/alert-dialog-cancel.svelte
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
import {
buttonVariants,
type ButtonVariant,
type ButtonSize,
} from "$lib/registry/ui/button/index.js";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
variant = "outline",
size = "default",
...restProps
}: AlertDialogPrimitive.CancelProps & {
variant?: ButtonVariant;
size?: ButtonSize;
} = $props();
</script>
<AlertDialogPrimitive.Cancel
bind:ref
data-slot="alert-dialog-cancel"
class={cn(buttonVariants({ variant, size }), className)}
{...restProps}
/>
src/lib/registry/ui/alert-dialog/alert-dialog-content.svelte
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
import { cn, type WithoutChild, type WithoutChildrenOrChild } from "$lib/utils.js";
import { type Elevation, setSurface, useElevation } from "$lib/registry/ui/elevation/index.js";
import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
import AlertDialogPortal from "./alert-dialog-portal.svelte";
import type { ComponentProps } from "svelte";
let {
ref = $bindable(null),
class: className,
size = "default",
portalProps,
elevation = "auto",
...restProps
}: WithoutChild<AlertDialogPrimitive.ContentProps> & {
size?: "default" | "sm";
/** ✦ depth: flat 0, raised +1 (bevel), floating +2 (bevel + drop). */
elevation?: Elevation;
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof AlertDialogPortal>>;
} = $props();
// ✦ depth (v4): overlay role. Natural level is floating in layered mode; flat otherwise.
const overlayElevation = {
sunken: "",
flat: "",
raised: "border-transparent shadow-raised",
floating: "border-transparent shadow-floating",
};
const level = useElevation(() => elevation, "overlay");
setSurface(() => level.current);
</script>
<AlertDialogPortal {...portalProps}>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
bind:ref
data-slot="alert-dialog-content"
data-size={size}
class={cn(
"group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-2xl border border-border bg-popover p-[22px] text-popover-foreground outline-none duration-150 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm",
overlayElevation[level.current],
className
)}
{...restProps}
/>
</AlertDialogPortal>
src/lib/registry/ui/alert-dialog/alert-dialog-description.svelte
<script lang="ts">
import { AlertDialog as Primitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: Primitive.DescriptionProps = $props();
</script>
<Primitive.Description
bind:ref
data-slot="alert-dialog-description"
class={cn("text-[13px] text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground", className)}
{...restProps}
/>
src/lib/registry/ui/alert-dialog/alert-dialog-footer.svelte
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="alert-dialog-footer"
class={cn("flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end", className)}
{...restProps}
>
{@render children?.()}
</div>
src/lib/registry/ui/alert-dialog/alert-dialog-header.svelte
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="alert-dialog-header"
class={cn("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className)}
{...restProps}
>
{@render children?.()}
</div>
src/lib/registry/ui/alert-dialog/alert-dialog-media.svelte
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="alert-dialog-media"
class={cn("mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6", className)}
{...restProps}
>
{@render children?.()}
</div>
src/lib/registry/ui/alert-dialog/alert-dialog-overlay.svelte
<script lang="ts">
import { AlertDialog as Primitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: Primitive.OverlayProps = $props();
</script>
<Primitive.Overlay
bind:ref
data-slot="alert-dialog-overlay"
class={cn("fixed inset-0 isolate z-50 bg-overlay data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className)}
{...restProps}
/>
src/lib/registry/ui/alert-dialog/alert-dialog-portal.svelte
<script lang="ts">
import { AlertDialog as Primitive } from "bits-ui";
let { ...restProps }: Primitive.PortalProps = $props();
</script>
<Primitive.Portal {...restProps} />
src/lib/registry/ui/alert-dialog/alert-dialog-title.svelte
<script lang="ts">
import { AlertDialog as Primitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: Primitive.TitleProps = $props();
</script>
<Primitive.Title
bind:ref
data-slot="alert-dialog-title"
class={cn("text-base leading-snug font-semibold tracking-[-0.2px] sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className)}
{...restProps}
/>
src/lib/registry/ui/alert-dialog/alert-dialog-trigger.svelte
<script lang="ts">
import { AlertDialog as Primitive } from "bits-ui";
let { ref = $bindable(null), ...restProps }: Primitive.TriggerProps = $props();
</script>
<Primitive.Trigger bind:ref data-slot="alert-dialog-trigger" {...restProps} />
src/lib/registry/ui/alert-dialog/alert-dialog.svelte
<script lang="ts">
import { AlertDialog as Primitive } from "bits-ui";
let { open = $bindable(false), ...restProps }: Primitive.RootProps = $props();
</script>
<Primitive.Root bind:open {...restProps} />
src/lib/registry/ui/alert-dialog/index.ts
import Root from "./alert-dialog.svelte";
import Action from "./alert-dialog-action.svelte";
import Cancel from "./alert-dialog-cancel.svelte";
import Content from "./alert-dialog-content.svelte";
import Description from "./alert-dialog-description.svelte";
import Footer from "./alert-dialog-footer.svelte";
import Header from "./alert-dialog-header.svelte";
import Media from "./alert-dialog-media.svelte";
import Overlay from "./alert-dialog-overlay.svelte";
import Portal from "./alert-dialog-portal.svelte";
import Title from "./alert-dialog-title.svelte";
import Trigger from "./alert-dialog-trigger.svelte";
export {
Action,
Action as AlertDialogAction,
Cancel,
Cancel as AlertDialogCancel,
Content,
Content as AlertDialogContent,
Description,
Description as AlertDialogDescription,
Footer,
Footer as AlertDialogFooter,
Header,
Header as AlertDialogHeader,
Media,
Media as AlertDialogMedia,
Overlay,
Overlay as AlertDialogOverlay,
Portal,
Portal as AlertDialogPortal,
Root,
//
Root as AlertDialog,
Title,
Title as AlertDialogTitle,
Trigger,
Trigger as AlertDialogTrigger,
};

See the Elevation guide for the levels, roles and layered mode.

Flat by default. On AlertDialogContent, raised (+1) adds the bevel and floating (+2) the bevel plus one soft drop (the natural level in layered mode).

StackBlitz ↗StackBlitz ↗StackBlitz ↗
Prop Type Default
AlertDialogContent size default | sm default
AlertDialogAction Button props default variant
AlertDialogCancel variant / size Button props outline / default
elevation "auto" | "sunken" | "flat" | "raised" | "floating" "auto"

Parts: AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogMedia, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction.

  • --overlay scrim without blur, optional bevel / drop via elevation. Pass variant="destructive" to AlertDialogAction for the red confirm.