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!
modaloverlayaccessibilitykeyboard events
Demo 1: Simple Info Modal
ℹ️ Information
Welcome to LokaScript Modal Dialogs!
This is a simple modal dialog created entirely with hyperscript.
No JavaScript frameworks needed.
Click the × button, press ESC, or click outside to close.
Demo 2: Confirmation Dialog
⚠️ Confirm Delete
Are you sure you want to delete your account?
This action cannot be undone. All your data will be permanently deleted.
Demo 3: Form Modal
👤 New User
Demo 4: Behavior-Powered Modal
Same modal pattern, but using ClickOutside + FocusTrap
behaviors. No manual event wiring — composable, accessible, and self-contained.
✅ Behavior-Powered Modal
Composable behaviors replace manual wiring!
This modal uses two behaviors:
install ClickOutside — fires clickoutside event when clicking the overlay
install FocusTrap — traps Tab key, sets aria-modal, restores focus on close
Try pressing Tab to see focus stay trapped inside the modal.
<!-- 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:
Add role="dialog" to modal container
Add aria-modal="true" to indicate modal state
Add aria-labelledby pointing to modal title
Implement focus trap to keep keyboard focus in modal
Return focus to trigger element when modal closes
Support ESC key to close (already implemented)
Prevent body scroll when modal is open
Try it yourself:
Add animations (fade in, slide up)
Implement focus trap for keyboard navigation
Create stacked modals (modal over modal)
Add auto-close timer for notifications
Build a modal manager for multiple modals
Add confirmation before closing with unsaved changes