stellify/ui

Components

Dialog

A modal or non-modal dialog component built on the native <dialog> element with trigger integration, light dismiss, and event hooks.

Preview

Delete your account?

This action cannot be undone. All your data will be permanently deleted.

Usage

<button type="button" data-dialog-trigger="confirm-delete">
  Delete account
</button>

<st-dialog id="confirm-delete">
  <h2 class="text-lg font-semibold">Delete your account?</h2>
  <p class="mt-2 text-sm text-muted-foreground">
    This action cannot be undone.
  </p>

  <form method="dialog" class="mt-4 flex justify-end gap-2">
    <button type="submit" value="cancel">Cancel</button>
    <button type="submit" value="confirm">Delete</button>
  </form>
</st-dialog>

Triggers

Any element with data-dialog-trigger="<dialog-id>" opens the dialog when clicked. Multiple triggers can target the same dialog.

By default, dialogs open as modals. To open non-modal, add data-dialog-modal="false" to the trigger.

<!-- Opens as modal (default) -->
<button data-dialog-trigger="my-dialog">Open Modal</button>

<!-- Opens as non-modal -->
<button data-dialog-trigger="my-dialog" data-dialog-modal="false">
  Open Non-Modal
</button>

Form Integration

Use <form method="dialog"> to handle Cancel / Confirm patterns. The submitter button's value is exposed as the dialog's return value:

const dialog = document.getElementById('confirm-delete')

dialog.addEventListener('st-dialog:close', (e) => {
  if (e.detail.returnValue === 'confirm') {
    // perform action
  }
})

Programmatic API

const dialog = document.getElementById('my-dialog')

dialog.open()                  // Open as modal
dialog.openNonModal()          // Open as non-modal
dialog.close()                 // Close without return value
dialog.close('confirmed')      // Close with return value
dialog.isOpen                  // Check open state

Light Dismiss

By default, clicking outside the dialog (on the backdrop) closes it. To disable for critical dialogs:

<st-dialog id="critical" data-st-dialog-no-light-dismiss>
  <!-- User must explicitly cancel or confirm -->
</st-dialog>

Events

All events bubble.

Event Detail
st-dialog:open { modal: boolean }
st-dialog:close { returnValue: string }

Styling

Default styling matches the shadcn popover aesthetic. Override on the inner <dialog> element:

st-dialog dialog {
  max-width: 28rem;
  padding: 2rem;
}

For full control, write your <dialog> explicitly inside:

<st-dialog id="custom">
  <dialog class="my-custom-dialog">
    <!-- your styling, your structure -->
  </dialog>
</st-dialog>