If keyboard focus can be moved to a component, then focus can be moved away from that component using only the keyboard.
This modal allows escape with Esc key and proper tab cycling
This modal creates a keyboard trap - you can't escape!
• Esc key closes dropdown
• Tab moves to next element
• Arrow keys navigate options
• Focus returns to trigger
• All elements focusable
• Clear button accessible
• Natural tab order
• No focus traps
Tab content can be navigated out of
• Tab key moves between tabs
• Arrow keys navigate within tabs
• Content is not trapped
• Focus can move to next section
• Natural form navigation
• Validation doesn't trap focus
• Error messages are announced
• Submit button is reachable
<!-- Good Example: Modal with Proper Focus Management -->
<div class="modal" role="dialog" aria-labelledby="modal-title" aria-modal="true">
<div class="modal-content" onkeydown="handleModalKeyDown(event)">
<header>
<h2 id="modal-title">Settings</h2>
<button class="close-button" onclick="closeModal()" aria-label="Close modal">
×
</button>
</header>
<main>
<label for="username">Username:</label>
<input id="username" type="text" />
<label for="email">Email:</label>
<input id="email" type="email" />
</main>
<footer>
<button onclick="saveSettings()">Save</button>
<button onclick="closeModal()">Cancel</button>
</footer>
</div>
</div>
<!-- Good Example: Dropdown with Escape -->
<div class="dropdown">
<button onclick="toggleDropdown()" aria-expanded="false" aria-haspopup="true">
Options
</button>
<ul class="dropdown-menu" onkeydown="handleDropdownKeys(event)">
<li><a href="#option1">Option 1</a></li>
<li><a href="#option2">Option 2</a></li>
<li><a href="#option3">Option 3</a></li>
</ul>
</div>
<!-- Bad Example: Modal without Escape -->
<div class="bad-modal" role="dialog">
<div class="modal-content" onkeydown="preventEscape(event)">
<h2>Trapped Modal</h2>
<input type="text" placeholder="You're stuck here!" />
<button>Can't escape!</button>
</div>
</div>
<script>
function handleModalKeyDown(event) {
if (event.key === 'Escape') {
closeModal();
return;
}
if (event.key === 'Tab') {
const focusableElements = getFocusableElements();
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (event.shiftKey) {
if (document.activeElement === firstElement) {
event.preventDefault();
lastElement.focus();
}
} else {
if (document.activeElement === lastElement) {
event.preventDefault();
firstElement.focus();
}
}
}
}
function preventEscape(event) {
// BAD: This prevents escape and creates a trap
if (event.key === 'Escape') {
event.preventDefault();
}
}
</script>Remember: Keyboard traps are one of the most frustrating accessibility barriers. Users must always be able to navigate away from any focusable element.
Always provide a way to escape, whether through Escape key, close buttons, or natural Tab navigation.