Dialog
Modal dialogs with focus trapping and accessible announcements.
Usage
<w-dialog label="Confirm Action">
<w-slot trigger><button>Open Dialog</button></w-slot>
<w-slot body>
<div>
<h2>Are you sure?</h2>
<p>This action cannot be undone.</p>
<w-slot close><button>Cancel</button></w-slot>
<button>Confirm</button>
</div>
</w-slot>
</w-dialog>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Whether the dialog is open |
modal | boolean | true | Trap focus and add backdrop |
persistent | boolean | false | Prevent closing via Escape or outside click |
close-on-escape | boolean | true | Close when Escape is pressed |
close-on-outside-click | boolean | true | Close when clicking outside |
return-focus | boolean | true | Return focus to trigger on close |
label | string | "" | Accessible label for the dialog |
Slots
| Slot | Element | Description |
|---|---|---|
trigger | Button | Element that opens the dialog |
content | Any | Dialog content (teleported to portal) |
close | Button | Close button inside content |
Events
| Event | Description |
|---|---|
open | Fired when dialog opens |
close | Fired when dialog closes |
Methods
| Method | Description |
|---|---|
showModal() | Open the dialog |
close() | Close the dialog |
toggle(force?) | Toggle open state, optionally force a state |
Keyboard
| Key | Action |
|---|---|
Enter / Space | Open dialog (on trigger) |
Escape | Close dialog |
Tab | Cycle through focusable elements (trapped) |
CSS Transitions
The dialog supports CSS transitions using these classes:
/* Enter transition */
.dialog-enter {
}
.dialog-enter-from {
opacity: 0;
transform: scale(0.95);
}
.dialog-enter-to {
opacity: 1;
transform: scale(1);
}
/* Leave transition */
.dialog-leave {
}
.dialog-leave-from {
opacity: 1;
}
.dialog-leave-to {
opacity: 0;
}
Examples
Non-Modal Dialog
<w-dialog modal="false" label="Side Panel">
<w-slot trigger><button>Open Panel</button></w-slot>
<w-slot body>
<div>
<!-- No focus trap, no backdrop -->
</div>
</w-slot>
</w-dialog>
Programmatic Control
<w-dialog id="my-dialog" label="Alert">
<w-slot body>
<div>
<p>Something happened!</p>
<w-slot close><button>OK</button></w-slot>
</div>
</w-slot>
</w-dialog>
<script>
const dialog = document.getElementById("my-dialog");
dialog.showModal(); // Open
dialog.close(); // Close
</script>
Persistent Dialog
A persistent dialog cannot be dismissed by pressing Escape or clicking outside. The user must explicitly close it via the close button or programmatically.
<w-dialog persistent label="Important Action Required">
<w-slot trigger><button>Open Persistent Dialog</button></w-slot>
<w-slot body>
<div>
<h2>Action Required</h2>
<p>You must complete this action before continuing.</p>
<w-slot close><button>I understand</button></w-slot>
</div>
</w-slot>
</w-dialog>
Styling
The dialog body is teleported to a portal at <body> level when opened, so target it by role rather than as a descendant of w-dialog. The library handles viewport-centering and position: fixed.
/* Modal dialog body — already viewport-centered */
[role="dialog"][aria-modal="true"] {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
max-width: 500px;
}
/* Trigger button */
w-dialog w-slot[trigger] > * {
/* button-like trigger */
}
/* Optional backdrop — render your own with a fixed pseudo-element if wanted */
[role="dialog"][aria-modal="true"]::before {
content: "";
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: -1;
}
| Selector | Targets |
|---|---|
[role="dialog"][aria-modal="true"] | The modal dialog body |
[role="dialog"][aria-modal="false"] | A non-modal dialog body |
w-dialog[open] | The host while open |
w-dialog w-slot[trigger] > *[aria-expanded="true"] | Trigger while dialog is open |
Accessibility
role="dialog"on contentaria-modal="true"when modalaria-labelfromlabelprop- Focus trapped within modal
- Focus returned to trigger on close
- Escape key closes dialog