A modal isn't just a centered box with a dark backdrop. Get it wrong and it's an accessibility trap — keyboard users get stuck, screen readers wander off, the page scrolls behind it. Here's one done right, from scratch.
🪟 Try it (Tab around, hit Esc): https://dev48v.infy.uk/design/day20-modal-dialog.html
The five things a real modal does
- Opens over a dimmed backdrop with a fade/scale-in.
- Closes on ×, backdrop click, AND Escape — all three.
- Traps focus — Tab/Shift+Tab cycle within the dialog; you can't tab out to the page behind it.
- Locks body scroll (and compensates for the scrollbar shift so the page doesn't jump).
- Returns focus to the trigger button on close — don't strand the user.
Plus the ARIA: role="dialog", aria-modal="true", aria-labelledby pointing at the title.
The focus trap
Query the dialog's focusable elements; on Tab from the last one, wrap to the first; on Shift+Tab from the first, wrap to the last. That's the whole trick.
The shortcut
The native <dialog> element + .showModal() gives you focus trap, Esc, backdrop, and ::backdrop for free. Reach for it first; hand-roll only when you need full control.
🔨 Full build (overlay → open/close → Esc+backdrop → focus trap → scroll lock + return focus) on the page: https://dev48v.infy.uk/design/day20-modal-dialog.html
Part of DesignFromZero. 🌐 https://dev48v.infy.uk
Top comments (0)