Building Accessible UI Primitives (Focus Trapping, Combobox, ARIA Live)
Accessible UI primitives strictly adhere to WAI-ARIA Authoring Practices: managing keyboard focus trapping (Tab/Shift+Tab), restoring trigger focus on close (Escape), associating labels via aria-labelledby/describedby, and announcing dynamic updates via aria-live.
Why it matters in interviews
Visual & Interactive Explanation
1. User clicks Trigger Button
Save document.activeElement to memory -> Open Modal
2. Lock & Focus
Add inert to sibling DOM -> Focus first focusable modal element
3. User presses Tab
If at last focusable element, wrap focus back to first element
4. User presses Shift+Tab
If at first focusable element, wrap focus to last element
5. User presses Escape / Close
Dismiss modal -> Restore focus back to original trigger button
Code Examples & Implementation
import { useEffect, useRef } from 'react';
export function useFocusTrap(isOpen: boolean, onClose: () => void) {
const containerRef = useRef<HTMLDivElement>(null);
const previousActiveElement = useRef<HTMLElement | null>(null);
useEffect(() => {
if (!isOpen) return;
// 1. Remember element that triggered modal
previousActiveElement.current = document.activeElement as HTMLElement;
const container = containerRef.current;
if (!container) return;
// Selector for all keyboard-focusable elements
const focusableElements = container.querySelectorAll<HTMLElement>(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
// Focus first interactive element
firstElement?.focus();
const handleKeyDown = (e: KeyboardEvent) => {
// Handle Escape key
if (e.key === 'Escape') {
e.preventDefault();
onClose();
return;
}
if (e.key !== 'Tab') return;
// Handle Tab & Shift+Tab wrapping
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement?.focus();
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement?.focus();
}
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
// Restore focus to trigger button
previousActiveElement.current?.focus();
};
}, [isOpen, onClose]);
return containerRef;
}Interview-Ready Answers
A fully accessible modal requires role='dialog', aria-modal='true', aria-labelledby/describedby, a keyboard focus trap cycling between interactive children, Escape key dismissal, and restoring focus to the trigger button upon closing.
Official Documentation & Specifications
Follow-up Questions & Deep Dives
Common Mistakes & Anti-Patterns
- Building a custom modal that allows users to Tab into invisible elements behind the modal backdrop.
- Forgetting to restore focus to the trigger button when closing a modal, causing screen readers to lose context and reset to the top of the body.
- Using non-semantic <div onClick> without role='button', tabIndex={0}, and onKeyDown handler for Enter/Space.
Real-World Architectural Scenario
A government education portal fails a strict WCAG 2.1 AA audit because screen reader users get disoriented after submitting a search form with asynchronous results. How do you fix it?
Rate Your Readiness
Rate how comfortably you can explain this in an interview to update your global readiness gauge: