01 — WHAT IS ACCESSIBILITY

Web Accessibility
from zero to expert

Accessibility (shortened as a11y — 11 letters between a and y) means building websites and apps that everyone can use — including people with visual, auditory, motor, or cognitive disabilities.

Real numbers: ~15% of the world's population lives with some form of disability. In India alone, that's 26 crore+ people. Poor accessibility = excluding them from your product entirely.

Disabilities that affect web usage include:

Interview framing tip: Always say "accessibility benefits everyone, not just disabled users." Example: keyboard navigation helps power users. Captions help people in noisy environments. High contrast helps people in bright sunlight.
02 — WHAT IS WCAG

Web Content Accessibility Guidelines

WCAG (pronounced wuh-kag) is a set of guidelines published by the W3C (World Wide Web Consortium) that defines how to make web content accessible. Think of it as the rulebook.

  • WCAG 2.0 — published 2008, became ISO standard
  • WCAG 2.1 — published 2018, added mobile & cognitive improvements (current standard)
  • WCAG 2.2 — published 2023, added more criteria especially for cognitive disabilities
  • WCAG 3.0 — in progress, completely redesigned scoring model
JPMC context: Enterprise banks follow WCAG 2.1 AA as a minimum legal requirement in most countries. Some aim for AAA for specific high-risk flows like payments.
03 — POUR PRINCIPLES

The 4 POUR principles

Every WCAG guideline falls under one of these four core principles. Remember this acronym — interviewers love asking it.

P
Perceivable
Information must be presentable to users in ways they can perceive. You can't hide content from all senses.

Examples: alt text for images, captions for video, don't use color alone to convey meaning.
O
Operable
UI components must be operable by everyone. Users must be able to navigate and interact.

Examples: keyboard navigable, no seizure-inducing flashes, skip navigation links, enough time to read content.
U
Understandable
Content and UI must be understandable. Users must be able to understand info and how to operate the UI.

Examples: readable text, predictable navigation, form error suggestions.
R
Robust
Content must be robust enough to work with current and future assistive technologies.

Examples: valid HTML, ARIA used correctly, no browser-specific hacks that break screen readers.
04 — CONFORMANCE LEVELS

A, AA, AAA — what do they mean?

A
Minimum level. Must pass. Failing means severe blockers for disabled users — like a page with no text alternatives for images at all.
AA
The legal standard in most countries. Required by ADA (USA), EAA (EU), and most enterprise procurement rules including banks. This is what you need to know.
AAA
Highest standard. Very difficult to achieve across an entire site. Usually targeted for specific high-stakes flows (e.g. emergency info, payment screens).
What to say in interviews: "We target WCAG 2.1 AA as a baseline across all components in our design system, with spot AAA checks on critical user journeys like checkout or form submission."
CORE IMPLEMENTATION
05 — SEMANTIC HTML

Semantic HTML — the foundation

Semantic HTML is using the right HTML element for the right job. Screen readers and other assistive technologies read the HTML structure to understand your page. If everything is a <div>, they have no idea what's a button, what's navigation, what's a heading.

Bad vs good — the most common mistake

/* ❌ BAD — a div pretending to be a button */
<div onclick="doSomething()" class="btn">Submit</div>
// Problems: not focusable by keyboard, no role announced,
// screen reader says "Submit" with no context

/* ✅ GOOD */
<button type="button" onclick="doSomething()">Submit</button>
// Free: keyboard focusable, Enter/Space activates it,
// screen reader announces "Submit, button"

Key semantic elements to know

ElementPurposeScreen reader announces
<header>Page or section header"banner" landmark
<nav>Navigation links"navigation" landmark
<main>Primary page content"main" landmark
<footer>Page or section footer"contentinfo" landmark
<aside>Sidebar / related content"complementary" landmark
<button>Clickable action"[text], button"
<a href>Navigation link"[text], link"
<h1>–<h6>Headings hierarchy"[text], heading level N"
<ul> / <ol>Lists"list, N items"
<table>Tabular data onlyreads row/col headers
Never use a table for layout. Screen readers will read it as data and confuse users completely. Tables are only for tabular data.

Heading hierarchy — the most broken thing on enterprise apps

/* ❌ BAD — headings used for visual size, not structure */
<h1>Dashboard</h1>
<h3>Revenue</h3>   // jumped h2!
<h5>Chart</h5>     // jumped h4!

/* ✅ GOOD — logical nested structure */
<h1>Dashboard</h1>
  <h2>Revenue</h2>
    <h3>Monthly Chart</h3>
    <h3>Yearly Chart</h3>
  <h2>Expenses</h2>
06 — KEYBOARD NAVIGATION

Keyboard navigation

Many users cannot use a mouse — they navigate entirely by keyboard. Your entire UI must be usable with just a keyboard.

Essential keyboard shortcuts to know

KeyAction
TabMove to next focusable element
Shift + TabMove to previous focusable element
EnterActivate links and buttons
SpaceActivate buttons, checkboxes
Arrow keysNavigate within components (menus, radio groups, sliders)
EscapeClose modals, dropdowns, tooltips

What is naturally focusable?

These elements get Tab focus automatically — no extra work:

  • <a href>, <button>, <input>, <select>, <textarea>
  • Elements with tabindex="0"
/* tabindex values */
tabindex="0"   // add element to natural tab order
tabindex="-1"  // focusable via JS (.focus()) but NOT in tab order — used for modals
tabindex="5"   // ❌ AVOID — custom order is fragile and confusing
Skip navigation link — required for WCAG 2.4.1. A "Skip to main content" link that appears on first Tab press, letting keyboard users jump past the navbar directly to content.
/* Skip link — first element in body */
<a href="#main-content" class="skip-link">
  Skip to main content
</a>

/* CSS — visually hidden until focused */
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: #fff;
  padding: 8px;
  z-index: 100;
}
.skip-link:focus {
  top: 0; /* slides into view on focus */
}
07 — FOCUS MANAGEMENT

Focus management

Focus indicator = the visible outline that shows which element is currently focused. Never remove it without replacing it with something better.

/* ❌ The most harmful CSS line in frontend history */
*:focus { outline: none; }

/* ✅ Replace with custom visible indicator */
*:focus-visible {
  outline: 2px solid #4ade80;
  outline-offset: 3px;
  border-radius: 3px;
}
/* :focus-visible fires only on keyboard focus, not mouse click */
:focus-visible is the modern fix — it shows the focus ring only when navigating by keyboard, not when clicking with mouse. Best of both worlds.

Modal / dialog focus trap

When a modal opens, focus must be trapped inside it. Users shouldn't be able to Tab out to content behind the modal.

// Focus trap pattern for modals
function trapFocus(modalElement) {
  const focusable = modalElement.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  const first = focusable[0];
  const last = focusable[focusable.length - 1];

  modalElement.addEventListener('keydown', (e) => {
    if (e.key !== 'Tab') return;
    if (e.shiftKey) {
      if (document.activeElement === first) {
        last.focus(); e.preventDefault();
      }
    } else {
      if (document.activeElement === last) {
        first.focus(); e.preventDefault();
      }
    }
  });
  first.focus(); // move focus into modal on open
}

// On close — return focus to the trigger element
const trigger = document.querySelector('#open-modal-btn');
// when modal closes:
trigger.focus();
08 — COLOR & CONTRAST

Color & contrast ratios

WCAG defines minimum contrast ratios between text and its background. Contrast ratio is measured from 1:1 (same color) to 21:1 (black on white).

Text typeAA minimumAAA minimum
Normal text (under 18pt)4.5 : 17 : 1
Large text (18pt+ or 14pt bold)3 : 14.5 : 1
UI components & icons3 : 1
Decorative / logo textNo requirement
❌ FAIL — ratio ~1.6:1
This text is hard to read — light gray on light gray background
✅ PASS — ratio ~8.5:1
This text is easy to read — dark text on light gray background
Color alone rule (WCAG 1.4.1): Never use color as the ONLY way to convey information. Example: a red asterisk for required fields must also have a text label like "(required)". Color-blind users can't see the red.

Tools for checking contrast

  • WebAIM Contrast Checker — webaim.org/resources/contrastchecker
  • Chrome DevTools — inspect any element, click the color swatch
  • Figma — plugins like "Contrast" or "Stark"
  • axe DevTools — browser extension that auto-flags contrast issues
09 — IMAGES & ALT TEXT

Images & alt text

/* 3 types of images — different alt text strategy */

// 1. Informative image — describe what it shows
<img src="chart.png" alt="Bar chart showing revenue grew 40% in Q4 2024" />

// 2. Decorative image — empty alt, screen reader skips it
<img src="decorative-wave.svg" alt="" role="presentation" />

// 3. Functional image (inside a link/button) — describe the action
<button>
  <img src="search-icon.svg" alt="Search" />
</button>

// For icon-only buttons — use aria-label instead
<button aria-label="Close dialog">
  <svg aria-hidden="true">...</svg>
</button>
Missing alt on meaningful images = WCAG 1.1.1 Level A failure. Automated scanners catch this instantly.
10 — FORMS

Accessible forms

/* ❌ BAD — placeholder as label */
<input type="text" placeholder="Enter your email" />
// Placeholder disappears on typing — user forgets what field it was
// Screen reader may or may not read placeholder as label

/* ✅ GOOD — label associated with input */
<label for="email">Email address</label>
<input type="email" id="email" name="email"
       aria-describedby="email-hint"
       aria-required="true" />
<span id="email-hint">We'll never share your email</span>

/* Error state */
<input aria-invalid="true"
       aria-describedby="email-error" />
<span id="email-error" role="alert">
  Please enter a valid email address
</span>
ADVANCED TOPICS
11 — ARIA DEEP DIVE

ARIA — Accessible Rich Internet Applications

ARIA is a set of HTML attributes that modify the accessibility tree — what screen readers see. The golden rule:

First rule of ARIA: Don't use ARIA if you can use native HTML instead. <button> is always better than <div role="button">.

3 types of ARIA attributes

TypeAttributePurposeExample
RoleroleOverrides element type in accessibility treerole="dialog", role="alert"
Propertyaria-*Adds meaning/relationshiparia-label, aria-required
Statearia-*Dynamic current statearia-expanded, aria-checked

Most important ARIA attributes for interviews

/* aria-label — give a name when no visible text */
<button aria-label="Close dialog"> × </button>

/* aria-labelledby — point to another element as the label */
<div role="dialog" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Confirm Delete</h2>
</div>

/* aria-describedby — point to description text */
<button aria-describedby="submit-hint">Submit</button>
<span id="submit-hint">This will finalize your order</span>

/* aria-expanded — for dropdowns, accordions */
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>...</ul>

/* aria-live — announce dynamic content changes */
<div aria-live="polite">    // waits for user to finish action
<div aria-live="assertive">  // interrupts immediately (for errors)

/* aria-hidden — hide decorative elements from screen readers */
<span aria-hidden="true"></span>
12 — SCREEN READERS

How screen readers work

Screen readers convert the accessibility tree (not the visual DOM) into speech or braille output. The most common ones:

Screen readerPlatformNotes
NVDAWindows (free)Most used for testing; pairs with Firefox/Chrome
JAWSWindows (paid)Most common in enterprise/banking environments
VoiceOvermacOS / iOSBuilt-in; cmd+F5 to enable on Mac
TalkBackAndroidBuilt-in; important for React Native apps
Quick test: Enable VoiceOver (Mac: Cmd+F5) and try to complete a flow on your app using only keyboard + hearing the announcements. You'll immediately find the gaps.
13 — DYNAMIC CONTENT

Dynamic content & live regions

When content changes without a page reload (SPA behavior, toast messages, loading states), screen readers don't know about it unless you tell them.

/* Toast / notification — use role="alert" for urgent messages */
<div role="alert">
  Payment failed. Please check your card details.
</div>
// Automatically treated as aria-live="assertive"

/* Status message — polite, non-urgent */
<div role="status">
  Saved successfully
</div>
// Automatically treated as aria-live="polite"

/* Loading state */
<button aria-busy="true" aria-label="Submitting form, please wait">
  <span aria-hidden="true">Loading...</span>
</button>

/* Page title updates in SPA — critical for screen reader users */
// After route change, update document.title
document.title = "Order Confirmation — MyApp";
14 — ACCESSIBILITY IN REACT

Accessibility in React

React uses className and htmlFor instead of class and for, but all ARIA attributes work exactly the same.

// JSX — note htmlFor instead of for
<label htmlFor="email">Email</label>
<input id="email" type="email" />

// Accessible modal component with focus trap
import { useEffect, useRef } from 'react';

function Modal({ isOpen, onClose, title, children }) {
  const modalRef = useRef(null);
  const triggerRef = useRef(document.activeElement);

  useEffect(() => {
    if (isOpen) {
      modalRef.current?.focus();
    } else {
      triggerRef.current?.focus(); // return focus on close
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-labelledby="modal-title"
      ref={modalRef}
      tabIndex={-1}
      onKeyDown={(e) => e.key === 'Escape' && onClose()}
    >
      <h2 id="modal-title">{title}</h2>
      {children}
      <button onClick={onClose}>Close</button>
    </div>
  );
}

// Route change announcement in React Router
function RouteAnnouncer() {
  const location = useLocation();
  useEffect(() => {
    document.title = getPageTitle(location.pathname);
  }, [location]);
  return null;
}

eslint-plugin-jsx-a11y

Add this ESLint plugin to your project — it catches accessibility issues at code-writing time:

npm install --save-dev eslint-plugin-jsx-a11y

// .eslintrc
{
  "plugins": ["jsx-a11y"],
  "extends": ["plugin:jsx-a11y/recommended"]
}
INTERVIEW PREP
15 — QUICK CHECKLIST

Pre-deployment accessibility checklist

  • All images have meaningful alt text (or empty alt if decorative)
  • All form inputs have associated labels (via for/id or aria-label)
  • Color contrast ≥ 4.5:1 for normal text, ≥ 3:1 for large text
  • Page is fully navigable by keyboard (Tab, Enter, Space, Escape)
  • Focus indicator is visible — never outline: none without replacement
  • Modal/dialog traps focus and returns it on close
  • Dynamic content updates announced via aria-live or role="alert"
  • Heading hierarchy is logical (h1 → h2 → h3, no skipping)
  • Skip to main content link present at top of page
  • No information conveyed by color alone
  • Interactive elements use correct semantic HTML (button, not div)
  • Icon-only buttons have aria-label
  • Videos have captions, audio has transcripts
  • Run axe DevTools — fix all critical + serious issues
  • Test with keyboard-only — complete one full user journey
16 — INTERVIEW Q&A

Interview Q&A — JPMC style

Q: What is WCAG and what level do you target?

Say: "WCAG stands for Web Content Accessibility Guidelines, published by the W3C. It's organized around 4 POUR principles — Perceivable, Operable, Understandable, Robust. I target WCAG 2.1 AA as the baseline for all production work — that's also the legal requirement under ADA and EAA. For critical flows like payments or error states, I consider AAA criteria like enhanced contrast."

Q: How do you handle accessibility in a React component library?

Say: "I make sure semantic HTML is the foundation — buttons are always button elements, links are always anchor tags. I use eslint-plugin-jsx-a11y to catch issues early. For dynamic components like modals, I implement focus management — trap focus inside the modal, return it to the trigger on close. For form components, I always associate labels with inputs via htmlFor/id. And I test with keyboard navigation and VoiceOver before shipping."

Q: How do you make a custom dropdown accessible?

Say: "A custom dropdown needs: the trigger button with aria-expanded reflecting open/closed state, aria-haspopup="listbox", and aria-controls pointing to the list. The list gets role="listbox", each item gets role="option" with aria-selected. Arrow keys move between options, Enter/Space select, Escape closes it. This follows the ARIA Authoring Practices Guide combobox pattern."

Q: What tools do you use for accessibility testing?

Say: "Three layers: automated, manual, and assistive tech testing. For automated I use axe-core (via axe DevTools browser extension or jest-axe in unit tests) — catches about 30-40% of issues. For manual I do keyboard-only walkthroughs and zoom to 200%. For assistive tech I test with VoiceOver on Mac. No tool catches everything — keyboard testing in particular surfaces issues automated tools miss."

Q: Tell me about a time you improved accessibility in a real project.

Your angle with Superset: "In my Superset work, I customized chart components for business needs. From an accessibility perspective, charts are one of the hardest areas — they're inherently visual. I made sure each chart had a proper aria-label describing the data it showed, added a text summary table as an accessible alternative, and ensured the color palettes we used had sufficient contrast ratios. I also made the interactive filtering controls keyboard-accessible."