tezvyn:

Fix keyboard trap in a custom modal dialog

AI-drafted, machine-checkedSource: w3.orgintermediate
Fix keyboard trap in a custom modal dialog

Tests accessible modal focus management. Strong answers: role="dialog" and aria-modal="true", move focus on open, trap Tab/Shift+Tab cycles inside, Escape to close, return focus to trigger. Red flag: CSS-only fixes or aria-hidden without JS focus control.

WHAT THIS TESTS: Whether you understand that an accessible modal is a focus management problem, not just a visual overlay problem. Interviewers want to see that you know the W3C Dialog Pattern requirements: the dialog must be perceivable as a modal, focus must be contained, and the user must be able to exit and return to their prior context. They are checking if you can translate ARIA specifications into concrete JavaScript behavior.

A GOOD ANSWER COVERS: First, ARIA semantics on the container. The dialog element needs role="dialog", aria-modal="true", and usually aria-labelledby pointing to the title. Second, initial focus placement. On open, move focus into the dialog with JavaScript, typically to the first focusable element. If the content is long or structurally complex, focus a static element like the heading by giving it tabindex="-1". Third, focus trapping. Listen for Tab and Shift+Tab keydown events. If Tab is pressed on the last tabbable element, move focus to the first. If Shift+Tab is pressed on the first, move focus to the last. You can query tabbable elements with selectors for visible links, buttons, inputs, and elements with tabindex="0". Fourth, close behavior. Bind Escape to close the dialog. Fifth, focus restoration. Store the element that triggered the dialog, and on close call .focus() on it. If that element is gone from the DOM, move focus to a logical workflow successor.

COMMON WRONG ANSWERS: Relying on aria-hidden="true" on the background without actually moving focus into the modal or trapping it. Using positive tabindex values anywhere. Forgetting to restore focus on close, which strands keyboard users. Suggesting only CSS solutions like pointer-events: none on the backdrop. Failing to handle Shift+Tab wrap, creating a one-way trap. Not using aria-modal so screen readers do not treat the background as inert.

LIKELY FOLLOW-UPS: How do you make the background truly inert for screen readers? The answer is using the inert attribute or aria-hidden on sibling content while keeping the dialog outside that subtree. What if the dialog contains an iframe or web component? You may need a MutationObserver or shadow DOM query to update your list of tabbable elements when content changes. How do you handle nested modals? Track a focus stack so each close restores focus to the opener of that specific layer.

ONE CONCRETE EXAMPLE: A delete confirmation modal. The trigger is a Delete button in a table row. Open: set role="dialog", aria-modal="true", aria-labelledby="delete-title". Move focus to the Cancel button because deletion is destructive. Trap Tab so it cycles between Cancel and Confirm Delete. On Escape or Cancel, close the modal and return focus to the original Delete button. If the row was deleted and the button no longer exists, move focus to the next row or a table summary.

Source: W3C ARIA Authoring Practices Guide (APG) Dialog (Modal) Pattern

Read the original → w3.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.