Web Accessibility (a11y)
ARIA, semantic HTML, and accessibility best practices
Accessibility (a11y) means building web applications that people with disabilities can use. In interviews, accessibility questions reveal whether you understand that the web is a public utility — and whether you can implement it, not just define it.
Semantic HTML — The Foundation
Using the right HTML element gives you keyboard interaction, ARIA roles, and screen reader announcements for free. Native elements are always preferable to custom implementations.
<!-- Bad: div soup with manual ARIA -->
<div role="button" tabindex="0" onclick="submit()">Submit</div>
<!-- Good: native button with all behaviours built in -->
<button type="submit">Submit</button>
<!-- Semantic structure -->
<header>...</header>
<nav aria-label="Main navigation">...</nav>
<main>
<article>
<h1>Page Title</h1>
...
</article>
</main>
<footer>...</footer>
ARIA — When Semantic HTML Is Not Enough
ARIA (Accessible Rich Internet Applications) attributes convey meaning to assistive technologies for custom components that have no native HTML equivalent. The first rule of ARIA: don't use ARIA if a native element can do the job.
<!-- Custom toggle button -->
<button
role="switch"
aria-checked="true"
aria-label="Enable notifications"
>
<span aria-hidden="true">🔔</span>
</button>
<!-- Live region — announces dynamic changes to screen readers -->
<div aria-live="polite" aria-atomic="true" class="sr-only">
<!-- Inject status messages here with JS -->
</div>
<!-- Modal dialog -->
<div role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">Confirm deletion</h2>
...
</div>
Keyboard Navigation
Every interactive element must be reachable and operable by keyboard alone. Focus must be visible at all times.
/* Never suppress focus outline — style it instead */
:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
border-radius: 2px;
}
/* Visually hide but keep accessible — for skip links and sr-only text */
.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;
}
<!-- Skip link — lets keyboard users jump past navigation -->
<a class="sr-only focus:not-sr-only" href="#main-content">Skip to main content</a>
Focus Management in SPAs
// After a route change, move focus to the main heading
function handleRouteChange() {
const heading = document.querySelector('h1') || document.querySelector('#main-content');
if (heading) {
heading.tabIndex = -1; // make it focusable if not interactive
heading.focus();
}
}
// Trap focus inside a modal
function trapFocus(modal) {
const focusable = modal.querySelectorAll(
'a[href], button:not([disabled]), input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
modal.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
});
}
Colour Contrast
WCAG 2.1 AA requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (18px+ or 14px+ bold). Use the browser DevTools colour picker or a tool like Colour Contrast Analyser.
Common Interview Questions
- What is the difference between
aria-label,aria-labelledby, andaria-describedby? - How do you handle keyboard interaction in a custom dropdown?
- When should you use
role="presentation"? - What WCAG level does your team target and why?