Modal Dialog
Native dialog patterns with forms, confirmation, and scrolling. Accessible by default with focus management.
Overview
Modal dialogs use the native HTML <dialog> element which provides built-in accessibility features: focus trapping, escape key handling, and proper ARIA semantics. No JavaScript library required.
Key features:
- Native focus management and trapping
- Escape key closes dialog
- Built-in backdrop with
::backdrop - Form integration with
method="dialog" - Return value for form submissions
Basic Modal
A simple modal with header, body content, and action buttons.
Basic modal
<button type="button" id="open-modal"> Open Modal</button> <dialog id="basic-modal"> <layout-stack data-layout-gap="l"> <header> <h2>Modal Title</h2> </header> <div class="dialog-body"> <p>This is a basic modal dialog.</p> </div> <footer> <layout-cluster data-layout-justify="flex-end" data-layout-gap="m"> <button type="button" class="secondary" id="cancel-btn"> Cancel </button> <button type="button" class="primary" id="confirm-btn"> Confirm </button> </layout-cluster> </footer> </layout-stack></dialog> <script> const modal = document.getElementById('basic-modal'); document.getElementById('open-modal').onclick = () => modal.showModal(); document.getElementById('cancel-btn').onclick = () => modal.close(); document.getElementById('confirm-btn').onclick = () => modal.close('confirm');</script>
Confirmation Dialog
A centered confirmation dialog for destructive actions. Uses method="dialog" for form submission.
Confirmation dialog
<dialog id="confirm-modal" class="confirm-dialog"> <form method="dialog"> <layout-stack data-layout-gap="l"> <header> <icon-wc name="alert-triangle" size="lg" class="warning-icon"></icon-wc> <h2>Confirm Deletion</h2> </header> <p>Are you sure? This action cannot be undone.</p> <footer> <layout-cluster data-layout-justify="center" data-layout-gap="m"> <button type="submit" value="cancel" class="secondary">Cancel</button> <button type="submit" value="delete" class="danger">Delete</button> </layout-cluster> </footer> </layout-stack> </form></dialog> <script> const modal = document.getElementById('confirm-modal'); modal.addEventListener('close', () => { if (modal.returnValue === 'delete') { // Perform delete action } });</script>
Form Modal
A modal containing a form with inputs. Includes a close button in the header.
Form modal
<dialog id="form-modal"> <form method="dialog"> <layout-stack data-layout-gap="l"> <header> <h2>Add New Item</h2> <button type="button" class="ghost close-btn" aria-label="Close"> <icon-wc name="x"></icon-wc> </button> </header> <div class="dialog-body"> <layout-stack data-layout-gap="m"> <form-field> <label for="item-name">Name</label> <input type="text" id="item-name" name="name" required> </form-field> <form-field> <label for="item-desc">Description</label> <textarea id="item-desc" name="description" rows="3"></textarea> </form-field> </layout-stack> </div> <footer> <layout-cluster data-layout-justify="flex-end" data-layout-gap="m"> <button type="button" class="secondary">Cancel</button> <button type="submit" value="save" class="primary">Save</button> </layout-cluster> </footer> </layout-stack> </form></dialog>
Dialog Styles
Base styles for the dialog element. Add to your project CSS.
Dialog styles
dialog { padding: 0; border: none; border-radius: var(--radius-l); box-shadow: var(--shadow-lg); max-width: min(90vw, 500px); max-height: 85vh;} dialog::backdrop { background: var(--color-overlay-strong); backdrop-filter: blur(4px);} dialog > layout-stack,dialog > form { padding: var(--size-l);} dialog header { display: flex; justify-content: space-between; align-items: center; gap: var(--size-m);} dialog header h2 { margin: 0; font-size: var(--font-size-xl);}
Usage Notes
- Opening: Use
dialog.showModal()for modal behavior with backdrop - Closing: Use
dialog.close()or let users press Escape - Return value: Use
method="dialog"on forms to get the submit button's value - Focus: First focusable element receives focus when opened
- Click outside: Add click listener on dialog to close when clicking backdrop
- Scrolling: For long content, add a scrollable class to the body section
Click Outside to Close
Click outside handler
dialog.addEventListener('click', (e) => { if (e.target === dialog) { dialog.close(); }});
Related
Dialog Element
Native dialog documentation
Toast Notifications
Non-blocking notifications