
This guide to senior accessibility developer interview questions focuses on judgment under real constraints rather than recall of attributes: when ARIA makes a component worse, why a button reads the wrong name, where focus should go after a client-side route change, and what an automated scan cannot tell you. It works through 12 of those questions for senior frontend engineers, with code and the standards behind each answer. It is written for engineers who own accessibility as part of frontend work, not for dedicated accessibility specialist roles.
Some ground is deliberately left to other GreatFrontEnd guides. How headings, landmarks and semantic elements build the accessibility tree is covered in the senior HTML interview questions, and fixing div-based page structure and list markup at a mid level is covered in HTML interview questions for 3 years experience.
A mid-level answer names the right attribute. A senior answer explains what assistive technology actually receives, names the standard or pattern the behavior comes from, and says where the evidence is thin. Accessibility has several areas where no single rule exists, such as focus after a route change, and several widely repeated numbers that need their caveats attached. Saying "this is practice, not a requirement" in the right place is part of the senior signal.
Start from the rules of ARIA use. The W3C's Using ARIA note states the first rule as: if a native HTML element or attribute already has the semantics and behavior you need, use it instead of re-purposing an element and adding ARIA. The other rules say not to change native semantics unless you have to, that all interactive ARIA controls must be keyboard usable, and not to use role="presentation" or aria-hidden="true" on a focusable element.
A precise answer also mentions the document's status. Using ARIA is now a W3C Discontinued Draft (February 2026); its status section says the rules are kept for historical purposes and points readers to the ARIA Authoring Practices Guide, whose "Read Me First" page carries the heading "No ARIA is better than Bad ARIA". The rules are still the right mental model; the citation just needs to be current.
<!-- Adds a role but none of the behavior --><div role="button" onclick="save()">Save</div><!-- Native element: focusable, Enter and Space work, role and name exposed --><button type="button" onclick="save()">Save</button>
The div version announces as a button but is not focusable and does not respond to Enter or Space unless you add tabindex and key handlers yourself. ARIA changes what is announced, not how the element behaves.
On data, WebAIM's 2026 Million report found that home pages using ARIA averaged 59.1 detected errors against 42 for pages without it. WebAIM is explicit that this does not show ARIA caused the errors, because those pages were also more complex. Quote it with that caveat or not at all.
The ARIA Authoring Practices Guide's names and descriptions page gives the precedence. aria-labelledby has the highest precedence and overrides child content and every other naming attribute, including aria-label. Then aria-label, then host language labelling such as <label>, <legend>, <caption> and alt, then child content for roles that support naming from content, and finally title or placeholder as a last resort.
That order explains both bugs:
<!-- Fails WCAG 2.5.3: the visible label is not in the accessible name --><button aria-label="Find items">Search</button><!-- Fix: keep the visible text, add context after it if needed --><button aria-label="Search products">Search</button><!-- Silent override: aria-labelledby beats the native label --><span id="account-label">Work account</span><label for="email">Email</label><input id="email" aria-labelledby="account-label" />
The first example fails WCAG 2.5.3 Label in Name (Level A), which requires that a component's accessible name contain its visible text. A speech-input user who says "click Search" may not be able to activate a button whose accessible name is "Find items". The Understanding document also recommends starting the accessible name with the visible label text.
The second example shows accessible-name precedence. Because aria-labelledby has higher precedence than the native <label>, the input is announced as "Work account" rather than "Email". This only applies when aria-labelledby references a valid element; a reference to an ID that does not exist does not necessarily replace the native label.
Two more details are worth knowing. On elements such as buttons and links that can derive their accessible name from their contents, aria-label replaces that content as the accessible name rather than adding to it. And WAI-ARIA 1.2 lists generic, the implicit role of elements such as a plain div or span, among roles that cannot be named, so aria-label on a generic div should not be relied on.
Start with the honest framing: there is no W3C or WCAG normative requirement that specifies where focus must go after a client-side route change. What exists is practice and some user research.
The user research most directly on this question that this guide could find is Gatsby's user testing of accessible client-side routing from 2019, run with Fable Tech Labs and five participants with disabilities. It found that focusing a heading worked well, that live region announcements alone helped screen reader users but not magnification or voice users, and that focusing a large wrapper element could cut off text for magnifier users. It is a small vendor study, so present it as evidence for one approach, not as a standard.
Frameworks have also built in partial answers. Next.js, for example, includes a route announcer by default, which reads the new page's title, then its h1, then the URL path.
One implementation moves focus to the new page's main heading:
function PageHeading({ title }: { title: string }) {const { pathname } = useLocation();const ref = useRef<HTMLHeadingElement>(null);const isFirstRender = useRef(true);useEffect(() => {document.title = `${title} | Acme`;if (isFirstRender.current) {isFirstRender.current = false; // do not steal focus on the initial page loadreturn;}ref.current?.focus();}, [pathname, title]);return (<h1 ref={ref} tabIndex={-1}>{title}</h1>);}
tabIndex={-1} makes the heading focusable by script without adding it to the Tab order. Updating document.title on every route change matters too, because it is what some announcers and browser history rely on. The senior part is naming the trade-offs: focus a small element rather than a full-width container, do not move focus on the first load, and test with more than one assistive technology, since the evidence base is thin.
<dialog> give you, and what is still your job?Native <dialog> with showModal() handles most of the hard parts:
<button id="open-delete">Delete project</button><dialog id="confirm-delete" aria-labelledby="confirm-title"><h2 id="confirm-title">Delete this project?</h2><p>This removes all environments and cannot be undone.</p><form method="dialog"><button value="cancel" autofocus>Cancel</button><button value="confirm">Delete</button></form></dialog><script>const dialog = document.getElementById('confirm-delete');document.getElementById('open-delete').addEventListener('click', () => dialog.showModal());dialog.addEventListener('close', () => {if (dialog.returnValue === 'confirm') deleteProject();});</script>
Per MDN and the HTML specification, a modal dialog makes the rest of the page inert, can be closed with Escape, moves focus into the dialog when it opens (to the autofocus element if there is one), and restores focus to the previously focused element when it closes. MDN also advises not adding tabindex to the <dialog> element itself. Putting initial focus on Cancel for a destructive action is a deliberate choice, so an accidental Enter does not delete anything. Choosing between <dialog> and the Popover API in the first place, modal versus non-modal, is covered in the senior HTML guide; this question is about making the modal case accessible.
What is still your job: a clear accessible name (aria-labelledby above), sensible initial focus, and deciding whether clicking the backdrop should close it. The closedby attribute controls light dismissal, and a modal dialog does not close on an outside click by default.
The nuance that separates senior answers is the focus trap. The APG modal dialog pattern says Tab and Shift+Tab should wrap inside the dialog. Native modal dialogs let focus move out to the browser's own interface instead of wrapping. That difference is under open discussion in w3c/aria-practices issue 1772, so the accurate answer is that the page content is inert either way, and whether to add a manual trap on top of <dialog> is a judgment call, not a settled requirement. A custom div-based modal, by contrast, needs all of it by hand: role="dialog", aria-modal="true", an accessible name, Escape handling, initial focus, focus restoration, and making the background unreachable, for example with the inert attribute.
A live region tells assistive technology to announce content changes without moving focus. Per MDN's live regions guide, aria-live="polite" waits until the user is idle, while aria-live="assertive" interrupts, so it should be reserved for urgent messages. role="status" implies polite announcements and role="alert" implies assertive ones.
One reason a toast stays silent is timing. MDN advises establishing the live region before updating its content: render an empty region first, give it time to be exposed to assistive technology, and then change its text. A toast component that mounts the region and its message in the same render may not be announced.
function ResultsAnnouncer({ count, loading }: { count: number; loading: boolean }) {// Rendered from the start and kept mounted; only its text changesreturn (<div role="status" className="visually-hidden">{loading ? '' : `${count} results found`}</div>);}
Three more mistakes worth naming. Using role="alert" for routine updates makes every filter change interrupt the user. Setting the same string twice may not be announced again, because nothing changed. And announcing every keystroke of a type-ahead creates noise; debounce the message to the settled result.
The senior framing is restraint. A live region is a channel the user cannot easily turn off, so announce outcomes ("12 results found", "Saved") rather than every intermediate state.
aria-activedescendant?Both are patterns from the APG's keyboard interface guidance for composite widgets, where Tab should move into and out of the widget and arrow keys should move within it.
With roving tabindex, the active item has tabindex="0", every other item has tabindex="-1", and arrow keys move real DOM focus by swapping those values. The browser scrolls the newly focused element into view. The APG tabs pattern uses this approach:
function Tabs({ tabs }: { tabs: { id: string; label: string; content: ReactNode }[] }) {const [active, setActive] = useState(0);const refs = useRef<(HTMLButtonElement | null)[]>([]);function onKeyDown(e: React.KeyboardEvent) {const last = tabs.length - 1;let next = active;if (e.key === 'ArrowRight') next = active === last ? 0 : active + 1;else if (e.key === 'ArrowLeft') next = active === 0 ? last : active - 1;else if (e.key === 'Home') next = 0;else if (e.key === 'End') next = last;else return;e.preventDefault();setActive(next);refs.current[next]?.focus();}return (<><div role="tablist" aria-label="Account settings" onKeyDown={onKeyDown}>{tabs.map((tab, i) => (<buttonkey={tab.id}ref={(el) => (refs.current[i] = el)}role="tab"id={`tab-${tab.id}`}aria-selected={i === active}aria-controls={`panel-${tab.id}`}tabIndex={i === active ? 0 : -1}onClick={() => setActive(i)}>{tab.label}</button>))}</div>{tabs.map((tab, i) => (<div key={tab.id} role="tabpanel" id={`panel-${tab.id}`} aria-labelledby={`tab-${tab.id}`} hidden={i !== active}>{tab.content}</div>))}</>);}
With aria-activedescendant, DOM focus stays on the container and the attribute points to the currently active child by id. The author then has to handle scrolling and the visible focus style, because the browser is not moving focus. The APG combobox pattern uses this approach, because DOM focus has to stay in the text input while the user moves through suggestions.
The rule of thumb: use aria-activedescendant when focus must stay on an element such as an input, and roving tabindex otherwise, since it gets scrolling and focus styling from the browser for free.
aria-hidden="true", but keyboard users still tab into it. What is wrong, and when do you use inert instead?aria-hidden="true" removes content from the accessibility tree, but it does not remove it from the Tab order. A focusable link inside it can still receive focus, and a screen reader user lands on something that is not announced. MDN's aria-hidden reference says not to use it on focusable elements, and because it is inherited, not on an ancestor of focusable elements either. This is also the fourth rule in Using ARIA.
<!-- Bug: hidden from assistive technology, still reachable with Tab --><div aria-hidden="true"><a href="/promo">See the offer</a></div><!-- Background content behind a custom modal: removes focus AND accessibility tree exposure --><main inert>...</main><!-- Fine: decorative and not focusable --><svg aria-hidden="true" focusable="false">...</svg>
Per MDN, inert makes content unfocusable and unclickable and excludes it from the accessibility tree, which is usually what "hide the background" actually means. MDN also notes that there is no visual indication of inertness, so if inert content is still visible, consider styling it so sighted keyboard users understand why they cannot reach it.
The decision rule: aria-hidden is for content that is purely decorative or duplicated elsewhere; inert is for content that should be temporarily unavailable to everyone.
WCAG 2.2 became a W3C Recommendation in October 2023, per W3C's what's new in WCAG 2.2 page. The new success criteria most relevant to frontend engineers:
| Criterion | Level | What it means in practice |
|---|---|---|
| 2.4.11 Focus Not Obscured (Minimum) | AA | a focused element is not entirely hidden by author content such as sticky headers or cookie banners |
| 2.5.7 Dragging Movements | AA | anything done by dragging can also be done with a single pointer without dragging, unless dragging is essential |
| 2.5.8 Target Size (Minimum) | AA | targets are at least 24 by 24 CSS pixels, with exceptions such as spacing and inline links |
| 3.3.7 Redundant Entry | A | information already entered in a process is auto-populated or selectable, with exceptions |
| 3.3.8 Accessible Authentication (Minimum) | AA | no cognitive function test, such as remembering or transcribing, is required to log in, unless an alternative or mechanism is provided |
Two details make the answer concrete. For 2.4.11, a sticky header can hide the element that just received focus when the page scrolls to it, and CSS scroll-padding is a documented technique:
html {scroll-padding-top: 4rem; /* height of the sticky header */}
For 3.3.8, the Understanding document notes that blocking paste into authentication fields forces users to transcribe information and therefore fails the criterion, so onpaste="return false" on a password field is a WCAG 2.2 failure, not just an annoyance. For 2.5.7, a keyboard alternative alone does not satisfy it; there has to be a single-pointer way, such as up and down buttons on a sortable list.
Also worth knowing: 4.1.1 Parsing is obsolete and removed from WCAG 2.2. And WCAG 3.0 is still a W3C Working Draft; W3C's WCAG 3 introduction says it is not expected to be a completed standard for a few more years and will not supersede WCAG 2 for several years after that. Projects should target WCAG 2.2 today.
prefers-reduced-motion is required for WCAG AA. What do you say?The precise answer separates two criteria that often get blurred.
2.3.3 Animation from Interactions is Level AAA. It requires that motion animation triggered by interaction can be disabled unless it is essential, and the prefers-reduced-motion media query is listed as a sufficient technique. So honoring it for interaction-triggered motion is not required for AA conformance.
2.2.2 Pause, Stop, Hide is Level A. Moving, blinking or scrolling content that starts automatically, lasts more than five seconds and is shown alongside other content needs a way to pause, stop or hide it. Auto-rotating carousels, marquees and background video fall here. The Understanding document also notes it is a non-interference criterion, so it applies to all content on the page, even content outside the conformance claim.
@media (prefers-reduced-motion: reduce) {.card {transform: none;transition: opacity 0.2s; /* an opacity fade is not motion animation under 2.3.3 */}}
So the answer to the product manager: for AA conformance, the Level A criterion 2.2.2 requires a way to pause, stop, or hide non-essential moving, blinking, or scrolling content that starts automatically, lasts more than five seconds, and appears alongside other content. Respecting prefers-reduced-motion for interaction-triggered motion relates to the Level AAA criterion 2.3.3, so it is not itself an AA requirement.
It tells you that the checks the tool can make automatically found no violations. It does not tell you the page is accessible.
A figure you may hear quoted comes from Deque's automated accessibility coverage report, which found that 57.38% of issues from first-time audit customers could be found with automated testing, across more than 2,000 audits. Two caveats belong with that number: it is measured by issue volume, not by the share of WCAG success criteria covered, and it is a vendor measuring its own tool. The axe-core README states the same "57% of WCAG issues" figure and returns uncertain results as "incomplete" for manual review.
One setup combines static and runtime checks:
import { test, expect } from '@playwright/test';import AxeBuilder from '@axe-core/playwright';test('checkout has no detectable WCAG A/AA violations', async ({ page }) => {await page.goto('/checkout');const results = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']).analyze();expect(results.violations).toEqual([]);});
Add the jsx-a11y ESLint plugin for static checks on JSX, such as click handlers without keyboard handlers. The Playwright documentation itself notes that many accessibility problems can only be found through manual testing.
What still needs a person: whether the keyboard order makes sense, whether accessible names are meaningful rather than merely present, whether live region timing works, whether focus lands somewhere sensible after an action, and how the page behaves with a screen reader. A tool can confirm a button has a name; it cannot tell you the name is "button button".
Base the matrix on usage data, and say where that data is weak. WebAIM's Screen Reader User Survey 11, published September 2026 with 1,780 responses, reports these primary desktop screen readers:
| Screen reader | Primary desktop use |
|---|---|
| JAWS | 55.0% |
| NVDA | 32.9% |
| VoiceOver | 6.5% |
and these as the most common screen reader and browser pairings: JAWS with Chrome (31.2%), NVDA with Chrome (17.3%), JAWS with Edge (17.0%) and NVDA with Firefox (9.1%).
WebAIM is careful about the limits: the sample was not controlled and may not represent all screen reader users, and it notes that the higher JAWS share may reflect more respondents from North America. The previous survey, number 10, had JAWS and NVDA much closer, which is itself a reason not to treat one edition as precise.
A practical matrix for a product team: NVDA with Chrome or Firefox, because NVDA is free and widely used, which makes it the most accessible regular test; JAWS with Chrome where the audience includes enterprise or government users and a license is available; and VoiceOver with Safari on iOS for mobile, since a desktop survey says little about mobile usage. The senior point is that the matrix is a decision with a rationale, revisited when the audience or the data changes, not a fixed list.
This is not a request for legal advice, and a senior answer says so. It is a request to understand which standards apply so engineering can plan. The facts worth knowing, from official sources:
The engineering takeaway is that WCAG 2.1 AA is the floor named in the US Title II rule, and WCAG 2.2 AA is the current version of the same standard, so targeting 2.2 AA covers 2.1 AA as well. Which law applies to a given product, and from when, is a question for counsel. What engineering owns is a documented target, a test process that matches it, and a backlog that is honest about known gaps.
aria-label as additive. On buttons and links it replaces the visible text, and it can break Label in Name.aria-hidden on focusable content instead of inert.Is WCAG 2.2 or WCAG 2.1 the right target? WCAG 2.2 is the current W3C Recommendation and includes 2.1's criteria apart from the removed 4.1.1, so targeting 2.2 AA generally satisfies a 2.1 AA requirement as well. Check the specific contract or regulation, since some still name 2.1.
Do I need to be an accessibility specialist to answer these? No. These questions test whether a senior frontend engineer can build and review accessible components and know where the standards are firm and where they are not.
Should I memorize the APG patterns? Know the common ones well enough to sketch them: dialog, tabs, menu button and combobox. For the rest, knowing that the APG is the reference and how to read a pattern's keyboard table is enough.
Pick one component you have shipped, such as a dialog, a tab set or a combobox, and test it three ways: keyboard only, with NVDA or VoiceOver, and with axe. Write down what each method found that the others missed. That comparison is the strongest preparation for questions 1, 6 and 10. For hands-on practice with accessibility questions, GreatFrontEnd's accessibility interview questions cover semantic HTML, ARIA and screen readers with worked solutions, and the senior testing interview questions go deeper into testing strategy.
Senior accessibility developer interview questions test judgment more than recall: choosing native elements over ARIA, understanding how accessible names are computed, managing focus where no single standard exists, using <dialog>, inert and live regions correctly, implementing keyboard patterns from the APG, knowing what WCAG 2.2 actually requires at each level, and being precise about what automated testing and legal requirements do and do not establish. If you can explain the behavior, the source behind it and its limits for each of these 12 questions, you are answering at the level a senior round is looking for.
Senior HTML developer interview questions and answers: the Popover API vs dialog, Shadow DOM trade-offs, document outline, and platform-level judgment.
Senior testing developer interview questions on flaky tests, mocking, coverage, and E2E architecture, with worked answers and real, sourced examples throughout.