Modal Dialog

Build accessible modal dialogs with keyboard support
Learn how to create modal overlays, handle clicks outside, and implement proper keyboard navigation. Essential for confirmations, forms, and notifications!
modal overlay accessibility keyboard events

Demo 1: Simple Info Modal

Demo 2: Confirmation Dialog

Demo 3: Form Modal

Demo 4: Behavior-Powered Modal

Same modal pattern, but using ClickOutside + FocusTrap behaviors. No manual event wiring — composable, accessible, and self-contained.

The Code

Modal HTML Structure:

<div id="my-modal" class="modal-overlay"> <div class="modal" _="on click halt"> <div class="modal-header"> <h3>Title</h3> <button _="on click hide #my-modal">×</button> </div> <div class="modal-body"> Content goes here </div> <div class="modal-footer"> <button _="on click hide #my-modal">Close</button> </div> </div> </div>

Open Modal:

<button _="on click show #my-modal"> Open Modal </button>

ESC Key to Close:

<!-- Global handler for ESC key --> <div _="on keydown[key=='Escape'] from window hide .modal-overlay"></div>

Click Outside to Close:

<!-- Click overlay (not modal content) to close --> <div _="on click from .modal-overlay if target matches .modal-overlay hide .modal-overlay end"></div>

Prevent Close on Modal Click:

<!-- Halt prevents click from bubbling to overlay --> <div class="modal" _="on click halt"> ...modal content... </div>

How it works:

Key Concepts:

CSS Requirements:

/* Initially hidden */ .modal-overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.6); z-index: 1000; } /* Shown when .show class added */ .modal-overlay.show { display: flex; align-items: center; justify-content: center; }

Advanced Modal Patterns:

<!-- Focus trap (keep focus in modal) --> <div class="modal" _="on show get first <button/> in me focus() it on keydown[key=='Tab'] get last focusable element in me if document.activeElement is it halt focus() first focusable element in me end end"> <!-- Auto-close after timeout --> <button _="on click show #notification-modal wait 3s hide #notification-modal"> Show Notification </button> <!-- Confirm before closing --> <button _="on click if #form-modified if confirm('Discard changes?') hide #form-modal end else hide #form-modal end"> Close </button>

With Behaviors (Demo 4):

Behaviors replace manual event wiring with composable, accessible primitives.

<!-- Before: Manual wiring (Demos 1-3) --> <!-- 1. Global ESC handler --> <div _="on keydown[key=='Escape'] from window hide .modal-overlay"></div> <!-- 2. Global click-outside handler --> <div _="on click from .modal-overlay if target matches .modal-overlay hide .modal-overlay end"></div> <!-- 3. Halt on each modal to prevent bubble --> <div class="modal" _="on click halt"> <!-- 4. No focus trapping ❌ -->
<!-- After: Behavior-powered (Demo 4) --> <div class="modal" _="install ClickOutside(active: false) install FocusTrap(active: false) on clickoutside js closeBehaviorModal() end end on keydown[key=='Escape'] js closeBehaviorModal() end end"> <!-- Behaviors activated/deactivated via CustomEvents: --> <!-- el.dispatchEvent(new CustomEvent('focustrap:activate')) --> <!-- el.dispatchEvent(new CustomEvent('clickoutside:activate')) --> <!-- ✅ Click-outside detection via behavior --> <!-- ✅ Focus trapping + aria-modal --> <!-- ✅ Focus restored on close --> <!-- ✅ Self-contained, no global handlers -->

Accessibility Best Practices:

Try it yourself: