Accessibility: Keyboard Traps in Modals
Every developer should care about accessibility. Plenty of people can only use a keyboard to browse the web, like blind users and people with physical disabilities. Modals, calendars, dropdowns, any custom widget you build, they all need to work without a mouse.
This article looks at one specific problem, keyboard traps. That's when a user moves focus into a part of the page and then can't move it back out using the keyboard. Modals are a good example.
What actually causes a trap
No plain HTML element traps focus by itself. A <textarea>, a <div>, a <button> always behave the same way when you add them to a page. A keyboard trap always comes from custom JavaScript that blocks what the browser would normally do.
Why modals should trap focus
For modals, trapping focus is actually the right thing to do, most of the time. Once a modal is open, the rest of the page should not be reachable, not with the mouse, and not with Tab either. So Tab should only move between elements inside the modal, going back to the first one after it reaches the last.
And when the modal closes, focus should go back to whatever element opened it.
Every modal needs a way out
A modal always needs a way to close it with the keyboard, Esc, a Cancel button, a Close button, anything you can reach with Tab. Without that, you built a real trap. Part of the page, or the whole page, becomes unusable until the user reloads it.
So these two things are not in conflict; trapping focus inside a modal on purpose is fine. Trapping someone with no way out is the real bug.
<dialog> already solves this
The good news is the native <dialog> element already does all of this for you.
Here's what it looks like with React.
import { useRef } from "react";
function Modal() {
const dialogRef = useRef<HTMLDialogElement>(null);
return (
<>
<button onClick={() => dialogRef.current?.showModal()}>
Open modal
</button>
<dialog ref={dialogRef}>
<p>Modal content</p>
<button onClick={() => dialogRef.current?.close()}>Close</button>
</dialog>
</>
);
}
Call .showModal() and the browser takes care of trapping focus, closing on Esc, and sending focus back afterward. No keyboard code needed.
If you want to read the official rule behind all this, it's WCAG Success Criterion 2.1.2, No Keyboard Trap.
Top comments (0)