Find keyboard trap issues
If keyboard focus can be moved to a component, focus can be moved away using only the keyboard. If it requires more than arrow keys or Tab, users must be informed.
Modal and focus traps
Modal Dialog:
⚠️ Tab escapes to page, no Escape key support
Infinite Scroll Trap:
Autocomplete Widget:
Suggestions (arrow keys):
Patterns to avoid
Focus escapes to page behind modal, or no way to close without mouse.
Flash, PDF viewers, or video players that capture keyboard focus.
WYSIWYG editors where Tab inserts content instead of moving focus.
Focus cycles through endless items, never reaching footer or other content.
Focus contained within modal, with Escape to exit. This is intentional and accessible when done correctly.
Testing hints
// Open modal
await page.click('[data-testid="open-modal"]');
// Try to escape with Tab (should stay in modal)
const startElement = await page.evaluate(
() => document.activeElement?.id
);
// Tab through all elements
for (let i = 0; i < 10; i++) {
await page.keyboard.press('Tab');
}
const currentElement = await page.evaluate(
() => document.activeElement?.closest('[role="dialog"]')
);
// Focus should still be in modal
expect(currentElement).not.toBeNull();
// Escape should close modal
await page.keyboard.press('Escape');
await expect(page.getByRole('dialog')).not.toBeVisible();// Detect potential keyboard traps
async function detectTraps(page) {
const focusableSelector = `
a[href], button, input, select, textarea,
[tabindex]:not([tabindex="-1"])
`;
const elements = await page.$$(focusableSelector);
const focusPath = [];
for (const el of elements.slice(0, 20)) {
await el.focus();
focusPath.push(await page.evaluate(
() => document.activeElement?.outerHTML
));
}
// Check for cycles or stuck focus
const uniquePaths = new Set(focusPath);
if (uniquePaths.size < focusPath.length / 2) {
console.log('Potential keyboard trap detected!');
console.log('Focus cycling through:',
[...uniquePaths].slice(0, 3)
);
}
}Automation hints
page.keyboard.press('Escape')