DhModal
Native <dialog> with sizes, slots (header / body / footer), and a confirmation pattern. Browser handles focus trap + aria-modal + ESC.
Sizes
<DhModal open={isOpen} title="Small modal" size="sm" onclose={() => (isOpen = false)}>
<p>Small modals work for quick confirmations.</p>
{#snippet footer()}
<DhButton variant="ghost" text="Close" onclick={() => (isOpen = false)} />
{/snippet}
</DhModal>Confirmation pattern
Compose DhModal with DhButton. Supports a 'don't ask again' checkbox and a loading state that blocks dismissal while in flight.
<DhModal
open={confirmOpen}
title="Delete this item?"
dismissOnBackground={!busy}
closeOnEsc={!busy}
onclose={() => !busy && (confirmOpen = false)}
>
<p>This cannot be undone.</p>
<label>
<input type="checkbox" bind:checked={dontAskAgain} disabled={busy} />
Don't ask again
</label>
{#snippet footer()}
<DhButton variant="ghost" text="Cancel" disabled={busy}
onclick={() => (confirmOpen = false)} />
<DhButton variant="error" text="Delete" startIcon="trash-2"
loading={busy} onclick={confirmDelete} />
{/snippet}
</DhModal>Sticky modal
Disable background click, ESC dismiss, and the close button to force a choice.
<DhModal
open={stickyOpen}
title="You must choose"
dismissOnBackground={false}
closeOnEsc={false}
showCloseButton={false}
onclose={() => (stickyOpen = false)}
>
...
</DhModal>Loading body
Use DhSpinner inside the modal's body for async views.
<DhModal open={loadingOpen} title="Fetching...">
<DhSpinner label="Loading data..." />
</DhModal>