intermediate 7 min read
Accessibility (a11y): WCAG 2.1 AA, Focus Management & ARIA
Web Accessibility (a11y) ensures that digital products are fully usable by people with visual, motor, auditory, and cognitive disabilities, adhering to the 4 WCAG principles: Perceivable, Operable, Understandable, and Robust (POUR).
Why it matters in interviews
Accessibility is legally mandated (ADA, Section 508, EAA) and heavily emphasized in Lead interviews (e.g. ConveGenius, Walmart, EdTech). Building accessible modals, dropdowns, and forms proves senior-level engineering rigor.
Visual & Interactive Explanation
Inaccessible vs Accessible Component Comparison
❌ Inaccessible Pattern
Div-soup & Mouse-only
Element<div onClick={...}>
Keyboard Tab❌ Not reachable via Tab
Screen ReaderAnnounces 'Generic group'
ContrastLow contrast gray on dark
Strengths
- •Quick to write without thinking
Trade-offs
- •Completely unusable for screen readers and keyboard users
✅ Accessible Pattern
Semantic & Keyboard First
Element<button type='button' onClick={...}>
Keyboard Tab✅ Natively focusable + Enter/Space
Screen ReaderAnnounces 'Button, [label]'
Contrast≥ 4.5:1 ratio verified
Strengths
- •100% keyboard and screen reader compatible
- •Free browser accessibility tree
Trade-offs
- •Requires disciplined semantic structure
Code Examples & Implementation
Accessible Modal with Focus Trap & Escape Key Handler
import { useEffect, useRef } from 'react';
export function AccessibleModal({
isOpen,
onClose,
title,
children,
}: {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}) {
const modalRef = useRef<HTMLDivElement>(null);
const triggerRef = useRef<HTMLElement | null>(null);
useEffect(() => {
if (isOpen) {
// 1. Save reference to element that triggered the modal
triggerRef.current = document.activeElement as HTMLElement;
// 2. Focus the modal container or first focusable element
modalRef.current?.focus();
// 3. Handle Escape key and Tab trapping
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
if (e.key === 'Tab' && modalRef.current) {
const focusable = modalRef.current.querySelectorAll<HTMLElement>(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
last.focus();
e.preventDefault();
} else if (!e.shiftKey && document.activeElement === last) {
first.focus();
e.preventDefault();
}
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
// 4. Return focus to the trigger button upon close!
triggerRef.current?.focus();
};
}
}, [isOpen, onClose]);
if (!isOpen) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
ref={modalRef}
tabIndex={-1}
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"
>
<div className="rounded-xl border border-ink-border bg-ink-panel p-6 shadow-2xl">
<h2 id="modal-title" className="text-lg font-bold text-text">{title}</h2>
<div className="mt-3 text-sm text-text-muted">{children}</div>
<button
onClick={onClose}
className="mt-4 rounded bg-signal-cyan px-4 py-2 text-sm font-semibold text-ink"
>
Close
</button>
</div>
</div>
);
}Interview-Ready Answers
Accessibility targets WCAG 2.1 AA: use semantic HTML over ARIA wherever possible, maintain visible focus rings, ensure 4.5:1 color contrast, implement focus trapping in modals, and announce dynamic updates with `aria-live`.
Official Documentation & Specifications
Follow-up Questions & Deep Dives
Common Mistakes & Anti-Patterns
- Removing focus rings globally via `outline: none` without providing a visible `:focus-visible` replacement
- Using `<div onClick>` instead of `<button>` (breaks keyboard Enter/Space activation)
- Trapping focus in a modal but forgetting to return focus to the trigger button on unmount
Real-World Architectural Scenario
A user navigating via keyboard opens a promotional modal. Pressing 'Tab' focuses buttons behind the modal on the background page. How do you fix this?
Rate Your Readiness
Rate how comfortably you can explain this in an interview to update your global readiness gauge:
Rate your confidence: