Escape Closes the Topmost Layer
MUST: Escape closes dialogs and overlays, and closes exactly ONE layer — the topmost open one. A nested popover and its parent dialog both listening for Escape on `window` means one keypress closes both and the user loses the form they were filling; the open layer handles the key and calls `stopPropagation()`. Native `<dialog>` and Radix-style primitives keep that stack for you. (Dismissal, not focus — `interactions-manage-focus` covers trap/return.)
Escape must dismiss a dialog or overlay, and it must dismiss exactly one layer — the one on top
function onKeyDown(e: React.KeyboardEvent) {
if (e.key !== "Escape") return;
e.stopPropagation(); // do not let the parent layer close too
close();
}Bad
Good
Why it matters
Escape is the universal "get me out of here" key, and a dialog that only closes via an X button strands anyone who is not holding a mouse — a keyboard user has to Tab around a trapped focus ring hunting for the exit. But the interesting half of this rule is the nested case, which almost every hand-rolled implementation gets wrong. Put a select or a popover inside a dialog, attach `keydown` for Escape on `window` in both, and one keypress closes both: the child dismisses, and the same event keeps bubbling to the dialog's listener.
The user tried to close a dropdown and lost their whole form. The fix is layer discipline — the topmost open layer handles Escape and calls `stopPropagation()` so it does not reach anything beneath it. Native `<dialog>` and Radix-style primitives maintain that stack for you, which is why the a11y skill closes with "for complex widgets (menu, dialog, combobox), prefer established accessible primitives over custom behavior".
This is distinct from interactions-manage-focus, which covers trapping focus and returning it — dismissal is a separate obligation.