Skip to content

Alert Dialog

A modal dialog that closes only through the answers it renders, for decisions that cannot be undone.

View source View as Markdown

Alert Dialog is Dialog with the ways out taken away. It is always modal, a press outside never closes it, and the only ways past it are the buttons it renders. Reach for it when the answer decides something that cannot be taken back, and use Dialog for everything else.

App.vue
<template>
  <div
    class="border-surface rounded-component-2xl relative isolate flex h-80 w-full max-w-md items-center justify-center overflow-hidden border-2"
  >
    <AlertDialog.Root>
      <AlertDialog.Trigger :class="danger">
        Delete project
      </AlertDialog.Trigger>

      <AlertDialog.Teleport disabled>
        <AlertDialog.Backdrop :class="backdrop" />

        <AlertDialog.Content :class="popup">
          <div class="flex flex-col gap-1">
            <AlertDialog.Title :class="title">
              Delete project?
            </AlertDialog.Title>
            <AlertDialog.Description :class="description">
              Every deployment, token and log goes with it. This cannot be
              undone.
            </AlertDialog.Description>
          </div>

          <div class="flex items-center justify-end gap-2">
            <AlertDialog.Close :class="quiet">Cancel</AlertDialog.Close>
            <AlertDialog.Close :class="danger">Delete</AlertDialog.Close>
          </div>
        </AlertDialog.Content>
      </AlertDialog.Teleport>
    </AlertDialog.Root>
  </div>
</template>

<script setup lang="ts">
import { AlertDialog } from '@maas/mirror/vue/AlertDialog'

const danger = [
  'inline-flex h-12 items-center justify-center gap-1.5 px-[1.125rem] whitespace-nowrap',
  'rounded-component-lg border-2 border-[transparent] type-component-lg',
  'bg-danger-solid text-danger-on-solid',
  'transition-all duration-100 ease-linear',
  'outline-4 outline-transparent focus-visible:focus-ring',
  'hover:bg-danger-solid-hover active:bg-danger-solid-active',
].join(' ')

const quiet = [
  'inline-flex h-12 items-center justify-center gap-1.5 px-[1.125rem] whitespace-nowrap',
  'rounded-component-lg border-2 border-[transparent] type-component-lg',
  'text-primary-on-subtle hover:bg-primary-subtle active:bg-primary-subtle-hover',
  'transition-all duration-100 ease-linear',
  'outline-4 outline-transparent focus-visible:focus-ring',
].join(' ')

const backdrop = 'bg-surface-dimmer absolute inset-0'

const popup = [
  'absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',
  'flex w-[20rem] flex-col gap-5 p-6',
  'rounded-component-2xl bg-primary-inverted shadow-component-high',
  'outline-4 outline-transparent focus:outline-none',
  '[&_*]:transition-all [&_*]:duration-100 [&_*]:ease-linear',
].join(' ')

const title = 'type-component-xl text-primary-solid'
const description = 'type-component-2xs text-primary-muted'
</script>

The example switches the teleport off and positions the layer absolutely, so the alert dialog stays inside the preview frame rather than covering the viewport.

Usage guidelines

  • Give it a AlertDialog.Title that asks the question and a AlertDialog.Description that says what happens, so the reader has the consequence in front of them before they answer.should
  • Render both answers. A dialog whose only button is the destructive one leaves the reader nothing safe to press.must
  • Do not reach for it for anything reversible. A press outside is how people leave a layer, so take it away only where the next click destroys something.must not