Alert Dialog
v0.0.10
mix mishka.ui.gen.headless alert_dialog
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.
<.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.
| Part | data-part | State / ARIA | Purpose |
|---|---|---|---|
| Root | - | data-open, data-closed, data-close-on-outside | Wraps the whole component; always renders data-close-on-outside="false". |
| Trigger | trigger | aria-haspopup, aria-expanded, aria-controls, data-popup-open, data-disabled | The button that opens the alert dialog. |
| Backdrop | backdrop | aria-hidden | The overlay behind the popup; never dismisses on click. |
| Popup | popup | role="alertdialog", aria-modal, aria-labelledby, aria-describedby, data-open, data-closed | The dialog panel itself; focus-trapped. |
| Title / Description | title / description | required; wired to aria-labelledby / aria-describedby | Announced to screen readers automatically. |
| Content | content | - | Optional inner body, rendered only when present. |
| Actions | actions | - | 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.
Attributes
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
id
|
string
| — | — | Yes | Unique id (anchors aria relationships) |
open
|
boolean
|
false
| — | No | Initial/controlled open state |
disabled
|
boolean
|
false
| — | No | Disable the trigger (data-disabled) |
on_open_change
|
string
| — | — | No | LiveView event pushed on open/close ({open}) |
on_open_change_target
|
string
| — | — | No | Optional pushEventTo target (LiveComponent selector) |
labelledby
|
string
| — | — | No | Override aria-labelledby |
describedby
|
string
| — | — | No | Override aria-describedby |
class
|
any
| — | — | No | Extra classes for the root |
trigger_class
|
any
| — | — | No | Extra classes for the trigger button |
backdrop_class
|
any
| — | — | No | Extra classes for the backdrop |
popup_class
|
any
| — | — | No | Extra classes for the popup |
title_class
|
any
| — | — | No | Extra classes for the title |
description_class
|
any
| — | — | No | Extra classes for the description |
content_class
|
any
| — | — | No | Extra classes for the inner content |
actions_class
|
any
| — | — | No | Extra classes for the actions footer |
rest
|
global
| — | — | No | — |
Slots
| Name | Required | Description |
|---|---|---|
trigger
| No | The element that opens the alert dialog |
title
| Yes | Accessible title (wired to aria-labelledby) |
description
| Yes | Accessible description (wired to aria-describedby) |
inner_block
| No | Alert dialog body |
actions
| No | Footer with the confirm/cancel buttons (use data-close on a button to close) |
Options
Each option takes one or more of its values, separated by commas.
| Option | Values |
|---|---|
--type
|
alert_dialog
|
--module
| A module name of your own |
What it generates
mix mishka.ui.gen.headless alert_dialog