bwc-modal
A dialog overlay: a trigger button opens a native <dialog> popup with
showModal(). Use it for confirmations, forms, or anything needing modal
focus and backdrop dismissal.
import "basic-web-components/modal";Usage
Uncontrolled:
<bwc-modal>
<button slot="trigger">Open</button>
<dialog slot="popup">
<header data-modal-header>
<h2 data-title>Confirm changes?</h2>
<button data-close title="Close">×</button>
</header>
<div data-modal-content>
<p data-description>Review the changes before continuing.</p>
</div>
<div data-modal-actions>
<button data-close>Cancel</button>
<button data-action data-close>Confirm</button>
</div>
</dialog>
</bwc-modal>Controlled:
<bwc-modal id="m" open>
<button slot="trigger">Open</button>
<dialog slot="popup">
<p>Confirm?</p>
<button data-close>Close</button>
</dialog>
</bwc-modal>
<script>
const m = document.getElementById("m");
m.onOpenChange = (open) => {
open ? m.setAttribute("open", "") : m.removeAttribute("open");
};
</script>API
Properties / attributes
| Property | Attribute | Type | Default | Notes |
|---|---|---|---|---|
open |
open |
boolean |
false |
Present (or assigned) → controlled mode. |
defaultOpen |
default-open |
boolean |
false |
Seed for uncontrolled mode. |
disabled |
disabled |
boolean |
false |
Disables the trigger; open/close requests are ignored. |
triggerClass |
trigger-class |
string |
"" |
Extra classes for the trigger. |
popupClass |
popup-class |
string |
"" |
Extra classes for the dialog. |
closeClass |
close-class |
string |
"" |
Extra classes for each data-close button. |
onOpenChange |
— (property only) | ((open: boolean) => void) | null |
null |
Must be a function or null. |
Events
| Name | Detail | Bubbles / composed |
|---|---|---|
open-change |
{ open: boolean } |
yes / yes |
Emitted for trigger clicks, data-close clicks, backdrop clicks, and native
cancel (Escape) / close dismissal. In controlled mode state only changes
when you update open.
Slots
| Slot | Required element |
|---|---|
trigger |
exactly one button |
popup |
exactly one dialog |
Close buttons are any <button data-close> inside the dialog. Wrong count or
wrong element type throws TypeError on connect. Replacing slotted nodes
rebinds automatically (an open dialog is closed and reopened on the new node).
Accessibility
- Trigger gets
aria-haspopup="dialog",aria-controls→ dialog id, andaria-expanded; ids anddata-testid="bwc-modal-popup"are auto-assigned. - Parts carry
modal-trigger/modal-popup/modal-closemarker classes plus stable testids; author classes are preserved. - Open/closed/disabled state mirrors as
data-open/data-closed/data-disabledon trigger and dialog. Disabled trigger uses thenot-allowedcursor. - Escape requests close (native
cancelis prevented and routed throughopen-change); backdrop clicks close; focus returns to the trigger on close; document scroll is locked while open.
Form behavior
None (dialog content participates normally if you put a <form> inside).
Errors
TypeErrorwhen the trigger/popup topology is invalid.TypeErrorwhenonOpenChangeis a non-function, non-null value.
State ownership
Without open, interactions update internal state seeded by default-open.
Assigning open permanently enters controlled mode. Property assignments and
show() / close() / toggle() apply immediately and emit open-change
when the effective state changes; other interactions only request changes in
controlled mode.