A modal panel on the native <dialog>, so the top layer, backdrop, inert page behind and Escape come from the platform. One primitive absorbs Dialog, Sheet and Drawer through side, and Alert Dialog through alert.
A dialog is never wider than the viewport less 1rem each side, and a form taller than the screen scrolls inside the dialog while its title and footer stay put. A sheet (side) is 85 percent of the width.
Opens or closes the dialog through showModal(). Reflected, so an Alpine binding or a server render can drive it.
side
"none" | "top" | "right" | "bottom" | "left"
"none"
Pins the panel to an edge as a sheet or drawer. An unknown value falls back to none.
alert
boolean
false
Gives the dialog role="alertdialog" and refuses Escape and backdrop dismissal.
dismissible
boolean
true
When false, Escape, the backdrop and the close button no longer request hui-dismiss, and the close button is not drawn.
hide-close
boolean
false
Leaves out the close button a dismissible dialog draws at its top right. An alert dialog never has one.
data-dismiss
boolean, on a slotted control
-
Not an attribute of the dialog: put it on a control inside it - a footer Cancel - and clicking that control closes the dialog through hide(). Works in an alert dialog too, whose only way out is its own buttons.
Properties5
Properties
Name
Type
Default
Description
open
boolean
false
Reflects to the open attribute.
side
DialogSide
'none'
Reflects to the side attribute.
alert
boolean
false
Reflects to the alert attribute.
dismissible
boolean
true
Reflects to the dismissible attribute.
hideClose
boolean
false
Reflects to the hide-close attribute.
Methods2
Methods
Name
Type
Description
show(from?)
-
Opens the dialog and records from as the element focus returns to. The focused element is the fallback when from is omitted.
hide()
-
Closes the dialog without asking, bypassing hui-dismiss. This is the route an unsaved-changes guard cannot refuse.
Events4
Events
Name
Description
hui-open
The dialog entered the top layer. It bubbles and is composed, and carries no detail.
hui-close
The dialog left the top layer, after focus has been handed back. It bubbles and is composed, and carries no detail.
hui-dismiss
A dismissal was requested by Escape, the backdrop or the close button. It bubbles, is composed and is cancelable; calling preventDefault() keeps the dialog open. Never fired while alert or when dismissible is false.
click on [slot="trigger"]
Activating the slotted trigger opens the dialog and passes the clicked element as the focus-return target.
Slots5
Slots
Name
Description
trigger
The control that opens the dialog. It stays in the light DOM so the host owns its markup.
title
The heading, styled through part="title".
description
Supporting text under the title, styled through part="description".
(default)
The body content, wrapped in part="body".
footer
The action row, wrapped in part="footer" and laid out end-aligned.
The server owns the whole light-DOM surface: the slot="trigger" control, the title, description, default and footer content, and the side, alert and dismissible attributes. Render <hui-dialog open> and it opens through showModal() as it upgrades, so a state the host persisted returns without a flash, and read open, side, alert and dismissible back because all four reflect. The dialog is not form-associated and submits nothing under any name of its own, though a <form hx-post> inside its body submits exactly as it would anywhere else. An hx-swap that removes an open dialog runs disconnectedCallback(), which closes the native element, so the top layer and the inert page behind are never stranded; a swap that replaces the slotted trigger while it is open means focus falls back to <body> on close, because restoration checks isConnected rather than node identity. Re-rendering it mid-interaction gives whatever state the server echoed: emit open again to keep it up, omit it and the element closes.
Accessibility
Built on native <dialog>.showModal(), so the top layer, 50 percent backdrop, inertness of the page behind and Escape are the platform's.
The dialog carries role="dialog", or role="alertdialog" when alert is set.
Focus returns to the recorded trigger on close; when an hx-swap has removed the trigger, focus falls back to <body> rather than a detached node.
hui-dismiss is the cancellable dismissal, so an unsaved-changes guard can keep the dialog up; hide() is the unconditional close.
Removing an open dialog from the DOM closes it, so nothing is ever stranded in the top layer with the page still inert.
Keyboard
Keyboard
Keys
Action
Escape
Requests dismissal through hui-dismiss. The platform event is always prevented so an alert dialog stays up and a guard can refuse.
Tab, Shift+Tab
Cycles within the modal; the rest of the document is inert while it is open.
Enter, Space
Activates the focused control in the dialog, including the slotted trigger that opens it.
Gotchas
The title and description slots render visually but are never wired to the native <dialog> as aria-labelledby or aria-describedby; a host that needs the dialog announced with a name must set aria-label or aria-labelledby on hui-dialog itself.