Form Dialog

Dialog with form inputs. Click "Edit Profile" to open.

<button class="btn" data-variant="default"
        data-dialog-trigger="edit-dialog"
        aria-haspopup="dialog">
  Edit Profile
</button>

<dialog id="edit-dialog" class="dialog"
        role="dialog" aria-modal="true"
        aria-labelledby="edit-title">
  <div class="dialog-content">
    <div class="dialog-header">
      <h2 class="dialog-title" id="edit-title">Edit Profile</h2>
      <p class="dialog-description">Make changes to your profile here.</p>
    </div>
    <div class="dialog-body">
      <div style="display:flex;flex-direction:column;gap:1rem;">
        <div>
          <label class="label" for="name">Name</label>
          <input class="input" id="name" type="text" value="Cody Lindley">
        </div>
        <div>
          <label class="label" for="username">Username</label>
          <input class="input" id="username" type="text" value="@codylindley">
        </div>
      </div>
    </div>
    <div class="dialog-footer">
      <button class="btn" data-variant="outline" data-dialog-close>Cancel</button>
      <button class="btn" data-variant="default" data-dialog-close>Save changes</button>
    </div>
  </div>
</dialog>

Confirmation

Destructive action confirmation. Click "Confirm Delete" to open.

<button class="btn" data-variant="outline"
        data-dialog-trigger="confirm-dialog"
        aria-haspopup="dialog">
  Confirm Delete
</button>

<dialog id="confirm-dialog" class="dialog"
        role="dialog" aria-modal="true"
        aria-labelledby="confirm-title">
  <div class="dialog-content">
    <div class="dialog-header">
      <h2 class="dialog-title" id="confirm-title">Delete account?</h2>
      <p class="dialog-description">This action cannot be undone.
        This will permanently delete your account.</p>
    </div>
    <div class="dialog-footer">
      <button class="btn" data-variant="outline"
              data-dialog-close>Cancel</button>
      <button class="btn" data-variant="destructive"
              data-dialog-close>Delete account</button>
    </div>
  </div>
</dialog>

CSS view file

/* -- Dialog component ------------------------------------------ */

@layer components {
  dialog.dialog {
    border: none;
    border-radius: var(--radius-xl);
    background-color: var(--popover);
    color: var(--popover-foreground);
    padding: 0;
    margin: auto;
    position: fixed;
    inset: 0;
    max-width: 28rem;
    width: calc(100vw - 2rem);
    max-height: calc(100vh - 2rem);
    box-shadow: 0 25px 80px oklch(0 0 0 / 0.25), 0 0 0 1px var(--border);
    opacity: 0;
    transform: translateY(-0.5rem) scale(0.98);
    transition: opacity 200ms ease, transform 200ms ease, display 200ms allow-discrete;

    &[open] {
      opacity: 1;
      transform: translateY(0) scale(1);
    }

    /* -- Sizes ------------------------------------------------ */
    &[data-size="sm"]   { max-width: 24rem; }
    &[data-size="lg"]   { max-width: 32rem; }
    &[data-size="xl"]   { max-width: 40rem; }
    &[data-size="full"] { max-width: calc(100vw - 2rem); }

    /* -- Backdrop --------------------------------------------- */
    &::backdrop {
      background: oklch(0 0 0 / 0);
      backdrop-filter: blur(0px);
      transition: all 200ms ease, display 200ms allow-discrete;
    }

    &[open]::backdrop {
      background: oklch(0 0 0 / 0.45);
      backdrop-filter: blur(3px);
    }
  }

  @starting-style {
    dialog.dialog[open] {
      opacity: 0;
      transform: translateY(-0.5rem) scale(0.98);
    }

    dialog.dialog[open]::backdrop {
      background: oklch(0 0 0 / 0);
      backdrop-filter: blur(0px);
    }
  }

  /* -- Content sections ------------------------------------- */
  .dialog-content { padding: 1.5rem; }
  .dialog-header  { margin-bottom: 1rem; }
  .dialog-title   { font-size: 1.0625rem; font-weight: 600; margin: 0 0 0.375rem; letter-spacing: -0.01em; }
  .dialog-description { font-size: 0.875rem; color: var(--muted-foreground); margin: 0; line-height: 1.6; }
  .dialog-body    { margin-top: 1rem; }
  .dialog-footer  { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1.5rem; }
}

JavaScript view file

Wire triggers via data-dialog-trigger, close buttons via data-dialog-close, and backdrop click. Focus returns to trigger on close.

// -- Dialog ---------------------------------------------------
// Wires [data-dialog-trigger] buttons to <dialog> elements.

function init() {
document.querySelectorAll('[data-dialog-trigger]:not([data-init])').forEach((trigger) => {
  trigger.dataset.init = '';
  const dialog = document.getElementById(trigger.dataset.dialogTrigger);
  if (!dialog) return;
  trigger.addEventListener('click', () => {
    dialog._trigger = trigger;
    dialog.showModal();
  });
});
document.querySelectorAll('dialog:not(.alert-dialog):not(.sheet):not([data-init])').forEach((dialog) => {
  dialog.dataset.init = '';
  dialog.addEventListener('click', (e) => {
    if (e.target === dialog) dialog.close();
  });
  dialog.querySelectorAll('[data-dialog-close]').forEach((btn) => {
    btn.addEventListener('click', () => { dialog.close(); });
  });
  dialog.addEventListener('close', () => {
    if (dialog._trigger) dialog._trigger.focus();
  });
});
}

init();
new MutationObserver(init).observe(document, { childList: true, subtree: true });

Edit Profile

Make changes to your profile here. Click save when done.

Delete account?

This action cannot be undone. This will permanently delete your account and remove all associated data from our servers.