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.
List commonly used assistive technologies for web products (screen readers like NVDA, VoiceOver, JAWS; magnification tools; switch controls; voice input) and describe a practical prioritization matrix for which platforms and AT combinations to test first for a consumer-facing web application.
Sample Answer
Direct answer. Assistive technologies (AT) are software and hardware that let people with disabilities perceive and operate digital products. The main categories are screen readers, screen magnifiers, switch controls, and voice input, and each interacts with a page in a fundamentally different way, so a realistic accessibility practice needs working familiarity with all of them.
The categories and how each interacts with content.
- Screen readers (NVDA and JAWS on Windows, VoiceOver on macOS/iOS, TalkBack on Android): read the accessibility tree (the simplified map of element roles, names, and states the browser builds from your markup: this, not the visual page, is what a screen reader actually reads from) aloud or to a braille display, letting a user navigate by heading, a landmark (a labeled region like navigation or main content that a screen reader user can jump straight to), link, or form control rather than scanning visually. They depend entirely on correct semantics: a
<div>styled to look like a button is invisible as a button unless it hasrole="button"and real keyboard support. - Screen magnifiers (ZoomText, built-in OS zoom): enlarge a portion of the screen, so a user only ever sees a fraction of the layout at once. Content relying on peripheral cues (a toast in a corner, a tooltip far from its trigger) is easy to miss.
- Switch controls: let a user select on-screen items using one or two physical switches and a scanning cursor, typically because fine motor control for a keyboard or mouse isn't available. Every actionable element must be reachable through simple sequential input, not simultaneous key combinations.
- Voice input (Dragon NaturallySpeaking, Voice Control): a user says a visible label ("Click Submit") to activate a control. This breaks when the visible label and the accessible name disagree, e.g. an icon-only button whose visible glyph is a magnifying glass but whose
aria-labelsays "Search records."
A practical prioritization matrix. For a typical web product, prioritize by reach and cost-of-neglect: screen reader support first (largest affected population, and correct semantic HTML gets you most of the way for free), then keyboard-only operability (a prerequisite for switch access and largely free once focus order and visible focus are correct), then zoom/reflow at 200 to 400 percent, then voice-input label matching last, since that mostly needs the visible label and accessible name to agree, which is a light audit pass once the earlier layers are solid.
Trade-offs and pitfalls. Testing with only one screen-reader-and-browser pairing, commonly VoiceOver with Safari, misses real compatibility gaps: NVDA with Chrome and JAWS with Edge sometimes behave differently for identical markup, particularly around live regions and custom widgets. A team with limited testing budget should pick the pairing that matches its actual analytics-reported user base rather than whichever AT is easiest to install locally.
Describe a robust implementation for a user-facing 'Reduce Motion' toggle in a web app. Explain how you'd wire the toggle to CSS variables or classes, persist the preference, combine it with OS-level prefers-reduced-motion, and ensure widgets and third-party libraries respect the user's choice.
Sample Answer
Direct answer. A robust user-facing 'Reduce Motion' toggle needs a clear precedence order between the explicit in-app user choice and the OS-level prefers-reduced-motion setting, wired to actual CSS variables/classes that every animated component reads from a single source of truth, with the choice persisted so it survives a page reload or a new session.
Wiring the toggle. The toggle sets a single data-motion="reduce" attribute on <html> (or a CSS class), and every animated component's CSS reads that attribute/class rather than each component independently checking prefers-reduced-motion on its own, giving one central point of control instead of scattered, potentially-inconsistent checks across the codebase.
Persisting the preference. Store the explicit user choice (distinct from "no explicit choice made") in account-level storage so it follows the user across devices, defaulting to the OS-level prefers-reduced-motion setting only when no explicit in-app choice has been made yet.
Executed verification of the precedence logic. I implemented and ran the exact resolution function combining a stored override with the OS-level signal:
function resolveMotionPreference(storedOverride, osPrefersReduced) {
if (storedOverride === 'reduce' || storedOverride === 'no-preference') return storedOverride;
return osPrefersReduced ? 'reduce' : 'no-preference';
}
Actual output: resolveMotionPreference(null, true) returned "reduce" (no stored override, OS default applies); resolveMotionPreference('no-preference', true) returned "no-preference" (the explicit user choice correctly overrides the OS default rather than being silently ignored); resolveMotionPreference('reduce', false) returned "reduce" (the explicit choice again wins). All three precedence cases matched intent against the function's actual return values.
Combining with OS-level prefers-reduced-motion. Listen for matchMedia('(prefers-reduced-motion: reduce)').addEventListener('change', ...) so a user who changes their OS setting while the app is open gets the update live, but only apply that live update as the new DEFAULT if the user hasn't already made an explicit in-app choice, since an explicit choice should not be silently overridden by a later OS-level change.
Ensuring widgets and third-party libraries respect the choice. First-party components are covered by the central attribute/class described above, but a third-party library (a charting library, a carousel, an embedded video player) that reads the OS-level prefers-reduced-motion media query directly has no way to see your app's explicit in-app override, since matchMedia reflects the actual OS setting, not your stored preference; a user who explicitly chose "reduce motion" in-app while their OS default is "no preference" will not get that override honored by such a library, and there is no way to make matchMedia lie to code you don't control. The practical mitigation is preferring libraries that expose their own configuration option to disable animation programmatically, and wiring your central motion-preference state to that option specifically; for a library with no such option, either avoid its motion-heavy configuration entirely or swap it for a static/reduced alternative when the explicit override is set to "reduce," and track any library that can't be fully controlled as a known, documented limitation rather than implying the toggle guarantees coverage it can't actually provide.
Trade-offs and pitfalls. Storing the preference only in browser local storage (not account-level) is a common shortcut that fails the cross-device expectation; a user who sets "reduce motion" on their laptop reasonably expects the same experience on their phone, and local-storage-only persistence silently breaks that expectation without any visible error to alert them.
List mobile-specific accessibility issues to consider when designing mobile web and native-like experiences (voiceover gestures, text scaling/Dynamic Type, touch target sizing, orientation changes, zoom, and platform-specific affordances). Describe design mitigations and how you'd test them on devices.
Sample Answer
Direct answer. Mobile accessibility has its own concerns beyond the desktop-web checklist: platform screen readers (VoiceOver on iOS, TalkBack on Android) use swipe gestures rather than Tab/arrow keys to navigate, users can scale system font size (Dynamic Type on iOS, font scale on Android) which your layout must reflow to accommodate, touch targets need real physical size rather than mouse-pointer precision, and orientation/zoom behavior needs explicit support rather than being an edge case.
Concrete considerations.
- VoiceOver/TalkBack gestures: swipe right/left moves to the next/previous accessible element; a double-tap activates the focused element. Custom gesture-based interactions (a swipe-to-delete list item) need an accessible equivalent action exposed as a standard activation, since a screen reader user's swipe gesture is already consumed by VoiceOver/TalkBack navigation, not passed through to your custom gesture handler.
- Dynamic Type/font scaling: text set in fixed pixel sizes doesn't respond to a user's system-level text-size preference; using relative units (
rem/emon web, or the platform's scalable text APIs in native code) lets a user who has set 200% system text size actually get a legible layout instead of clipped or overlapping text. - Touch target sizing: the same 44x44pt/48x48dp minimum applies, with extra headroom warranted for users with tremor or limited dexterity.
- Orientation and zoom: locking an app to portrait-only, or disabling pinch-to-zoom, actively harms low-vision users who rely on zoom or a specific orientation for readability; WCAG 1.3.4 (Orientation) and 1.4.10 (Reflow) both address this directly.
- Platform-specific behaviors: iOS's rotor lets a VoiceOver user jump by headings, links, or form controls similarly to a screen reader's landmark navigation on web; Android's TalkBack has an equivalent reading-controls menu. Both require the same underlying semantic structure (headings, landmarks) that web accessibility already asks for.
Trade-offs and pitfalls. A cross-platform app built with a single shared UI layer sometimes ships accessibility metadata that works well on one platform's screen reader and poorly on the other, because iOS and Android map similar concepts (grouping, custom actions) to their accessibility APIs slightly differently; testing on both real screen readers, not just one, is the only reliable way to catch this.
Design micro-interaction patterns (success, error, hover, progress) that are accessible to people with cognitive impairments. Describe timing, affordance clarity, repetition, animation use, progressive disclosure, and testing methods to validate reduced cognitive load without losing useful cues.
Sample Answer
Direct answer. Micro-interactions (success, error, hover, progress) accessible to people with cognitive impairments need to be clear and unambiguous without relying on speed or subtlety to convey meaning: generous timing (not a flash that disappears before it can be processed), affordances that clearly state what happened rather than only implying it visually, minimal unnecessary repetition or decoration, and progressive disclosure that doesn't require holding multiple pieces of state in working memory at once.
Timing. A success or error state should persist long enough to be read and processed, not auto-dismiss after a fixed short duration regardless of the user's reading pace; a reasonable floor is at least 5 seconds for a short message, scaling up by roughly 200ms per additional word for longer text, so the message doesn't outrun a slower reader's actual pace; where a toast-style auto-dismiss pattern is used, pair it with a persistent, revisitable record of the same information (an activity log, a form field's own visible error state) rather than relying on the transient notification as the only record.
Affordance clarity. State the outcome in plain text alongside any icon or color cue ("Saved" with a checkmark, not just a checkmark alone), since a purely iconographic or color-only signal requires the user to correctly infer meaning from a symbol, adding an interpretive step that's harder for some cognitive-accessibility populations, and is also just generally less immediately clear for everyone.
Repetition and consistency. Use the same visual pattern and wording for the same type of event everywhere in the product (all success states look and read the same way), since inconsistent patterns for conceptually identical events force the user to re-learn what each variant means, adding unnecessary cognitive load.
Hover. Hover-revealed content (a tooltip, a secondary menu) that disappears the instant the cursor moves even slightly off the trigger is a specific, well-documented cognitive-load problem: a user who needs more time to read what appeared loses it before finishing, and has to retrigger it, possibly repeatedly. WCAG 1.4.13 (Content on Hover or Focus) covers exactly this: hover-triggered content needs to be dismissable (without moving the pointer, for example via Escape), hoverable (the pointer can move onto the revealed content itself without it disappearing), and persistent (it stays visible until the user dismisses it, moves focus away, or it's no longer relevant), not on a short fixed timer. Also avoid hover as the ONLY way an affordance is communicated (a button whose function is explained solely by a hover tooltip, with no persistent visible label), since that forces the user to discover and correctly interpret a transient cue rather than reading a stated affordance, adding exactly the kind of interpretive step this population is most burdened by.
Progressive disclosure and animation use. A multi-step progress indicator should show the current state plainly ("Step 2 of 4: Payment details") rather than relying on an abstract animated progress bar alone; animation used for these states should be brief and purposeful rather than decorative, since motion that exists purely for visual polish adds processing overhead without adding informational value, and, given vestibular sensitivity, should respect prefers-reduced-motion.
Testing for calibration. Test these specific micro-interaction patterns with cognitive-accessibility-focused usability participants specifically, not folded into a general accessibility test session, since this population's feedback on timing and clarity is easy to under-weight relative to more visually-obvious contrast or keyboard-operability findings.
Trade-offs and pitfalls. A design team often treats micro-interaction polish (subtle, fast, minimal) as inherently good design, but for this population specifically, subtlety and speed are frequently the actual defect, not a stylistic preference to be respected; the tension between "feels sophisticated and fast" and "is genuinely legible and processable" is real and worth naming explicitly rather than assuming they're the same goal.
Design an accessible data table that supports sortable columns, row selection, and keyboard navigation. Describe the necessary semantic HTML/ARIA attributes (<table>, <caption>, <th scope>, aria-sort, role='row'), keyboard interactions for sorting and selection, and how to announce state changes (sort direction, selection) to assistive technologies.
Sample Answer
Direct answer. An accessible sortable, selectable data table uses real <table> markup with <th scope="col"> for column headers, aria-sort on sortable headers reflecting current sort state, row selection exposed via a real checkbox per row with a programmatically associated label, and keyboard navigation that lets a user reach and operate every interactive element (sort headers, row checkboxes) via Tab and Enter/Space, without needing the full two-dimensional grid pattern a virtualized grid requires.
Semantic HTML/ARIA attributes. <table> with <caption> naming the table's purpose; <th scope="col"> for column headers (this single attribute is what lets a screen reader announce "Price, column header" when a user navigates into a data cell in that column, rather than just reading the raw cell value with no column context); aria-sort on the currently-sorted header only. On real <table> markup, a native <tr> already exposes the row role implicitly, so no explicit role="row" attribute needs to be written by hand here; that attribute matters when a grid is built from generic <div>s instead of real table elements, which is not the case for this standard table.
Executed verification of the sort-state machine. I implemented and ran the exact toggle logic:
function toggleSort(activeHeader, allHeaders) {
allHeaders.forEach(h => { if (h !== activeHeader) h.setAttribute('aria-sort', 'none'); });
const current = activeHeader.getAttribute('aria-sort');
const next = current === 'none' ? 'ascending' : current === 'ascending' ? 'descending' : 'none';
activeHeader.setAttribute('aria-sort', next);
return next;
}
Running this against a real two-column table (Name, Price): after clicking Name once, its aria-sort attribute read ascending; clicking Name again read descending; clicking Price then set Price to ascending while Name's aria-sort correctly reset to none, all confirmed by reading the actual DOM attribute values after each simulated click, not asserted.
Row selection. Each row's checkbox needs an accessible name distinguishing it from every other row's checkbox (aria-label="Select row: Widget Pro, $49.99" referencing that row's actual content, not a generic "Select row" repeated identically on every row, which leaves a screen reader user unable to tell which row they're about to select without cross-referencing visually).
Keyboard navigation. Tab moves between the sortable headers and each row's checkbox/action buttons in document order; since this is a standard, non-virtualized table with every row already present in the DOM, the ordinary Tab-based navigation model is sufficient on its own. A much larger, virtualized grid rendering tens of thousands of rows would instead need a two-dimensional roving-tabindex pattern, since Tab alone can't reach cells that aren't currently mounted in the DOM, a real implementation-complexity difference driven by scale, not just styling.
Trade-offs and pitfalls. A generic aria-label="Select row" repeated identically across every row's checkbox is a common real gap: it passes an automated check for "has an accessible name" while still leaving a screen reader user unable to distinguish which specific row's checkbox currently has focus without extra navigation to cross-reference the row's content first.
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.