Alert Dialog

v0.0.10

v0.0.10StableHeadlessStatelessAccessibleJSApache-2.0
Installation mix mishka.ui.gen.headless alert_dialog
Source View Source

Overview of the Phoenix and LiveView Headless Alert Dialog Component

The Mishka Chelekom Headless Alert Dialog ships zero opinionated styling - only the semantic markup, full WAI-ARIA wiring, a focus trap, and paired-presence state (data-open/data-closed). Unlike a plain dialog, the root sets data-close-on-outside="false" - a confirmation requires an explicit choice, so a backdrop click never dismisses it. Every part - trigger, backdrop, popup, title, description, content and actions - is addressable through a *_class attribute, so you style it entirely with your own Tailwind classes while the accessibility and behavior keep working underneath.

Base UI Alert Dialog example (Phoenix headless)

Base UI Alert Dialog example (Phoenix headless)

Discard draft?

You cannot undo this action.

heex
<.alert_dialog
  id="baseui-alert_dialog-hero"
  trigger_class="flex h-8 items-center justify-center gap-2 border border-neutral-950 dark:border-white bg-white dark:bg-neutral-950 px-3 text-sm leading-none whitespace-nowrap font-normal text-red-700 dark:text-red-400 select-none hover:not-data-[disabled]:bg-neutral-100 dark:hover:not-data-[disabled]:bg-neutral-800 active:not-data-[disabled]:bg-neutral-200 dark:active:not-data-[disabled]:bg-neutral-700 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-neutral-950 dark:focus-visible:outline-white data-[disabled]:border-neutral-500 data-[disabled]:text-neutral-500 disabled:border-neutral-500 disabled:text-neutral-500 dark:data-[disabled]:border-neutral-400 dark:data-[disabled]:text-neutral-400"
  backdrop_class="fixed inset-0 min-h-dvh bg-black opacity-20 transition-opacity duration-150 data-[closed]:opacity-0 data-[starting-style]:opacity-0 dark:opacity-50 supports-[-webkit-touch-callout:none]:absolute"
  popup_class="fixed top-1/2 left-1/2 -mt-8 flex w-96 max-w-[calc(100vw-3rem)] -translate-x-1/2 -translate-y-1/2 flex-col gap-4 bg-white dark:bg-neutral-950 p-4 text-neutral-950 dark:text-white border border-neutral-950 dark:border-white shadow-[0.25rem_0.25rem_0] shadow-black/12 dark:shadow-none transition-[scale,opacity] duration-100 ease-out data-[closed]:scale-[0.98] data-[closed]:opacity-0 data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0"
  title_class="text-base font-bold"
  description_class="text-sm text-neutral-600 dark:text-neutral-400"
  actions_class="flex justify-end gap-3"
>
  <:trigger>Discard draft</:trigger>
  <:title>Discard draft?</:title>
  <:description>You can't undo this action.</:description>
  <:actions>
    <button
      type="button"
      data-close
      class="flex h-8 items-center justify-center gap-2 border border-neutral-950 dark:border-white bg-white dark:bg-neutral-950 px-3 text-sm leading-none whitespace-nowrap font-normal text-neutral-950 dark:text-white select-none hover:not-data-[disabled]:bg-neutral-100 dark:hover:not-data-[disabled]:bg-neutral-800 active:not-data-[disabled]:bg-neutral-200 dark:active:not-data-[disabled]:bg-neutral-700 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-neutral-950 dark:focus-visible:outline-white data-[disabled]:border-neutral-500 data-[disabled]:text-neutral-500 disabled:border-neutral-500 disabled:text-neutral-500 dark:data-[disabled]:border-neutral-400 dark:data-[disabled]:text-neutral-400"
    >
      Cancel
    </button>
    <button
      type="button"
      data-close
      class="flex h-8 items-center justify-center gap-2 border border-neutral-950 dark:border-white bg-white dark:bg-neutral-950 px-3 text-sm leading-none whitespace-nowrap font-normal text-red-700 dark:text-red-400 select-none hover:not-data-[disabled]:bg-neutral-100 dark:hover:not-data-[disabled]:bg-neutral-800 active:not-data-[disabled]:bg-neutral-200 dark:active:not-data-[disabled]:bg-neutral-700 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-neutral-950 dark:focus-visible:outline-white data-[disabled]:border-neutral-500 data-[disabled]:text-neutral-500 disabled:border-neutral-500 disabled:text-neutral-500 dark:data-[disabled]:border-neutral-400 dark:data-[disabled]:text-neutral-400"
    >
      Discard
    </button>
  </:actions>
</.alert_dialog>

Parts & data attributes

Each part carries a stable data-part and a structural chelekom-alert-dialog__* class you can target, plus paired-presence state for open/closed transitions.

Partdata-partState / ARIAPurpose
Root-data-open, data-closed, data-close-on-outsideWraps the whole component; always renders data-close-on-outside="false".
Triggertriggeraria-haspopup, aria-expanded, aria-controls, data-popup-open, data-disabledThe button that opens the alert dialog.
Backdropbackdroparia-hiddenThe overlay behind the popup; never dismisses on click.
Popuppopuprole="alertdialog", aria-modal, aria-labelledby, aria-describedby, data-open, data-closedThe dialog panel itself; focus-trapped.
Title / Descriptiontitle / descriptionrequired; wired to aria-labelledby / aria-describedbyAnnounced to screen readers automatically.
Contentcontent-Optional inner body, rendered only when present.
Actionsactions-Footer for confirm/cancel buttons; add data-close to close.

Accessibility

Follows the WAI-ARIA Alert Dialog pattern: focus is trapped inside the open popup and restored to the trigger on close, Escape closes it, Tab and Shift+Tab cycle within the trap, and - unlike a plain dialog - an outside click never dismisses it, forcing an explicit choice. The title and description slots are required and announced to screen readers.