Dialog

A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.

Usage

HTML + Javascript

This component requires Javascript.

The component is structured as such:

  • A <div class="dialog"> which wraps around the entire component and holds it state (e.g. open/close).
  • A <button> that acts as the trigger to open or close the popover.
  • A <div role="dialog"> that holds the dialog itself. It also acts as the backdrop for the dialog.
  • Inside of it, we have:
    • A <header> that contains the title (<h2>) and description (<p>) of the dialog.
    • A <section> for the content.
    • A <footer> that usually contains actions.
    • A close <button>.

You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind, x-data, @click).

<div id="demo-dialog-edit-profile" x-data="dialog(false, true)" x-bind="$main" class="dialog" x-ref="myDialog">
  <button type="button" aria-expanded="false" aria-controls="demo-dialog-edit-profile-dialog" x-bind="$trigger" class="btn-outline">Edit Profile</button>

  <div role="dialog" id="demo-dialog-edit-profile-dialog" tabindex="-1" aria-modal="true" aria-labelledby="demo-dialog-edit-profile-title" inert x-bind="$content">
    <article class="w-full sm:max-w-[425px] max-h-[612px]">
      <header>
        <h2 id="demo-dialog-edit-profile-title">Edit profile</h2>
        <p>Make changes to your profile here. Click save when you're done.</p>
      </header>

      <section>
        <form class="form grid gap-4">
          <div class="grid gap-3">
            <label for="demo-dialog-edit-profile-name">Name</label>
            <input type="text" value="Pedro Duarte" id="demo-dialog-edit-profile-name" x-ref="focusOnOpen" />
          </div>
          <div class="grid gap-3">
            <label for="demo-dialog-edit-profile-username">Username</label>
            <input type="text" value="@peduarte" id="demo-dialog-edit-profile-username" />
          </div>
        </form>
      </section>

      <footer>
        <button class="btn-outline" @click="open = false">Cancel</button>
        <button class="btn" @click="open = false">Save changes</button>
      </footer>

      <button @click="hide()" aria-label="Close dialog">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x">
          <path d="M18 6 6 18" />
          <path d="m6 6 12 12" />
        </svg>
      </button>
    </article>
  </div>
</div>

The component will dispatch dialog:opened and dialog:closed events when the dialog is opened or closed. You can also dispatch dialog:open and dialog:close events to open or close the dialog programmatically. You will need the element to have an id to be able to target it (e.g. <div id="my-dialog" class="dialog">):

<button type="button" x-init @click="$dispatch('dialog:open', { id: 'my-dialog' })">Open #my-dialog</button>

Jinja and Nunjucks

You can use the dialog() Nunjucks or Jinja macro for this component.

{% set footer %}
  <button class="btn-outline" @click="open = false">Cancel</button>
  <button class="btn" @click="open = false">Save changes</button>
{% endset %}
{% call dialog(
  id="demo-dialog-edit-profile",
  title="Edit profile",
  description="Make changes to your profile here. Click save when you're done.",
  main_attrs={"x-ref": "myDialog"},
  trigger="Edit Profile",
  trigger_attrs={"class": "btn-outline"},
  content_attrs={"class": "w-full sm:max-w-[425px] max-h-[612px]"},
  footer=footer,
  register_on=["alpine:init", "htmx:afterSwap"]
) %}
<form class="form grid gap-4">
  <div class="grid gap-3">
    <label for="demo-dialog-edit-profile-name">Name</label>
    <input type="text" value="Pedro Duarte" id="demo-dialog-edit-profile-name" />
  </div>
  <div class="grid gap-3">
    <label for="demo-dialog-edit-profile-username">Username</label>
    <input type="text" value="@peduarte" id="demo-dialog-edit-profile-username" />
  </div>
</form>
{% endcall %}

Examples

Scrollable content