kit-dialog
<kit-dialog id="confirm"> <h2>Are you sure?</h2> <kit-button meta-command="dialog.close" meta-prop-target="confirm">Cancel</kit-button> <kit-button class="danger">Yes, delete</kit-button></kit-dialog><kit-dialog> wraps a native <dialog>. The wrapper provides:
- Declarative open/close via the
openproperty. - A built-in
dialogModule()that handlesdialog.openanddialog.closecommands by id. - A
closeEventattribute that emits a Kitsune event on close.
It does not replace the platform’s behavior. Focus management, Escape to close, backdrop, and modal inerting all come from the native <dialog>.
Package
Section titled “Package”@atheory-ai/kitsune-ui
import '@atheory-ai/kitsune-ui'import { KitDialogElement, dialogModule } from '@atheory-ai/kitsune-ui'
shell.modules = [dialogModule(), /* others */]Attributes
Section titled “Attributes”| Attribute | Type | Default | Notes |
|---|---|---|---|
open | boolean | false | Whether the dialog is currently open. Reflects. |
modal | "modal" | "non-modal" | "modal" | modal calls showModal(); non-modal calls show(). Reflects. |
close-event | string | — | If set, emits a meta:event of this type when the dialog closes. |
id | string | — | The id used by dialog.open / dialog.close commands to target this dialog. |
Methods
Section titled “Methods”| Method | Notes |
|---|---|
show() | Opens the dialog (modal or not, based on modal attr). |
close(returnValue?: string) | Closes the dialog with an optional return value. |
Events
Section titled “Events”| Event | Detail | Fires |
|---|---|---|
kit-dialog-close | — | When the dialog closes (Escape, native button, programmatic). Composed and bubbles. |
meta:event | { type: <closeEvent> } | When the dialog closes, only if close-event is set. Composed and bubbles. |
CSS custom properties
Section titled “CSS custom properties”| Property | Default |
|---|---|
--kit-color-surface | Canvas |
--kit-color-text | CanvasText |
--kit-color-border | CanvasText |
--kit-radius-md | 0.5rem |
--kit-space-4 | 1rem |
--kit-focus-ring | Highlight |
Style the backdrop with ::backdrop on the inner <dialog>, but you may need to use the ::part(backdrop) pattern in a future version. Today, override :host dialog::backdrop from the parent.
Declarative open/close via metadata
Section titled “Declarative open/close via metadata”The simplest pattern. With dialogModule() installed:
<kit-button meta-command="dialog.open" meta-prop-target="my-dialog">Open</kit-button>
<kit-dialog id="my-dialog"> <h2>Hello</h2> <kit-button meta-command="dialog.close" meta-prop-target="my-dialog">Close</kit-button></kit-dialog>No JS handler needed. dialogModule() finds the dialog by id and calls show()/close().
Programmatic open/close
Section titled “Programmatic open/close”const dialog = document.getElementById('my-dialog') as KitDialogElementdialog.show()// laterdialog.close()Or via the runtime:
await shell.runtime.command({ type: 'dialog.open', payload: { target: 'my-dialog' } })await shell.runtime.command({ type: 'dialog.close', payload: { target: 'my-dialog' } })Emitting on close
Section titled “Emitting on close”<kit-dialog id="confirm" close-event="confirm.dismissed"> <!-- ... --></kit-dialog>When the dialog closes (Escape, button, programmatic), it dispatches a composed meta:event with type: 'confirm.dismissed'. The boundary picks it up and emits a runtime event.
This is a one-line way to track “dialog dismissed” interactions.
With <form method="dialog">
Section titled “With <form method="dialog">”Native pattern for confirms:
<kit-dialog id="confirm"> <form method="dialog"> <p>Delete this note?</p> <menu> <kit-button type="submit" value="cancel">Cancel</kit-button> <kit-button type="submit" value="confirm" class="danger">Yes</kit-button> </menu> </form></kit-dialog>The dialog’s close event has returnValue set to the clicked button’s value. The native dialog handles closing automatically.
Accessibility notes
Section titled “Accessibility notes”showModal()traps focus, inerts the rest of the page, and announces as a dialog.Escapecloses the dialog (browser default).- Use a real
<h2>heading inside; it acts as the dialog’s accessible name when it hasaria-labelledby(which the underlying<dialog>infers in modern browsers). - Don’t add custom focus traps — the native dialog already does it.