JS API
Options
modal
- Type:
Boolean - Default:
true
Open the dialog as a true modal with showModal(): the platform then handles the focus trap, makes the rest of the page inert, restores focus on close and paints the dialog in the top layer. Set it to false (via data-option-no-modal) to use show() instead, which keeps the rest of the page interactive (useful for a slide-in nav).
<dialog data-component="Action Dialog"
data-option-no-modal>
...
</dialog>trapFocus
- Type:
Boolean - Default:
true
Trap the tabulation inside the dialog. This is only meaningful on the non-modal path (modal: false): showModal() already traps focus natively, so the option is a no-op when modal is true. On the non-modal path it saves the active element on open, keeps Tab inside the dialog while open, and restores focus on close.
<dialog data-component="Action Dialog"
data-option-no-modal
data-option-no-trap-focus>
...
</dialog>scrollLock
- Type:
Boolean - Default:
true
Lock the scroll on the document element while the dialog is open by toggling document.documentElement.style.overflow. Set it to false with data-option-no-scroll-lock to leave the page scrollable.
Properties
dialog
- Type:
HTMLDialogElement
A getter returning the native <dialog> element (this.$el).
transitions
- Type:
Array<Transition | ViewTransition>
A getter returning every Transition and ViewTransition child the dialog orchestrates.
Methods
open
- Returns
Promise<void>
Open the dialog: call showModal() (or show() when modal is false), lock the scroll, emit open, then run every child's enter() alongside the waitUntil extensions. A no-op if the dialog is already open. Resolves once the enter choreography has finished.
close
- Returns
Promise<void>
Close the dialog: emit close, run every child's leave() alongside the waitUntil extensions, then call dialog.close(), release the focus-trap (non-modal path) and unlock the scroll. A no-op if the dialog is already closed, and concurrent calls await the same run. Resolves once closed.
toggle
- Returns
Promise<void>
Call close() if the dialog is open, open() otherwise.
Events
Both lifecycle events bubble up the DOM tree, so ancestors can route them and any listener up the tree can join the choreography.
open
Emitted when the dialog starts opening, before the enter transitions run. The detail carries a waitUntil function.
close
Emitted when the dialog starts closing, before the leave transitions run. The detail carries a waitUntil function.
Extending the choreography with waitUntil
Both lifecycle events expose event.detail.waitUntil(x), modeled on the Service Worker ExtendableEvent. Any listener can register an extension while the event dispatches, and the dialog awaits it alongside its transition children — on close, the native dialog stays painted until every registered extension settles. This lets any component participate in the open and close choreography without being a declared child.
waitUntil() accepts either form:
- a promise (any thenable), awaited as is
- a transitioner: a duck-typed object with
enter()andleave()methods, e.g.MotionViewfrom@studiometa/ui-motion— the dialog follows its lifecycle by awaitingenter()onopenandleave()onclose
For example, a Motion box can spring in and out with the dialog through two Action attributes:
<dialog
data-component="Action Dialog"
data-on:open="Motion(#box)->event.detail.waitUntil(target.play())"
data-on:close="Motion(#box)->event.detail.waitUntil(target.reverse())"
data-on:cancel.prevent="Dialog.close()">
…
</dialog>With the transitioner form, a single handler is enough — pass the transitioner itself and the dialog picks the right phase, like a MotionView navigation panel animating out before the dialog hides:
<dialog
data-component="Action Dialog"
data-on:close="MotionView(#nav)->event.detail.waitUntil(target)"
data-on:cancel.prevent="Dialog.close()">
<nav id="nav">…</nav>
</dialog>Registration is only valid synchronously, while the event dispatches; later calls warn and are ignored. A rejected extension never blocks the choreography: the rejection is reported with a warning and the run completes.