Over 1 billion people worldwide live with some form of disability, and web accessibility lawsuits in the US have increased 300% since 2018. Yet 96% of home pages have detectable WCAG failures. This isn't just a legal risk—it's a massive gap between what developers know they should do and what they actually implement. Here's how to nail accessibility questions in interviews.
The 30-Second Answer
When the interviewer asks "What is web accessibility?", here's your concise answer:
"Web accessibility means building websites that everyone can use, including people with visual, hearing, motor, or cognitive disabilities. The key practices are: use semantic HTML elements instead of divs, provide alt text for images, ensure keyboard navigation works, maintain sufficient color contrast, and use ARIA attributes when native HTML isn't enough. The standard is WCAG 2.1, and most sites target Level AA compliance."
Wait for follow-up questions.
The 2-Minute Answer (If They Want More)
If they ask you to elaborate:
"Accessibility, often called a11y, is about removing barriers. Different users need different accommodations:
- Blind users rely on screen readers that read content aloud
- Low-vision users need zoom and high contrast
- Deaf users need captions and transcripts
- Motor-impaired users navigate with keyboards only
- Cognitively impaired users need clear language and consistent layouts
The foundation is semantic HTML - using
buttoninstead ofdiv,navfor navigation,mainfor content. Screen readers use these to help users navigate.ARIA fills gaps when HTML isn't enough - like
aria-labelfor icon buttons oraria-livefor dynamic updates.Testing is crucial: keyboard-only navigation, screen reader testing, automated tools like axe or Lighthouse, and manual review.
It's not just ethics - it's often legal. ADA lawsuits against inaccessible websites are increasing, and it also improves SEO since search engines rely on the same semantic structure."
Code Examples to Show
Semantic HTML vs Div Soup
<!-- BAD: Div soup - no meaning to screen readers -->
<div class="header">
<div class="nav">
<div class="nav-item" onclick="goHome()">Home</div>
<div class="nav-item" onclick="goAbout()">About</div>
</div>
</div>
<div class="content">
<div class="article">
<div class="title">Article Title</div>
<div class="text">Article content...</div>
</div>
</div>
<!-- GOOD: Semantic HTML - meaningful to everyone -->
<header>
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h1>Article Title</h1>
<p>Article content...</p>
</article>
</main>Accessible Button vs Fake Button
<!-- BAD: Div pretending to be a button -->
<div class="btn" onclick="submit()">Submit</div>
<!-- Problems: Not focusable, no keyboard support, not announced as button -->
<!-- GOOD: Native button element -->
<button type="submit">Submit</button>
<!-- Benefits: Focusable, Enter/Space work, announced as "Submit, button" -->
<!-- If you MUST use a div (rare), add accessibility: -->
<div
role="button"
tabindex="0"
onclick="submit()"
onkeydown="if(event.key === 'Enter' || event.key === ' ') submit()"
>
Submit
</div>
<!-- But why do all this when <button> does it automatically? -->Image Accessibility
<!-- Informative image - describe the meaning -->
<img
src="chart.png"
alt="Sales increased 50% from Q1 to Q2 2024"
/>
<!-- Decorative image - empty alt -->
<img src="decorative-border.png" alt="" />
<!-- Complex image - extended description -->
<figure>
<img
src="complex-chart.png"
alt="Quarterly sales comparison"
aria-describedby="chart-desc"
/>
<figcaption id="chart-desc">
Q1: $1M, Q2: $1.5M (50% increase), Q3: $1.8M, Q4: $2.1M.
Total annual growth: 110%.
</figcaption>
</figure>
<!-- Icon button - aria-label for meaning -->
<button aria-label="Close dialog">
<svg aria-hidden="true"><!-- X icon --></svg>
</button>The Classic Problem: Focus Management in Modals
This scenario tests real-world accessibility knowledge:
Interviewer: "How do you make a modal dialog accessible?"
The Bug (Inaccessible Modal)
<!-- BAD: Typical inaccessible modal -->
<div class="modal" style="display: block;">
<div class="modal-content">
<span class="close" onclick="closeModal()">×</span>
<h2>Modal Title</h2>
<p>Modal content</p>
</div>
</div>Problems:
- Focus doesn't move to modal when it opens
- User can tab to elements behind the modal
- Escape key doesn't close it
- Screen reader doesn't know it's a dialog
- Close button isn't keyboard accessible
The Solution (Accessible Modal)
<!-- GOOD: Accessible modal -->
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-describedby="modal-desc"
>
<h2 id="modal-title">Modal Title</h2>
<p id="modal-desc">Modal content goes here.</p>
<button onclick="closeModal()">Close</button>
</div>// Focus management
function openModal() {
const modal = document.querySelector('[role="dialog"]');
const firstFocusable = modal.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
// Store the element that opened the modal
previouslyFocused = document.activeElement;
// Show modal and move focus
modal.style.display = 'block';
firstFocusable.focus();
// Trap focus inside modal
modal.addEventListener('keydown', trapFocus);
// Close on Escape
document.addEventListener('keydown', handleEscape);
}
function closeModal() {
const modal = document.querySelector('[role="dialog"]');
modal.style.display = 'none';
// Return focus to trigger element
previouslyFocused.focus();
// Clean up listeners
modal.removeEventListener('keydown', trapFocus);
document.removeEventListener('keydown', handleEscape);
}
function trapFocus(e) {
if (e.key !== 'Tab') return;
const modal = e.currentTarget;
const focusable = modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
function handleEscape(e) {
if (e.key === 'Escape') closeModal();
}The lesson: "Accessible modals require focus management - move focus in, trap it inside, return it when closed, and support Escape to close. This is where many developers fail accessibility."
ARIA: The Double-Edged Sword
ARIA can help or hurt accessibility. Know the rules.
First Rule of ARIA: Don't Use ARIA
<!-- DON'T: Using ARIA when HTML works -->
<div role="button" tabindex="0">Click me</div>
<!-- DO: Use native HTML -->
<button>Click me</button>
<!-- DON'T: ARIA label on element with visible text -->
<button aria-label="Submit form">Submit</button>
<!-- DO: Just use the visible text -->
<button>Submit form</button>When ARIA Is Necessary
<!-- Icon-only button needs label -->
<button aria-label="Search">
<svg aria-hidden="true"><!-- magnifying glass --></svg>
</button>
<!-- Custom tabs widget -->
<div role="tablist" aria-label="Product information">
<button role="tab" aria-selected="true" aria-controls="panel-1">
Description
</button>
<button role="tab" aria-selected="false" aria-controls="panel-2">
Reviews
</button>
</div>
<div role="tabpanel" id="panel-1">Description content</div>
<div role="tabpanel" id="panel-2" hidden>Reviews content</div>
<!-- Live region for dynamic updates -->
<div aria-live="polite" aria-atomic="true">
<!-- Screen reader announces when content changes -->
Item added to cart
</div>
<!-- Loading state -->
<button aria-busy="true" disabled>
<span aria-hidden="true">Loading...</span>
<span class="sr-only">Please wait, submitting form</span>
</button>Common ARIA Attributes
<!-- aria-label: Accessible name when no visible text -->
<button aria-label="Close">×</button>
<!-- aria-labelledby: Reference another element as label -->
<dialog aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm Delete</h2>
</dialog>
<!-- aria-describedby: Additional description -->
<input type="email" aria-describedby="email-hint">
<p id="email-hint">We'll never share your email</p>
<!-- aria-expanded: Toggle state -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>...</ul>
<!-- aria-hidden: Hide from screen readers -->
<span aria-hidden="true">★★★☆☆</span>
<span class="sr-only">3 out of 5 stars</span>
<!-- aria-live: Announce dynamic changes -->
<div aria-live="assertive">Error: Invalid email</div>
<div aria-live="polite">3 items in cart</div>Keyboard Navigation Patterns
Skip Links
<!-- First element in body - skip to main content -->
<a href="#main-content" class="skip-link">
Skip to main content
</a>
<header>
<!-- Long navigation menu -->
</header>
<main id="main-content" tabindex="-1">
<!-- tabindex="-1" allows programmatic focus -->
</main>
<style>
.skip-link {
position: absolute;
top: -40px;
left: 0;
padding: 8px;
background: #000;
color: #fff;
z-index: 100;
}
.skip-link:focus {
top: 0; /* Visible when focused */
}
</style>Focus Indicators
/* BAD: Removing focus indicator */
*:focus {
outline: none; /* NEVER do this without replacement */
}
/* GOOD: Custom focus indicator */
*:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
/* BETTER: Only for keyboard users */
*:focus:not(:focus-visible) {
outline: none;
}
*:focus-visible {
outline: 2px solid #005fcc;
outline-offset: 2px;
}Expected Keyboard Behaviors
// Buttons: Enter and Space
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
button.click();
}
});
// Menus: Arrow keys
menu.addEventListener('keydown', (e) => {
const items = menu.querySelectorAll('[role="menuitem"]');
const current = Array.from(items).indexOf(document.activeElement);
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
items[(current + 1) % items.length].focus();
break;
case 'ArrowUp':
e.preventDefault();
items[(current - 1 + items.length) % items.length].focus();
break;
case 'Home':
e.preventDefault();
items[0].focus();
break;
case 'End':
e.preventDefault();
items[items.length - 1].focus();
break;
case 'Escape':
closeMenu();
break;
}
});Form Accessibility
Proper Form Labeling
<!-- BAD: No label association -->
<input type="email" placeholder="Email">
<!-- GOOD: Explicit label -->
<label for="email">Email address</label>
<input type="email" id="email" name="email">
<!-- ALSO GOOD: Implicit label -->
<label>
Email address
<input type="email" name="email">
</label>
<!-- Error messages -->
<label for="password">Password</label>
<input
type="password"
id="password"
aria-describedby="password-error password-requirements"
aria-invalid="true"
>
<p id="password-requirements">Must be at least 8 characters</p>
<p id="password-error" role="alert">Password is too short</p>
<!-- Required fields -->
<label for="name">
Name <span aria-hidden="true">*</span>
</label>
<input type="text" id="name" required aria-required="true">Fieldset for Related Inputs
<!-- Group related inputs -->
<fieldset>
<legend>Shipping Address</legend>
<label for="street">Street</label>
<input type="text" id="street" name="street">
<label for="city">City</label>
<input type="text" id="city" name="city">
</fieldset>
<!-- Radio buttons always need fieldset -->
<fieldset>
<legend>Preferred contact method</legend>
<input type="radio" id="contact-email" name="contact" value="email">
<label for="contact-email">Email</label>
<input type="radio" id="contact-phone" name="contact" value="phone">
<label for="contact-phone">Phone</label>
</fieldset>Common Follow-Up Questions
"What's the difference between aria-label, aria-labelledby, and aria-describedby?"
"
aria-labelprovides text directly as the accessible name - use it when there's no visible text.
aria-labelledbyreferences another element's ID whose text becomes the accessible name - use it when visible text exists elsewhere.
aria-describedbyreferences additional descriptive text that supplements the name - like hints or error messages. It's read after the name and role.Priority:
aria-labelledby>aria-label> native text content."
"How do you test for accessibility?"
"I use a combination:
- Automated tools - axe DevTools, Lighthouse, WAVE for quick scans
- Keyboard testing - Navigate entire site with Tab, Enter, Space, arrows
- Screen reader testing - VoiceOver (Mac), NVDA (Windows), or ChromeVox
- Zoom testing - 200% zoom, text-only zoom
- Color contrast checkers - WebAIM contrast checker
Automated tools catch only 30-40% of issues. Manual testing is essential."
"What is WCAG Level AA and what does it require?"
"WCAG AA is the standard compliance target. Key requirements:
- Color contrast: 4.5:1 for normal text, 3:1 for large text
- Resize: Content works at 200% zoom
- Keyboard: All functionality via keyboard
- Focus visible: Clear focus indicators
- Headings: Logical heading structure
- Forms: Labels, error identification, suggestions
- Timing: Adjustable time limits
- Navigation: Multiple ways to find pages, consistent navigation
Level A is minimum (like alt text), AAA is ideal (like sign language for video)."
"How do you handle dynamic content for screen readers?"
"Use ARIA live regions.
aria-live='polite'announces when the user is idle - good for status updates.aria-live='assertive'interrupts immediately - use for errors.Add
aria-atomic='true'if the entire region should be re-read on any change."
<!-- Status messages -->
<div aria-live="polite">
{{ cartItemCount }} items in cart
</div>
<!-- Error messages -->
<div aria-live="assertive" role="alert">
Form submission failed: {{ errorMessage }}
</div>What Interviewers Are Really Testing
When I ask about accessibility, I'm checking:
- Awareness - Do you consider users with disabilities?
- Semantic HTML - Do you know why it matters?
- ARIA knowledge - Do you know when to use it (and when not to)?
- Keyboard navigation - Can you implement proper focus management?
- Testing - Do you know how to verify accessibility?
A candidate who emphasizes semantic HTML first, knows the first rule of ARIA, and mentions testing with screen readers will stand out.
Quick Reference Card
| Task | Solution |
|---|---|
| Make element focusable | Native element or tabindex="0" |
| Hide from screen readers | aria-hidden="true" |
| Hide visually but keep accessible | .sr-only CSS class |
| Label icon button | aria-label="Action name" |
| Announce dynamic content | aria-live="polite" or "assertive" |
| Required field | required + aria-required="true" |
| Error state | aria-invalid="true" + aria-describedby |
| Expanded/collapsed | aria-expanded="true/false" |
| Group related inputs | <fieldset> + <legend> |
| Color contrast (AA) | 4.5:1 normal text, 3:1 large text |
Screen Reader Only CSS
/* Hide visually but keep accessible */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Allow element to be focusable when navigated to */
.sr-only-focusable:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
white-space: normal;
}Practice Questions
Test yourself before your interview:
1. What's wrong with this code?
<div onclick="submitForm()" class="btn-primary">Submit</div>2. How would you make this image accessible?
<img src="company-org-chart.png" />3. Write accessible HTML for a dropdown menu with a toggle button.
4. What does this do and when would you use it?
<div aria-live="polite" aria-atomic="true"></div>Answers:
-
Multiple issues: Not focusable, not keyboard accessible, not announced as button. Fix: Use
<button type="submit">Submit</button>. -
Depends on context. If informative:
alt="Organization chart showing CEO at top, 3 VPs reporting to CEO, and 2 managers under each VP". If decorative:alt="". If complex, addaria-describedbylinking to a detailed text description.
<div class="dropdown">
<button
aria-expanded="false"
aria-haspopup="true"
aria-controls="dropdown-menu"
>
Options
</button>
<ul id="dropdown-menu" role="menu" hidden>
<li role="menuitem"><a href="/edit">Edit</a></li>
<li role="menuitem"><a href="/delete">Delete</a></li>
</ul>
</div>- It's a live region that announces content changes to screen readers.
politemeans it waits for idle time,atomicmeans the entire content is re-read on any change. Use for status updates like "3 items in cart" or "Changes saved".
Related Articles
If you found this helpful, check out these related guides:
- Complete Frontend Developer Interview Guide - comprehensive preparation guide for frontend interviews
- CSS Flexbox vs Grid Interview Guide - Modern CSS layout techniques compared
- React 19 Interview Guide - New features like Actions, use() hook, and Server Components
Ready for More HTML5 Interview Questions?
This is just one topic from our complete HTML5 interview prep guide. Get access to 50+ HTML5 questions covering:
- Semantic elements and document structure
- Forms and validation
- Web Storage and IndexedDB
- Canvas and SVG
- Web Workers and Service Workers
Get Full Access to All HTML5 Questions →
Or try our free HTML5 preview to see more questions like this.
Written by the EasyInterview team, based on real interview experience from 12+ years in tech and hundreds of technical interviews conducted at companies like BNY Mellon, UBS, and leading fintech firms.
