# `PhiaUi.Components.AlertDialog`
[🔗](https://github.com/charlenopires/PhiaUI/blob/v0.1.17/lib/phia_ui/components/feedback/alert_dialog.ex#L1)

Alert Dialog component for critical confirmations requiring explicit user action.

An alert dialog is a modal dialog that interrupts the user's workflow to
communicate an important message and require a response. Unlike a standard
`Dialog`, it uses `role="alertdialog"` per the WAI-ARIA specification, which
signals to assistive technology that this requires immediate attention and
an explicit decision.

Use an alert dialog for **irreversible or high-impact actions** such as:
- Deleting a record permanently
- Revoking access or permissions
- Cancelling an in-progress operation with unsaved work
- Confirming payment or subscription changes

Do not use an alert dialog for informational messages that require no decision
— use `Alert` or `Toast` for those.

## JavaScript Hook

Reuses the `PhiaDialog` JavaScript hook (shared with the standard `Dialog`
component). The hook provides:
- Focus trap — Tab/Shift+Tab cycle within the dialog
- Escape key closes the dialog
- Scroll locking — prevents the page behind from scrolling while open
- Focus return — restores focus to the previously focused element on close

## Hook Registration

Copy the hook via `mix phia.add alert_dialog`, then register it:

    # assets/js/app.js
    import PhiaDialog from "./hooks/dialog"

    let liveSocket = new LiveSocket("/live", Socket, {
      hooks: { PhiaDialog }
    })

## Sub-components

| Function                     | Purpose                                            |
|------------------------------|----------------------------------------------------|
| `alert_dialog/1`             | Root modal with `role="alertdialog"`               |
| `alert_dialog_header/1`      | Title + description layout container               |
| `alert_dialog_title/1`       | `<h2>` heading — set `:id` for ARIA linkage        |
| `alert_dialog_description/1` | `<p>` supporting text — set `:id` for ARIA         |
| `alert_dialog_footer/1`      | Action row (cancel + confirm buttons)              |
| `alert_dialog_action/1`      | Confirm button (default or destructive style)      |
| `alert_dialog_cancel/1`      | Cancel button with outline style                   |

## Example — Delete Confirmation

The most common pattern: confirm before permanently deleting a record.

    <.alert_dialog
      id="delete-confirm"
      open={@show_delete_confirm}
      aria-labelledby="delete-confirm-title"
      aria-describedby="delete-confirm-desc">
      <.alert_dialog_header>
        <.alert_dialog_title id="delete-confirm-title">
          Delete Project?
        </.alert_dialog_title>
        <.alert_dialog_description id="delete-confirm-desc">
          This will permanently delete "{@project.name}" and all its data.
          This action cannot be undone.
        </.alert_dialog_description>
      </.alert_dialog_header>
      <.alert_dialog_footer>
        <.alert_dialog_cancel phx-click="cancel_delete">
          Cancel
        </.alert_dialog_cancel>
        <.alert_dialog_action variant="destructive" phx-click="confirm_delete" phx-value-id={@project.id}>
          Delete Project
        </.alert_dialog_action>
      </.alert_dialog_footer>
    </.alert_dialog>

    # In the LiveView
    def handle_event("confirm_delete", %{"id" => id}, socket) do
      Projects.delete!(id)
      {:noreply,
       socket
       |> assign(:show_delete_confirm, false)
       |> push_event("phia-toast", %{title: "Project deleted", variant: "success"})}
    end

    def handle_event("cancel_delete", _params, socket) do
      {:noreply, assign(socket, :show_delete_confirm, false)}
    end

## Example — Permission Revocation

Destructive actions that affect another user:

    <.alert_dialog
      id="revoke-access"
      open={@show_revoke}
      aria-labelledby="revoke-title"
      aria-describedby="revoke-desc">
      <.alert_dialog_header>
        <.alert_dialog_title id="revoke-title">
          Revoke Admin Access?
        </.alert_dialog_title>
        <.alert_dialog_description id="revoke-desc">
          {@selected_user.name} will immediately lose admin privileges.
          They will need to be re-invited to regain access.
        </.alert_dialog_description>
      </.alert_dialog_header>
      <.alert_dialog_footer>
        <.alert_dialog_cancel phx-click="cancel_revoke">Keep Access</.alert_dialog_cancel>
        <.alert_dialog_action variant="destructive" phx-click="confirm_revoke">
          Revoke Access
        </.alert_dialog_action>
      </.alert_dialog_footer>
    </.alert_dialog>

## Example — Unsaved Changes Warning

Warn before discarding work-in-progress:

    <.alert_dialog id="unsaved-changes" open={@navigating_away and @form_dirty}>
      <.alert_dialog_header>
        <.alert_dialog_title>Unsaved Changes</.alert_dialog_title>
        <.alert_dialog_description>
          You have unsaved changes. If you leave now, they will be lost.
        </.alert_dialog_description>
      </.alert_dialog_header>
      <.alert_dialog_footer>
        <.alert_dialog_cancel phx-click="stay_on_page">Stay</.alert_dialog_cancel>
        <.alert_dialog_action phx-click="discard_and_navigate">Leave anyway</.alert_dialog_action>
      </.alert_dialog_footer>
    </.alert_dialog>

## Opening and Closing

Control visibility via the `:open` boolean assign. Toggle it from your
LiveView event handlers:

    # Open the dialog
    {:noreply, assign(socket, :show_confirm, true)}

    # Close the dialog
    {:noreply, assign(socket, :show_confirm, false)}

## ARIA Linkage

Always set `aria-labelledby` and `aria-describedby` on `alert_dialog/1`
pointing at the `id` values of `alert_dialog_title/1` and
`alert_dialog_description/1` respectively. This is required by the
WAI-ARIA `alertdialog` specification and enables screen readers to correctly
announce the dialog's purpose when it opens.

## Accessibility

- `role="alertdialog"` signals to assistive technology that focus should
  immediately move to the dialog when it opens (stronger than `role="dialog"`)
- `aria-modal="true"` tells screen readers to restrict virtual cursor
  navigation to the dialog content
- Cancel is visually first (on mobile, stacked below action) but users should
  be able to press `Escape` to cancel without moving the mouse
- On desktop the action buttons render horizontally; on mobile they stack
  vertically (action on top, cancel below) for thumb accessibility
- The default focus lands on the first focusable element — by convention,
  the Cancel button — so the safe action is immediately accessible

# `alert_dialog`

Root container for the Alert Dialog modal.

Renders a full-screen fixed overlay with `role="alertdialog"` and
`aria-modal="true"`. Focus trap, Escape key handling, and scroll locking are
provided by the `PhiaDialog` JavaScript hook.

Set `aria-labelledby` to the `:id` of `alert_dialog_title/1` and
`aria-describedby` to the `:id` of `alert_dialog_description/1` for
correct ARIA linkage — screen readers announce the title when the dialog
opens and the description when the user explores its contents.

The dialog is shown when `open={true}` and hidden when `open={false}`.
Toggle via a LiveView assign — the hook does not manage visibility state.

## Attributes

* `id` (`:string`) (required) - Unique dialog ID — the `PhiaDialog` hook mount point.
* `open` (`:boolean`) - Whether the alert dialog is currently visible. When `false`, the outer
  container gets the `hidden` class. Toggle from your LiveView event handlers.

  Defaults to `false`.
* `class` (`:string`) - Additional CSS classes for the dialog panel. Defaults to `nil`.
* Global attributes are accepted. Extra HTML attributes forwarded to the root element. Use this for required
  ARIA attributes: `aria-labelledby="{title-id}"` and
  `aria-describedby="{description-id}"`.

## Slots

* `inner_block` (required) - Alert dialog content: header, footer sub-components.

# `alert_dialog_action`

Confirm / action button for the alert dialog.

This is the button that executes the critical action. Wire it with `phx-click`
to handle the confirmed action in your LiveView. Pass any data via `phx-value-*`.

Use `variant="destructive"` for irreversible actions (deletes, revocations):

    <.alert_dialog_action variant="destructive" phx-click="confirm_delete" phx-value-id={@id}>
      Delete permanently
    </.alert_dialog_action>

Use `variant="default"` for confirmations without a destructive consequence:

    <.alert_dialog_action phx-click="confirm_publish">
      Publish now
    </.alert_dialog_action>

## Attributes

* `variant` (`:string`) - Visual style for the action button:
  - `"default"` — primary colour (blue/brand); use for confirmations with no data loss
  - `"destructive"` — danger red; use for deletes, revocations, irreversible changes

  Defaults to `"default"`. Must be one of `"default"`, or `"destructive"`.
* `class` (`:string`) - Additional CSS classes. Defaults to `nil`.
* Global attributes are accepted. Extra HTML attributes forwarded to the `<button>`.
## Slots

* `inner_block` (required) - Button label text.

# `alert_dialog_cancel`

Cancel button for the alert dialog.

This button should close the dialog without performing any destructive action.
Wire with `phx-click` to set your visibility assign to `false`:

    <.alert_dialog_cancel phx-click="cancel_delete">
      Cancel
    </.alert_dialog_cancel>

    # In the LiveView
    def handle_event("cancel_delete", _params, socket) do
      {:noreply, assign(socket, :show_confirm, false)}
    end

The cancel button uses a neutral outline style so it reads as the "safe"
option — users can always press `Escape` to cancel as well.

## Attributes

* `class` (`:string`) - Additional CSS classes. Defaults to `nil`.
* Global attributes are accepted. Extra HTML attributes forwarded to the `<button>`.
## Slots

* `inner_block` (required) - Button label text — typically 'Cancel' or 'Keep'.

# `alert_dialog_description`

Supporting text below the alert dialog title.

Set `:id` and reference it via `aria-describedby` on the parent
`alert_dialog/1`. Screen readers announce this when the user explores the
dialog, providing context about the consequences of the action.

    <.alert_dialog aria-describedby="delete-desc" ...>
      <.alert_dialog_description id="delete-desc">
        This action cannot be undone. The record will be permanently removed.
      </.alert_dialog_description>
    </.alert_dialog>

Write descriptions that help users make an informed decision: explain
consequences, mention what will be lost, and avoid technical jargon.

## Attributes

* `id` (`:string`) - Element ID referenced by `aria-describedby` on the parent `alert_dialog/1`. Defaults to `nil`.
* `class` (`:string`) - Additional CSS classes. Defaults to `nil`.
* Global attributes are accepted. Extra HTML attributes forwarded to the `<p>`.
## Slots

* `inner_block` (required) - Description text — 1–3 sentences explaining consequences.

# `alert_dialog_footer`

Action row at the bottom of the alert dialog.

On mobile: buttons stack vertically (action on top, cancel below) for thumb
reachability. On desktop: buttons render horizontally, right-aligned
(`sm:flex-row sm:justify-end sm:space-x-2`).

By convention, place `alert_dialog_cancel/1` before `alert_dialog_action/1`
in the template — the flexbox reverse order handles the mobile layout.

## Attributes

* `class` (`:string`) - Additional CSS classes. Defaults to `nil`.
* Global attributes are accepted. Extra HTML attributes forwarded to the footer `<div>`.
## Slots

* `inner_block` (required) - `alert_dialog_cancel/1` and `alert_dialog_action/1`.

# `alert_dialog_header`

Layout container for the alert dialog title and description.

Stacks title and description vertically with `space-y-2`. Text alignment is
centered on mobile (`text-center`) and left-aligned on desktop (`sm:text-left`)
for optimal readability across screen sizes.

## Attributes

* `class` (`:string`) - Additional CSS classes. Defaults to `nil`.
* Global attributes are accepted. Extra HTML attributes forwarded to the header `<div>`.
## Slots

* `inner_block` (required) - `alert_dialog_title/1` and `alert_dialog_description/1`.

# `alert_dialog_title`

Alert dialog heading rendered as `<h2>`.

Set `:id` and reference it via `aria-labelledby` on the parent
`alert_dialog/1`. This is how screen readers know the dialog's name — they
announce it when the dialog opens and whenever the user queries the current
dialog context.

    <.alert_dialog aria-labelledby="delete-title" ...>
      <.alert_dialog_title id="delete-title">
        Permanently delete?
      </.alert_dialog_title>
    </.alert_dialog>

Keep the title short and specific — it should clearly state what the dialog
is about: "Delete Project?", "Revoke Access?", "Unsaved Changes".

## Attributes

* `id` (`:string`) - Element ID referenced by `aria-labelledby` on the parent `alert_dialog/1`. Defaults to `nil`.
* `class` (`:string`) - Additional CSS classes. Defaults to `nil`.
* Global attributes are accepted. Extra HTML attributes forwarded to the `<h2>`.
## Slots

* `inner_block` (required) - Title text.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
