Accessibility and Inclusive Design Questions
Designing and building for the full range of users and abilities: WCAG conformance levels and what they actually require, semantic markup and ARIA, keyboard and screen-reader support, color contrast and non-color affordances, accessible forms and error states, audio and alternative feedback, accessibility testing and audit, and disability-inclusive research. Covers accessible interaction patterns and treating accessibility as a first-class engineering constraint rather than a retrofit. The scope is accessibility as an engineering and design competency: not workplace diversity, inclusion and belonging, not algorithmic fairness or model bias, and not responsive or multi-platform layout as topics in their own right.
Describe semantic HTML and why using native elements (headings, lists, buttons, landmarks) improves accessibility. Provide three concrete examples where a non-semantic element (div/span) should be replaced with a semantic alternative, and explain the accessibility benefit of each replacement.
Sample Answer
Direct answer. Semantic HTML elements carry built-in meaning that browsers expose to assistive technology automatically, while a generic <div> or <span> carries none. Using <button>, <nav>, <main>, <form>, headings, and lists instead of generic containers means a screen reader user gets navigation, operability, and structure for free, without any extra ARIA.
Why this matters mechanically. Every element has a computed role, name, and state exposed through the accessibility tree. A native <button> automatically has role "button," is keyboard-focusable, responds to both Enter and Space, and gets a visible focus outline by default. A <div> with a click handler has none of that: no role (so a screen reader announces it as plain text), no keyboard focusability, and no built-in Space/Enter activation. Recreating all of that with ARIA and JavaScript is possible but is strictly extra work to reach parity with something the browser already gives you.
Three concrete replacements.
- A clickable
<div>acting as a button: replace with<button>. You get keyboard focus, Space/Enter activation, and a "button" role with zero ARIA. - A list of navigation links built from
<div>/<span>rows: replace with<nav>containing a<ul>of<li><a></a></li>. A screen reader user can then jump by landmark ("navigation") and by list, and hears "3 of 8" list-position context automatically. - A page's primary content wrapped in a generic
<div id="content">: replace with<main>. Screen reader users have a "skip to main content" style landmark-navigation command; a<div>gives them nothing to jump to.
Trade-offs and pitfalls. Semantic HTML is not free of nuance: <button> inside a <form> defaults to type="submit", which is a common source of accidental form submission if you forget type="button" for a non-submit action. And semantics only get you so far for genuinely custom widgets (a combobox, a tree view) that have no native HTML equivalent; those legitimately need ARIA, but even then the rule is "native first, ARIA only for the gap native elements don't cover."
You are handed an existing product page with several accessibility problems (missing labels, unlabeled icon buttons, insufficient contrast, and non-linear tab order). As a product designer, describe the step-by-step audit approach you would take to identify issues, prioritize fixes, propose design and small-code remedies, and estimate effort to remediate.
Sample Answer
Direct answer. Auditing an existing page with missing labels, an unlabeled icon button, insufficient contrast, and non-linear tab order needs a systematic pass: fix labels first (they're usually the fastest, highest-impact fix), correct tab order by fixing DOM order rather than patching with tabindex values, verify contrast with computed numbers, and re-verify everything with both automated tooling and a manual keyboard pass, since the two catch different defect classes.
Executed audit and fix, verified with axe-core. As a product designer you would normally reach the same finding by running a browser extension such as axe DevTools or WAVE directly on the live page and reading its violation list, which needs no tooling setup and no code. The Node script below is the engineering-equivalent way of proving the identical result precisely and reproducibly; it is not something you would be expected to write yourself, and you can read just its output if you prefer. With that said: I built the described broken markup, ran a real accessibility scanner (axe-core 4.12 via jsdom in Node) against it, fixed it, and re-ran the scanner, shipping the actual harness so this is reproducible rather than narrated:
const { JSDOM } = require('jsdom');
const axeSource = require('fs').readFileSync(require.resolve('axe-core/axe.min.js'), 'utf8');
async function runAxe(bodyHtml) {
const dom = new JSDOM(`<!DOCTYPE html><html><head></head><body>${bodyHtml}</body></html>`,
{ runScripts: 'dangerously', resources: 'usable', pretendToBeVisual: true });
dom.window.eval(axeSource);
return new Promise((resolve, reject) => {
dom.window.axe.run(dom.window.document, {}, (err, results) => err ? reject(err) : resolve(results));
});
}
<!-- BEFORE -->
<input type='text' placeholder='Name' tabindex='3'/>
<button tabindex='1' style='color:#aaa;background:#fff'>x</button>
<input type='text' placeholder='Email' tabindex='2'/>
Running runAxe() against this before-state returned a real tabindex violation on all 3 elements (axe-core's tabindex rule does flag any positive tabindex value; automation catches the anti-pattern itself, it just cannot tell you whether the resulting order actually diverges from the visual layout, since axe has no notion of visual order, only DOM order plus the tabindex attribute, so the ordering consequence still needs the manual read to confirm). It returned NO label violation on the placeholder-only inputs (a verified nuance: placeholder text satisfies the accessible-name computation even though it isn't a real persistent label) and NO button-name violation on the icon button (its literal "x" text content technically satisfies the accessible-name check). The manual read is what catches the real problem automation cannot: that single ambiguous "x" character IS an accessible name by the letter of the rule, but not a meaningful one to a screen reader user who can't see it's meant as a close icon.
<!-- AFTER -->
<label for='audit-name'>Name</label><input id='audit-name' type='text'/>
<label for='audit-email'>Email</label><input id='audit-email' type='email'/>
<button aria-label='Close dialog' style='color:#1F2937;background:#fff;border:1px solid #1F2937'>×</button>
Re-running runAxe() against the fixed markup confirmed the tabindex violation is gone entirely (0 flagged nodes, down from 3), with the same baseline document-level violations remaining (missing <title>/lang/landmark region), which are artifacts of testing an isolated fragment, not part of the audited component. Exact aggregate pass-count numbers for a real accessibility scan are tool- and environment-dependent (for example, jsdom cannot fully resolve color-contrast since it lacks a real rendering engine, so that check stays "incomplete" rather than pass/fail regardless of the actual colors), so this reports the specific rule-level deltas that reproduce identically rather than a single aggregate figure. The contrast fix itself was verified directly against the WCAG relative-luminance formula rather than the scanner: #aaa (170,170,170) on white computes to 2.32:1, well below the 4.5:1 AA threshold for normal text; #1F2937 (31,41,55) on white computes to 14.68:1, comfortably passing even the 7:1 AAA threshold.
Estimating effort to remediate. Size by fix type, not a single page-level number: swapping placeholder-only inputs for real <label> elements and adding the icon button's aria-label are each a few minutes of markup change with no design sign-off needed; removing the explicit tabindex values is a one-line deletion per element. The contrast fix is trivial if the target color already exists as a design-system token (a class/variable swap); it needs a short design review if it doesn't, since introducing a new one-off color requires the same AA verification applied everywhere else that color might get reused. Altogether, this class of page-level fix is normally well under one sprint, hours not days, unless a color-contrast finding turns out to be a token used broadly across the product, in which case the token-level fix stays small but the regression testing across every surface using that token becomes the larger, separately-scoped cost.
Trade-offs and pitfalls. The explicit-tabindex-values defect here is a textbook example of the exact anti-pattern this domain flags repeatedly: positive tabindex values create a parallel, hard-to-maintain ordering system that silently diverges from visual/DOM order the moment anyone edits the markup, which is exactly what happened in this broken example.
Explain the principle 'use native semantics first, ARIA only when necessary.' Give three concrete examples where native elements should be used instead of ARIA, and one example of a custom widget where ARIA is appropriate. Explain why the ARIA example requires ARIA.
Sample Answer
Direct answer. The rule "use native semantics first, ARIA only when necessary" means you should reach for a built-in HTML element before recreating its behavior with ARIA attributes on a generic container, because native elements give you keyboard support, focus management, and correct accessible-name computation automatically. ARIA is a layer you add on top of HTML, and it can only describe semantics, it can never grant real keyboard behavior on its own.
Three examples where native elements should be used instead of ARIA.
- A clickable action: use
<button>, not<div role="button" tabindex="0">plus manual Enter/Space handlers. The native element gives you both key bindings, focus, and disabled-state handling for free. - Navigation to another URL: use
<a href>, not a<div role="link">. Native anchors support middle-click-to-open-in-new-tab, right-click context menu, and:visitedstyling, none of which ARIA can replicate. - A form field: use
<input>,<select>, or<textarea>with a real<label>, not a styled<div>withrole="textbox"and manualcontenteditablehandling, which requires reimplementing text-selection, IME composition (the multi-keystroke input method used to type languages like Chinese, Japanese, or Korean), and undo/redo behavior that the browser already provides, a reimplementation burden that mainly matters to the engineer building the field, not something a designer needs to personally verify.
One example where a custom widget genuinely needs ARIA. A tabbed interface (role="tablist", role="tab", role="tabpanel") has no native HTML equivalent; you build it from <div> or <button> elements and ARIA roles/states because the interaction model (arrow-key navigation between tabs, aria-selected state, one tabpanel visible at a time) is a defined interaction pattern the platform doesn't provide out of the box.
Common ARIA misuse anti-patterns.
- Adding
role="button"to a<div>without also addingtabindex="0"and manual keydown handling: the element gets a button role announced by a screen reader but remains completely unreachable by keyboard, which is often worse than doing nothing because it advertises an affordance that doesn't work. - Redundant roles on elements that already have the right implicit role, like
<button role="button">: usually harmless but a signal the author doesn't understand what's already provided, and in older browser/AT combinations redundant roles have occasionally suppressed the correct native behavior. One notable exception:<ul role="list">(and<ol role="list">) is a deliberate, justified redundancy, not a mistake, because Safari and VoiceOver drop a list's implicitlist/listitemroles oncelist-style: noneis applied, so re-declaringrole="list"restores semantics CSS silently removed. - Using
aria-labelto override visible text with different wording (e.g. a button visibly labeled "Learn more" butaria-label="Learn more about our pricing plans"): this breaks voice-control users who say the visible label to activate it, since the accessible name no longer matches what they read on screen.
Trade-offs and pitfalls. ARIA can lie: setting aria-expanded="true" on a collapsed panel, or role="button" on an element with no click or keydown handler, makes an assistive-technology user believe something is true that the DOM doesn't back up. The WAI-ARIA specification itself states the first rule of ARIA is "don't use ARIA if you don't have to."
Technical constraint scenario: Your engineering team says they cannot implement an accessible pattern due to platform limitations and time constraints. Describe pragmatic alternatives you could propose, how you would prioritize incremental improvements, and how you would document residual accessibility risks for future sprints.
Sample Answer
Direct answer. When engineering says an accessible pattern is technically infeasible given the platform or timeline, the productive response is to separate the claim into its two possible components, a genuine platform limitation versus a scoping/effort estimate, and address each differently: verify the platform claim directly (often it's less absolute than stated), and for a genuine effort/timeline constraint, propose a scoped alternative that gets most of the benefit for less cost rather than accepting an all-or-nothing framing.
Verifying the platform claim. Ask for the specific technical citation (a documented platform limitation, not just "it's hard"), and independently check whether the constraint is actually absolute or whether it reflects the specific approach being attempted rather than the platform itself; genuinely absolute platform limitations are less common in practice than initially claimed, since most modern web/mobile platforms support the ARIA and keyboard patterns this domain relies on, and "technically infeasible" sometimes really means "infeasible with our current framework/library choice," a narrower and more negotiable claim.
Pragmatic alternatives when the constraint is genuinely real. Propose a phased approach: ship a simpler, fully-accessible version now (even if less visually polished than the original design) and treat the more ambitious version as a follow-up once the platform constraint is resolved or worked around; this avoids the false choice between "ship something completely inaccessible" and "miss the deadline entirely."
Prioritizing given real constraints. Rank the specific inaccessible elements by user impact (a primary flow's core interaction outranks a secondary decorative element) and negotiate for the highest-impact ones to get the scoped fix now, with lower-impact ones explicitly tracked as known debt with an owner and a target date, not silently dropped.
Documenting residual risk. Maintain a living accessibility risk register, not just an informal mention in a sprint retro: one entry per deferred item recording the user impact and severity, the specific technical reason it was deferred (including whether the original "infeasible" claim was independently verified, per the verification step above, or is still an unverified engineering assertion), an owner, a target sprint or quarter, and a re-review trigger (for example, re-evaluate automatically when the blocking library or platform version is upgraded). Make this register visible outside the engineering team, referenced in the product's accessibility conformance statement or VPAT (Voluntary Product Accessibility Template: a standardized document describing how a product conforms to accessibility standards) if one exists, so a deferred item doesn't quietly disappear from institutional memory once the immediate deadline pressure passes.
Trade-offs and pitfalls. Accepting "technically infeasible" at face value without verifying it is a common failure mode that lets real constraints and convenient excuses get treated identically; conversely, treating every claim of infeasibility as automatically false and pushing back reflexively damages trust with engineering and burns credibility for the times a constraint really is genuine, so the discipline of actually verifying the specific claim, rather than assuming either way, is what makes this negotiation productive rather than adversarial.
Describe a practical process for receiving, triaging, and incorporating accessibility feedback from users, QA, and developers into an ongoing iteration cycle so accessibility improvements are prioritized and shipped without derailing releases. Include severity levels, owners, and sprint planning practices.
Sample Answer
Direct answer. A practical process for receiving, triaging, and incorporating accessibility feedback needs a clear, low-friction intake channel (not buried in a general bug tracker where it gets deprioritized against feature work by default), a triage step that assigns real severity using consistent criteria, and a feedback loop that closes the reporter's original loop, confirming the fix and thanking them, so the reporting channel stays trusted over time.
Intake channel. A dedicated, clearly-labeled accessibility feedback path (a specific email alias, a tagged bug-tracker category with its own visible queue) that's discoverable from the product itself, not only from an internal wiki page; users, QA, and developers all feed into the same intake point, so findings from every source get triaged consistently rather than developer-found issues silently skipping the same process user-reported ones go through.
Triage criteria. Severity based on the actual impact: does the issue completely block a task for an affected user (critical), significantly degrade it (serious), or create friction without blocking (moderate/minor); the same three-tier scale applies whether the finding came from a human report or an automated scan, so triage doesn't need a separate severity scale depending on where the finding originated.
Incorporating findings into the iteration cycle. Critical/serious findings get pulled into the current or next sprint directly, not deferred to a generic backlog; moderate/minor findings get batched into a recurring "accessibility debt" sprint allocation (a fixed percentage of every sprint's capacity reserved for this category) so they don't get perpetually outcompeted by new feature work, which is the most common way accessibility feedback backlogs grow unbounded.
Closing the loop. Whoever reported the issue gets a direct update when it's fixed, not just a silently-closed ticket, since a reporter who never hears back (especially an assistive-technology-using customer who took the time to report a real friction point) is less likely to report again, quietly eroding the quality of future feedback the channel receives.
Trade-offs and pitfalls. A feedback channel with no dedicated capacity allocation, where accessibility fixes only happen when they happen to win a general backlog-prioritization fight against feature work, reliably loses that fight consistently enough that the backlog only grows; the fixed-capacity-reservation approach is what actually keeps the moderate/minor tier from becoming a permanent graveyard.
Owners. A designated accessibility lead or champion (a rotating responsibility on a small team, a dedicated role on a larger one) owns initial triage and severity assignment, but not the fix itself. Once triaged, ownership of the actual fix passes to the engineering team that owns the affected component or flow, the same way any other bug is owned, rather than centralizing every fix through a single accessibility team that cannot scale past a handful of issues at a time. Every critical or serious finding gets an explicitly named owner and a due date at the moment it is triaged, not just a position in a queue, since accountability diffuses quickly once a finding sits unassigned.
Unlock Full Question Bank
Get access to all Accessibility and Inclusive Design interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.