<script setup lang="ts">
import {
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogRoot,
AlertDialogTitle,
AlertDialogTrigger,
} from 'akar';
import './styles.css';
function handleAction() {
// eslint-disable-next-line no-alert
alert('clicked action button!');
}
</script>
<template>
<div class="akar-demo akar-demo-AlertDialog">
<AlertDialogRoot>
<AlertDialogTrigger class="Button green">
Delete account
</AlertDialogTrigger>
<AlertDialogPortal>
<AlertDialogOverlay class="AlertDialogOverlay" />
<AlertDialogContent class="AlertDialogContent">
<AlertDialogTitle class="AlertDialogTitle">
Are you absolutely sure?
</AlertDialogTitle>
<AlertDialogDescription class="AlertDialogDescription">
This action cannot be undone. This will permanently delete your account and remove your data from our servers.
</AlertDialogDescription>
<div :style="{ display: 'flex', gap: 25, justifyContent: 'flex-end' }">
<AlertDialogCancel class="Button mauve">
Cancel
</AlertDialogCancel>
<AlertDialogAction
class="Button red"
@click="handleAction"
>
Yes, delete account
</AlertDialogAction>
</div>
</AlertDialogContent>
</AlertDialogPortal>
</AlertDialogRoot>
</div>
</template>@import '../_colors/black-alpha.css';
@import '../_colors/mauve.css';
@import '../_colors/red.css';
@import '../_colors/green.css';
@scope (.akar-demo-AlertDialog) {
/* reset */
button {
all: unset;
}
.AlertDialogOverlay {
background-color: var(--black-a9);
position: fixed;
inset: 0;
animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
.AlertDialogContent {
background-color: white;
border-radius: 6px;
box-shadow:
hsl(206 22% 7% / 35%) 0px 10px 38px -10px,
hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90vw;
max-width: 500px;
max-height: 85vh;
padding: 25px;
animation: contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
.AlertDialogContent:focus {
outline: none;
}
.AlertDialogTitle {
margin: 0;
color: var(--mauve-12);
font-size: 17px;
font-weight: 500;
}
.AlertDialogDescription {
margin-bottom: 20px;
color: var(--mauve-11);
font-size: 15px;
line-height: 1.5;
}
.Button {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
padding: 0 15px;
font-size: 15px;
line-height: 1;
font-weight: 500;
height: 35px;
}
.Button.green {
background-color: white;
color: var(--green-11);
box-shadow: 0 2px 10px var(--black-a7);
}
.Button.green:hover {
background-color: var(--mauve-3);
}
.Button.green:focus {
box-shadow: 0 0 0 2px black;
}
.Button.red {
background-color: var(--red-4);
color: var(--red-11);
}
.Button.red:hover {
background-color: var(--red-5);
}
.Button.red:focus {
box-shadow: 0 0 0 2px var(--red-7);
}
.Button.mauve {
background-color: var(--mauve-4);
color: var(--mauve-11);
}
.Button.mauve:hover {
background-color: var(--mauve-5);
}
.Button.mauve:focus {
box-shadow: 0 0 0 2px var(--mauve-7);
}
@keyframes overlayShow {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes contentShow {
from {
opacity: 0;
transform: translate(-50%, -48%) scale(0.96);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
}Features
- Focus is automatically trapped.
- Can be controlled or uncontrolled.
- Manages screen reader announcements with
TitleandDescriptioncomponents. - Esc closes the component automatically.
Installation
Install the component from your command line.
pnpm add akar
yarn add akar
npm install akar
bun add akar
Anatomy
Import all parts and piece them together.
<script setup lang="ts">
import {
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogRoot,
AlertDialogTitle,
AlertDialogTrigger,
} from 'akar';
</script>
<template>
<AlertDialogRoot>
<AlertDialogTrigger />
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogContent>
<AlertDialogTitle />
<AlertDialogDescription />
<AlertDialogCancel />
<AlertDialogAction />
</AlertDialogContent>
</AlertDialogPortal>
</AlertDialogRoot>
</template>
API Reference
Root
Contains all the parts of an alert dialog.
| Prop | Default | Type |
|---|---|---|
defaultOpen | booleanThe open state of the dialog when it is initially rendered. Use when you do not need to control its open state. | |
open | booleanThe controlled open state of the dialog. Can be binded as | |
unmountOnHide | true | booleanWhen set to Useful for SEO or when you want to improve performance by not remounting the component on every open. |
| Event | Payload |
|---|---|
update:open | value: booleanEvent handler called when the open state of the dialog changes. |
| Slot (default) | Payload |
|---|---|
open | booleanCurrent open state |
close | (): voidClose the dialog |
Trigger
A button that opens the dialog.
| Prop | Default | Type |
|---|---|---|
as | "button" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
| Data attribute | Value |
|---|---|
[data-state] | "open" | "closed" |
Portal
When used, portals your overlay and content parts into the body.
| Prop | Default | Type |
|---|---|---|
defer | booleanDefer the resolving of a Teleport target until other parts of the application have mounted (requires Vue 3.5.0+) | |
disabled | booleanDisable teleport and render the component inline | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
to | string | HTMLElementVue native teleport component prop |
Overlay
A layer that covers the inert portion of the view when the dialog is open.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
| Data attribute | Value |
|---|---|
[data-state] | "open" | "closed" |
Content
Contains content to be rendered when the dialog is open.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disableOutsidePointerEvents | booleanWhen | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
| Event | Payload |
|---|---|
closeAutoFocus | event: EventEvent handler called when auto-focusing on close. Can be prevented. |
escapeKeyDown | event: KeyboardEventEvent handler called when the escape key is down. Can be prevented. |
focusOutside | event: FocusOutsideEventEvent handler called when the focus moves outside of the |
interactOutside | event: PointerDownOutsideEvent | FocusOutsideEventEvent handler called when an interaction happens outside the |
openAutoFocus | event: EventEvent handler called when auto-focusing on open. Can be prevented. |
pointerDownOutside | event: PointerDownOutsideEventEvent handler called when a |
| Data attribute | Value |
|---|---|
[data-state] | "open" | "closed" |
Cancel
A button that closes the dialog. This button should be distinguished visually from AlertDialogAction buttons.
| Prop | Default | Type |
|---|---|---|
as | "button" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Action
A button that closes the dialog. These buttons should be distinguished visually from the AlertDialogCancel button.
| Prop | Default | Type |
|---|---|---|
as | "button" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Title
An accessible name to be announced when the dialog is opened. Alternatively, you can provide aria-label or aria-labelledby to AlertDialogContent and exclude this component.
| Prop | Default | Type |
|---|---|---|
as | "h2" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Description
An accessible description to be announced when the dialog is opened. Alternatively, you can provide aria-describedby to AlertDialogContent and exclude this component.
| Prop | Default | Type |
|---|---|---|
as | "p" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Examples
Close after asynchronous form submission
Use the controlled props to programmatically close the Alert Dialog after an async operation has completed.
<script setup>
import {
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogRoot,
AlertDialogTitle,
AlertDialogTrigger,
} from 'akar'
const wait = () => new Promise(resolve => setTimeout(resolve, 1000))
const open = ref(false)
</script>
<template>
<AlertDialogRoot v-model:open="open">
<AlertDialogTrigger>Open</AlertDialogTrigger>
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogContent>
<form
@submit.prevent="
(event) => {
wait().then(() => open = false);
}
"
>
<!-- some inputs -->
<button type="submit">
Submit
</button>
</form>
</AlertDialogContent>
</AlertDialogPortal>
</AlertDialogRoot>
</template>
Custom portal container
Customise the element that your alert dialog portals into.
<script setup>
import { ref } from 'vue'
const container = ref(null)
</script>
<template>
<div>
<AlertDialogRoot>
<AlertDialogTrigger />
<AlertDialogPortal :to="container">
<AlertDialogOverlay />
<AlertDialogContent>...</AlertDialogContent>
</AlertDialogPortal>
</AlertDialogRoot>
<div ref="container" />
</div>
</template>
Accessibility
Adheres to the Alert and Message Dialogs WAI-ARIA design pattern.
Keyboard Interactions
| Key | Description |
|---|---|
| Space | Opens/closes the dialog. |
| Enter | Opens/closes the dialog. |
| Tab | Moves focus to the next focusable element. |
| Shift + Tab | Moves focus to the previous focusable element. |
| Esc | Closes the dialog and moves focus to AlertDialogTrigger. |