frontend
Web Accessibility in JavaScript
January 24, 2026
Web Accessibility in JavaScript
Overview
Accessibility means creating websites and applications that everyone can use, regardless of their abilities or disabilities. It ensures that web content is perceivable, operable, understandable, and robust for all users, including those using assistive technologies.
Why Do We Need It?
- Ensures that people with disabilities can access and use web content
- Improves the overall user experience for everyone
- Many accessibility practices also benefit search engine optimization
- Legal compliance (ADA, WCAG guidelines)
- Increases potential user base
Tabindex Attribute
The tabindex attribute controls the order and focusability of elements when navigating through a webpage using the keyboard.
<!-- tabindex="0" means natural tab order -->
<button tabindex="0">Button 1</button>
<!-- tabindex="positive number" - goes in order -->
<button tabindex="1">Button 2</button>
<button tabindex="2">Button 3</button>
<!-- tabindex="-1" - Makes the element focusable only programmatically -->
<div tabindex="-1" id="modal">Modal Content</div>
Areas of Accessibility
1. Semantic HTML Tags
<header>
<nav>
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h1>Article Title</h1>
<p>Content</p>
</article>
</main>
<footer>Footer content</footer>
2. Alt Text for Images
<img src="image.jpg" alt="A beautiful scenery of mountains and a lake">
3. Keyboard Navigation
<!-- Ensure all interactive elements are accessible via keyboard -->
<a href="https://example.com" role="button">Link Styled as Button</a>
4. Form Labels
<label for="name">Name:</label>
<input type="text" id="name" name="name">
5. Contrast and Color
Ensure sufficient contrast between text and background colors (WCAG AA: 4.5:1 for normal text, 3:1 for large text).
6. ARIA Labels
<button aria-label="Submit Form">Click Me</button>
ARIA (Accessible Rich Internet Applications)
ARIA provides a way to make web applications more accessible to people with disabilities by defining a way to make dynamic content more accessible.
Common ARIA Attributes
<!-- Roles -->
<div role="button" tabindex="0">Custom Button</div>
<div role="alert">Error message</div>
<!-- States and Properties -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<div id="menu" aria-hidden="true">Menu content</div>
<!-- Labels -->
<input type="text" aria-label="Search" aria-required="true">
Assistive Technologies
- Screen readers: Read content aloud (NVDA, JAWS, VoiceOver)
- Screen magnifiers: Enlarge content for vision impairments
- Speech recognition: Voice input for navigation
- Alternate input devices: Switch controls, eye tracking
JavaScript Accessibility Best Practices
1. Keyboard Event Handling
element.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleClick();
}
});
2. Focus Management
function openModal() {
modal.style.display = "block";
modal.setAttribute("aria-hidden", "false");
modal.querySelector("button").focus();
}
function closeModal() {
modal.style.display = "none";
modal.setAttribute("aria-hidden", "true");
triggerButton.focus(); // Return focus to trigger
}
3. ARIA Live Regions
const liveRegion = document.createElement("div");
liveRegion.setAttribute("role", "status");
liveRegion.setAttribute("aria-live", "polite");
liveRegion.setAttribute("aria-atomic", "true");
document.body.appendChild(liveRegion);
function announce(message) {
liveRegion.textContent = message;
}
4. Skip Links
<a href="#main-content" class="skip-link">Skip to main content</a>
Testing Accessibility
Automated Testing
// Using axe-core
import axe from 'axe-core';
axe.run(document, (err, results) => {
if (err) throw err;
console.log(results.violations);
});
Manual Testing
- Test with keyboard only
- Test with screen reader
- Check color contrast
- Verify focus indicators
Common Accessibility Patterns
Accessible Modal
class AccessibleModal {
constructor(modalId) {
this.modal = document.getElementById(modalId);
this.trigger = null;
this.focusableElements = null;
}
open(triggerElement) {
this.trigger = triggerElement;
this.modal.style.display = "block";
this.modal.setAttribute("aria-hidden", "false");
this.trapFocus();
this.focusableElements[0].focus();
}
close() {
this.modal.style.display = "none";
this.modal.setAttribute("aria-hidden", "true");
if (this.trigger) {
this.trigger.focus();
}
}
trapFocus() {
this.focusableElements = this.modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = this.focusableElements[0];
const lastElement = this.focusableElements[this.focusableElements.length - 1];
this.modal.addEventListener("keydown", (e) => {
if (e.key === "Tab") {
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
}
if (e.key === "Escape") {
this.close();
}
});
}
}
WCAG Guidelines
Level A (Minimum)
- Basic accessibility requirements
Level AA (Recommended)
- 4.5:1 contrast ratio for text
- Keyboard accessible
- No content that flashes more than 3 times per second
Level AAA (Enhanced)
- 7:1 contrast ratio for text
- Sign language interpretation
- Extended audio descriptions
Best Practices
- Use Semantic HTML: Prefer native elements
- Provide Alt Text: For all images
- Ensure Keyboard Access: All functionality via keyboard
- Maintain Focus Order: Logical tab order
- Provide Feedback: Announce changes to screen readers
- Test Regularly: Use automated and manual testing
- Follow WCAG: Adhere to accessibility guidelines