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

Accessible Dialog (Modal) component following the WAI-ARIA Dialog pattern.

Uses `Phoenix.LiveView.JS` for open/close transitions and the `PhiaDialog`
JavaScript Hook for focus trap, keyboard navigation, and scroll locking.

## Registration in app.js

After running `mix phia.add dialog`, register the hook in your LiveSocket:

    import PhiaDialog from "./phia_hooks/dialog.js"

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

## Example

    <.dialog id="confirm-delete">
      <.dialog_trigger for="confirm-delete">
        <.button variant={:destructive}>Delete</.button>
      </.dialog_trigger>

      <.dialog_content id="confirm-delete">
        <.dialog_header>
          <.dialog_title id="confirm-delete-title">Delete Item</.dialog_title>
          <.dialog_description id="confirm-delete-description">
            This action cannot be undone.
          </.dialog_description>
        </.dialog_header>
        <.dialog_footer>
          <.dialog_close for="confirm-delete">Cancel</.dialog_close>
          <.button variant={:destructive} phx-click="delete">Delete</.button>
        </.dialog_footer>
      </.dialog_content>
    </.dialog>

## Sub-components

| Function              | Purpose                                      |
|-----------------------|----------------------------------------------|
| `dialog/1`            | Hook anchor, outer container                 |
| `dialog_trigger/1`    | Opens the dialog via JS.remove_class         |
| `dialog_content/1`    | Overlay + panel (hidden by default)          |
| `dialog_header/1`     | Title + description layout container         |
| `dialog_title/1`      | `<h2>` heading (set id for ARIA linkage)     |
| `dialog_description/1`| `<p>` supporting text (set id for ARIA)      |
| `dialog_footer/1`     | Action row (close button, confirmations)     |
| `dialog_close/1`      | Closes the dialog via JS.add_class           |

# `dialog`

Outer container for the Dialog. Binds the `PhiaDialog` JavaScript Hook.

Must wrap both `dialog_trigger/1` and `dialog_content/1`.

## Attributes

* `id` (`:string`) (required) - Unique ID used as the hook anchor.
* `class` (`:string`) - Additional CSS classes. Defaults to `nil`.
## Slots

* `inner_block` (required)

# `dialog_close`

Closes the dialog via `JS.add_class("hidden")` on `#dialog-{for}`.
The Escape key is also handled by the `PhiaDialog` JS Hook.

## Attributes

* `for` (`:string`) (required) - ID of the dialog to close (matches dialog/1's :id).
* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `dialog_content`

The dialog surface: renders the overlay backdrop and the modal panel.

Hidden by default. Shown by `dialog_trigger/1` via `JS.remove_class("hidden")`.

The outer container id is `"dialog-{id}"` (prefixed) so that
`dialog_trigger/1` and `dialog_close/1` can target it.

## Size variants

| Value       | Max width                  |
|-------------|----------------------------|
| `"sm"`      | `max-w-sm`                 |
| `"default"` | `max-w-lg` (default)       |
| `"lg"`      | `max-w-2xl`                |
| `"xl"`      | `max-w-4xl`                |
| `"full"`    | `max-w-[calc(100vw-2rem)]` |

## Attributes

* `id` (`:string`) (required) - ID matching the parent dialog/1's :id.
* `size` (`:string`) - Width of the dialog panel. Defaults to `"default"`. Must be one of `"sm"`, `"default"`, `"lg"`, `"xl"`, or `"full"`.
* `show_close_button` (`:boolean`) - Render the default X close button in the top-right corner of the panel. Defaults to `true`.
* `scrollable` (`:boolean`) - Add overflow-y-auto to the panel for content that may overflow the viewport. Defaults to `false`.
* `full_screen_mobile` (`:boolean`) - When `true`, the dialog panel fills the entire screen on mobile viewports
  (`fixed inset-0 rounded-none`) and reverts to its normal centered behavior
  on `sm:` and wider screens (`sm:relative sm:inset-auto sm:rounded-lg`).
  Ideal for complex forms or content that benefits from full viewport space
  on small devices.

  Defaults to `false`.
* `class` (`:string`) - Additional classes for the panel. Defaults to `nil`.
## Slots

* `inner_block` (required)

# `dialog_description`

Supporting text below the title. Set `:id` to `"{dialog-id}-description"` for ARIA linkage.

## Attributes

* `id` (`:string`) - Defaults to `nil`.
* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `dialog_footer`

Action row at the bottom of the dialog for confirmation and close buttons.

Renders a `<div>` with `flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2`.

**Responsive stacking behaviour:**
- On mobile (`< sm`): buttons stack vertically in reverse DOM order, so
  the primary action (last in the template) appears visually at the top —
  thumb-friendly and consistent with mobile modal conventions.
- On `sm`+ screens: buttons arrange horizontally, aligned to the right,
  with `space-x-2` (8px) gaps.

Place the cancel/close action first in the template and the primary
(submit/confirm) action last. The CSS reversal puts the primary action
at the top on mobile automatically:

## Example

    <.dialog_footer>
      <%!-- Cancel appears second on desktop, first (top) on mobile --%>
      <.dialog_close for="confirm-delete">Cancel</.dialog_close>
      <%!-- Primary action appears first on desktop (rightmost), top on mobile --%>
      <.button variant={:destructive} phx-click="delete_item">
        Delete
      </.button>
    </.dialog_footer>

## Attributes

* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `dialog_header`

Layout container for the dialog title and description.

Renders a `<div>` with `flex flex-col space-y-1.5 text-center sm:text-left`.
On mobile viewports the content is centred; on `sm` and wider it aligns
to the left, matching the standard modal convention for each form factor.

Wrap `dialog_title/1` and `dialog_description/1` inside this component.
Both must have their `:id` set (to `"{dialog-id}-title"` and
`"{dialog-id}-description"`) so the panel's `aria-labelledby` and
`aria-describedby` attributes resolve correctly.

## Example

    <.dialog_header>
      <.dialog_title id="confirm-delete-title">
        Delete project?
      </.dialog_title>
      <.dialog_description id="confirm-delete-description">
        All data will be permanently removed. This cannot be undone.
      </.dialog_description>
    </.dialog_header>

## Attributes

* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `dialog_title`

Dialog heading (`<h2>`). Set `:id` to `"{dialog-id}-title"` for ARIA linkage.

## Attributes

* `id` (`:string`) - Defaults to `nil`.
* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `dialog_trigger`

Opens the dialog by calling `JS.remove_class("hidden")` on `#dialog-{for}`.

## Attributes

* `for` (`:string`) (required) - ID of the dialog to open (matches dialog/1's :id).
* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

---

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