Skip to content
GeeksSmith
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

Interactive Comparison

❌ Inaccessible Pattern

Div-soup & Mouse-only

Fails WCAG
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

WCAG 2.1 AA
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: