interviewDeck

Your one-stop platform to prepare, practice and ace your interviews.

Loading your questions…

All Questions

Filters & tools

Accessibility Interview Questions and Answers

20 hand-picked Accessibility interview questions with detailed answers. Open the interactive version above to search, filter by difficulty, run code, bookmark questions and track your progress.

Why does accessibility matter, and what is POUR?

Around 15% of people live with a disability, and accessibility also covers temporary and situational impairments — a broken arm, bright sunlight, holding a baby, a noisy train. Most accessibility work improves the product for everyone.

There's also a legal dimension (ADA in the US, the European Accessibility Act, Section 508 for US public sector, and equivalents elsewhere), and enterprise procurement frequently requires a VPAT before they'll buy.

WCAG is organised around four principles — POUR:

  • Perceivable — users can perceive the content (text alternatives, contrast, captions).
  • Operable — users can operate the interface (keyboard access, enough time, no seizure triggers).
  • Understandable — predictable behaviour, clear language, helpful errors.
  • Robust — works with assistive technology now and as it evolves (valid markup, correct roles and names).

What are WCAG conformance levels, and which do you target?

Each WCAG success criterion is graded A, AA or AAA:

  • A — the minimum; failing these blocks people outright (no keyboard access, images with no alternative).
  • AA — the standard target and what essentially all legislation references: 4.5:1 contrast for body text, resize to 200%, visible focus, consistent navigation.
  • AAA — stricter (7:1 contrast, sign language for video). Not expected across a whole site; WCAG itself says AAA conformance for all content isn't generally achievable.

Versions: 2.0 (2008) → 2.1 (2018, added mobile, touch and low-vision criteria) → 2.2 (2023, added focus appearance, dragging alternatives, target size, and accessible authentication). Each is backwards compatible.

The practical answer to "what do we target": WCAG 2.2 level AA.

Why is native HTML better than ARIA?

Because a native element gives you the role, the name, the state, keyboard behaviour, focus management and platform conventions for free and correctly — and it keeps working as browsers and assistive technology evolve.

A <button> is focusable, announces as "button", activates on Enter and Space, fires click on both, participates in forms, and inherits OS high-contrast styling. A <div role="button"> gives you the announcement only — you must add tabindex="0", key handlers for Enter and Space, disabled semantics, and focus styles, and you will get one of them wrong.

This is the first rule of ARIA: if a native element with the semantics you need exists, use it instead of repurposing something else with ARIA.

ARIA is for the gap — genuinely custom widgets (comboboxes, tab panels, tree views), live regions, and describing relationships HTML can't express.

How do ARIA roles, states and properties work?

ARIA changes what's exposed in the accessibility tree — it never changes behaviour, appearance or keyboard support. Adding role="button" makes something announce as a button; it doesn't make it focusable or clickable.

  • Roles — what a thing is: role="tab", role="dialog", role="alert".
  • States — dynamic, change over time: aria-expanded, aria-checked, aria-selected, aria-disabled, aria-current.
  • Properties — largely static relationships: aria-label, aria-labelledby, aria-describedby, aria-controls, aria-haspopup.

Naming precedence (highest first): aria-labelledbyaria-label → native label (<label>, alt, element content) → title. Prefer aria-labelledby pointing at visible text, because a visible label helps everyone and can't drift out of sync.

The rule that catches people out: you must keep states in sync in JavaScript. A stale aria-expanded="false" on an open menu is worse than no attribute at all.

<button aria-expanded="false" aria-controls="menu-1" id="menu-btn">Filters</button>
<ul id="menu-1" hidden aria-labelledby="menu-btn">…</ul>

<script>
  btn.addEventListener('click', () => {
    const open = btn.getAttribute('aria-expanded') === 'true';
    btn.setAttribute('aria-expanded', String(!open));  // keep state in sync!
    menu.hidden = open;
  });
</script>

What makes an interface keyboard accessible?

  • Everything interactive is reachable with Tab and operable with Enter/Space (and arrow keys inside composite widgets like menus, tabs and grids).
  • Focus order follows visual order — it comes from DOM order, so CSS that visually reorders content (order, row-reverse, absolute positioning) creates a confusing tab sequence.
  • Focus is always visible. Never outline: none without a replacement; :focus-visible gives keyboard users a ring without showing it on mouse click.
  • No keyboard traps — you can always Tab out of anything (except intentionally, inside a modal).
  • Skip link — a "Skip to main content" link as the first focusable element, so keyboard users don't tab through 40 nav items on every page.
  • Escape closes overlays, menus and dialogs.
  • Don't use positive tabindextabindex="1" jumps ahead of everything and wrecks the order. Only 0 (focusable in order) and -1 (focusable by script only) are useful.

How do you manage focus in a single-page app?

SPAs break the browser's built-in focus behaviour, so you have to recreate it.

  • Route changes — a full page load resets focus to the top and the screen reader announces the new page. A client-side route change does neither: focus stays on the clicked link and nothing is announced. Fix by moving focus to the new page's <h1> (with tabindex="-1") or the main landmark after navigation, and announcing the new title in a live region.
  • Opening a dialog — move focus into the dialog, trap it there while open, and set aria-modal/inert on the background.
  • Closing anything — return focus to the element that opened it. Losing focus to <body> dumps keyboard users back at the top of the page.
  • Deleting the focused element — move focus somewhere sensible first (the next row, or the list container), or focus is lost.
  • Async content — don't steal focus when content loads in; announce it in a live region instead.

The native <dialog> element handles trapping, background inerting and Escape for you — prefer it over a hand-rolled modal.

How do you build an accessible form?

  • Every input has a real label<label for="id"> or wrapping. Placeholder text is not a label: it disappears on typing, fails contrast, and isn't reliably announced.
  • Group related controls<fieldset> + <legend> for radio groups and checkbox sets, so the group's question is announced with each option.
  • Errors must be programmatically associatedaria-describedby pointing at the message, aria-invalid="true" on the field, and the message next to the field, not only in a summary.
  • On submit failure, move focus to an error summary at the top (or the first invalid field) and announce the count.
  • Never signal state by colour alone — add an icon and text.
  • Required fields — mark visibly and with required/aria-required; explain the convention if you use an asterisk.
  • autocomplete tokens — a WCAG 2.1 criterion, and a genuine usability win.
<div>
  <label for="email">Email address</label>
  <input id="email" type="email" autocomplete="email"
         aria-describedby="email-hint email-err" aria-invalid="true" required>
  <p id="email-hint">We'll only use this to contact you.</p>
  <p id="email-err" role="alert">Enter an email address like name@example.com</p>
</div>

How do you announce dynamic changes to screen readers?

A screen reader only reads what has focus, so silent DOM updates — "3 results found", "Saved", "Added to cart" — go unnoticed. Live regions tell it to announce a region when its content changes.

  • aria-live="polite" — announced when the user is idle. The default choice for status updates and search results.
  • aria-live="assertive" — interrupts immediately. Reserve for genuine errors and urgent warnings; overuse makes the app unusable.
  • role="status" ≈ polite, role="alert" ≈ assertive — often simpler than the raw attributes.

The rules that make them actually work:

  1. The live region must be in the DOM before the content changes. Inserting a role="alert" element and its text at once is unreliable.
  2. Change only the text inside it.
  3. Keep it small and single-purpose; don't wrap a whole results table.
  4. Don't announce the same string twice in a row — it won't re-fire unless the text actually changes.
<!-- present in the DOM from the start, empty -->
<div id="status" role="status" aria-live="polite" class="visually-hidden"></div>

<script>
  // later: only the text changes -> announced
  document.getElementById('status').textContent = `${n} results found`;
</script>

How do you write good alt text?

Alt text conveys the image's function in context, not a description of its pixels.

  • Informative image — describe the information it carries: alt="Sales rose 40% from Q1 to Q4", not alt="chart".
  • Functional image (an icon inside a link or button) — describe the action: alt="Search", not alt="magnifying glass".
  • Decorative image — use alt="" (empty, but present) so screen readers skip it entirely. Omitting the attribute makes some readers announce the filename instead.
  • Image of text — the alt is the text.
  • Complex image (chart, diagram, map) — short alt plus a longer description nearby or via aria-describedby; ideally provide the underlying data as a table.

Don't start with "Image of…" (the role is already announced), stuff keywords, or duplicate adjacent caption text.

What are the colour requirements in WCAG?

Contrast ratios (AA):

  • 4.5:1 for normal text.
  • 3:1 for large text (18.66px bold, or 24px regular and above).
  • 3:1 for meaningful non-text: icons, form field borders, chart lines, focus indicators.
  • AAA raises these to 7:1 and 4.5:1.

Never use colour alone to convey meaning (WCAG 1.4.1) — a red border on an invalid field must be paired with an icon and message; a chart's series need labels or patterns, not just hue. Roughly 8% of men have some colour vision deficiency.

Other criteria that catch teams out: text must reflow and stay readable at 200% zoom (and at 400% in a 320px viewport); users must be able to override colours (high-contrast modes) without content disappearing; and disabled controls are exempt from contrast but are frequently made illegible anyway.

Why do headings and landmarks matter so much?

Screen reader users rarely read a page top to bottom — they navigate by structure. Surveys consistently show headings are the primary way people find content on a page.

Headings: one <h1> describing the page, then h2h6 nested without skipping levels. Heading level is document structure, not font size — style with CSS. A page of visually-bold <div>s has no structure at all to navigate.

Landmarks: <header>, <nav>, <main> (exactly one), <aside>, <footer>, <search> let users jump straight to a region. Label repeated landmarks so they're distinguishable: <nav aria-label="Breadcrumb"> versus <nav aria-label="Main">.

Both are essentially free — they're the markup you should be writing anyway — and they're among the highest-impact accessibility wins available.

What does an accessible modal dialog require?

Six things, and hand-rolled modals usually miss two or three:

  1. Correct semanticsrole="dialog" with aria-modal="true", plus aria-labelledby pointing at the title.
  2. Focus moves in on open — to the dialog, its heading, or the first control (not the close button, if there's a more useful target).
  3. Focus is trapped while open — Tab from the last element wraps to the first.
  4. Background is inert — the rest of the page is unreachable by keyboard and hidden from screen readers. The inert attribute does both.
  5. Escape closes it, as does the close button and (usually) a backdrop click.
  6. Focus returns to the element that opened it.

The native <dialog> element with showModal() provides trapping, inerting, Escape and the top-layer backdrop for free — use it unless you have a hard reason not to.

<dialog id="confirm" aria-labelledby="confirm-title">
  <h2 id="confirm-title">Delete project?</h2>
  <p>This cannot be undone.</p>
  <button value="cancel">Cancel</button>
  <button value="delete">Delete</button>
</dialog>

<script>
  const dlg = document.getElementById('confirm');
  openBtn.addEventListener('click', () => dlg.showModal());  // trap + inert + Esc free
  dlg.addEventListener('close', () => openBtn.focus());       // restore focus
</script>

How do you make a custom widget (tabs, combobox, tree) accessible?

Follow the ARIA Authoring Practices Guide (APG) pattern for that widget rather than inventing semantics — it defines the required roles, states and the exact keyboard behaviour users expect.

Three things every composite widget needs:

  1. The role structure — e.g. tabs need role="tablist"role="tab" (with aria-selected and aria-controls) → role="tabpanel" (with aria-labelledby).
  2. The keyboard model — arrow keys move between items, Home/End jump to the ends, Enter/Space activate. Note that arrow keys, not Tab, move within a widget.
  3. Roving tabindex — exactly one item in the group has tabindex="0" and the rest -1, so the whole widget is a single tab stop. (The alternative is aria-activedescendant, used for comboboxes where focus stays in the input.)

Better answer in most real situations: use a maintained headless library (Radix, React Aria, Headless UI, CDK) that has already implemented and tested these patterns. Getting a combobox right by hand is a genuinely hard, many-week job.

How does a screen reader actually work?

The browser builds an accessibility tree from the DOM — a parallel structure where each node has a role (what it is), a name (its label), a value, and states. The screen reader reads that tree, not your CSS.

Users move through it in two modes: browse mode (arrow keys traverse all content; shortcut keys jump by heading, link, landmark, form field) and focus/forms mode (keystrokes go to the control). This is why headings and landmarks matter so much — they're the navigation system.

The major combinations to know: NVDA + Firefox (free, Windows, most common for testing), JAWS + Chrome (enterprise Windows), VoiceOver + Safari (macOS/iOS, built in), TalkBack + Chrome (Android).

Consequences for developers: display: none and visibility: hidden remove content from the tree; visual order doesn't affect reading order (DOM order does); and anything conveyed purely by styling — a red border, a bold price — isn't conveyed at all.

How do you test accessibility?

Layer it, because automated tools catch only around 30–40% of issues — they can verify a contrast ratio or a missing alt attribute, but not whether the alt text is meaningful or the focus order makes sense.

  1. Automated, in CIaxe-core via jest-axe, Playwright or Cypress; Lighthouse's accessibility audit. Catches missing labels, contrast, invalid ARIA, duplicate ids.
  2. Lintingeslint-plugin-jsx-a11y / Angular template checks: fastest possible feedback, at authoring time.
  3. Keyboard pass — tab through every flow. Cheap, manual, and finds the highest-severity issues.
  4. Screen reader pass — at least NVDA or VoiceOver on critical journeys.
  5. Zoom and reflow — 200% zoom and a 320px viewport.
  6. Real users — usability testing with people who use assistive tech is the only way to find what's technically conformant but miserable to use.
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);

it('has no detectable a11y violations', async () => {
  const { container } = render(<CheckoutForm />);
  expect(await axe(container)).toHaveNoViolations();
});

How do you handle animation accessibly?

Motion can cause real harm — vestibular disorders trigger nausea and dizziness from large parallax, zoom and slide effects, and flashing content can trigger seizures.

The requirements:

  • Respect prefers-reduced-motion — an OS-level setting. Reduce or remove non-essential motion; usually you keep opacity fades (which don't trigger vestibular responses) and drop movement, parallax and scale.
  • Nothing flashes more than three times per second (WCAG 2.3.1) — a hard seizure-safety rule.
  • Anything auto-playing, moving or scrolling for more than five seconds needs a pause control (2.2.2) — carousels, marquees, animated backgrounds.
  • No animation triggered by scroll position that the user can't stop, if it's large-scale.

"Reduced" doesn't mean "none" — instant state changes can be disorienting too. A quick crossfade is usually the right reduced alternative.

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

What accessibility issues are specific to mobile?

  • Target size — WCAG 2.2 requires 24×24 CSS px minimum (2.5.8); platform guidelines say 44×44 (iOS) / 48×48 (Android), which is the better target. Small icon buttons crammed into a toolbar are the usual failure.
  • Don't block zoomuser-scalable=no or maximum-scale=1 in the viewport meta is a direct WCAG failure and a genuine barrier for low-vision users.
  • Support both orientations (1.3.4) — a device may be mounted in a fixed position.
  • Dragging alternatives (2.5.7) — any drag-and-drop needs a single-pointer alternative (buttons, a menu), for motor impairments and switch users.
  • Gesture alternatives — multi-finger or path-based gestures need a simple-tap equivalent.
  • Touch targets need spacing, not just size — adjacent 44px buttons with no gap still cause mis-taps.
  • Test with TalkBack and VoiceOver — mobile screen reader gestures differ enough that desktop testing doesn't cover them.

How do you make a data table accessible?

Use a real <table> — a grid of divs has no row/column relationships, so a screen reader reads a stream of disconnected values.

  • <th> with scope="col" / scope="row" — this is what lets a reader announce "Revenue, Q3: £4.2m" when the user moves to a cell instead of just "£4.2m".
  • <caption> — the table's accessible name, and useful for everyone.
  • <thead>/<tbody> for structure; headers/id for complex tables with split or nested headers.
  • Sortable columns — the header contains a <button>, and the <th> carries aria-sort="ascending|descending|none". Announce the result in a live region.
  • Responsive tables — if you reflow to cards on mobile, keep the header–value association visible in each card; a CSS-only transformation often destroys it.
  • Don't use tables for layout; if you must, role="presentation".

What are the most common accessibility mistakes you see?

  • <div onclick> instead of <button> — not focusable, no keyboard activation, no role. The single most common failure.
  • outline: none with no replacement — invisible focus makes keyboard use impossible.
  • Placeholder as label — vanishes on input, low contrast, unreliable announcement.
  • Icon-only buttons with no accessible name — announced as "button", nothing more.
  • Positive tabindex — destroys focus order.
  • aria-hidden="true" on something focusable — creates a "ghost" control: reachable by Tab but invisible to the screen reader. Use inert instead.
  • Wrong or stale ARIAaria-expanded never updated, role="button" on a link, aria-labelledby pointing at a missing id.
  • Colour-only status — red/green with no icon or text.
  • Auto-playing carousels with no pause.
  • Skipped heading levels chosen for font size.

How do you build accessibility into a team's process?

Retrofitting is far more expensive than building it in, and audits that land as a 300-item backlog get ignored — so the goal is prevention.

  • Design phase — annotate mockups with heading levels, focus order, alt text and error copy; check contrast in the design tool. Most accessibility bugs are decided before any code is written.
  • Design system — the highest-leverage investment by far. Accessible button, input, modal and menu components mean product teams get it right by default without becoming experts.
  • Definition of Done — keyboard-operable, labelled, contrast-checked. A short checklist, not a WCAG audit.
  • CI — axe and lint rules to catch regressions automatically.
  • Ownership — an accessibility champion per team beats a central gatekeeper that becomes a bottleneck.
  • Prioritise by impact — a blocked checkout for keyboard users outranks a decorative contrast issue on a marketing page.