Building Accessible Components with Vanilla JavaScript
Accessibility is a cornerstone of modern web development. Every user deserves equitable access to online content, regardless of their abilities or disabilities. In this blog post, we’ll explore how to build accessible components using vanilla JavaScript. We’ll cover essential practices, techniques, and examples to ensure your web applications are inclusive for all users.
Why Accessibility Matters
According to the World Health Organization, over 1 billion people experience some form of disability. Web accessibility ensures that the digital world is navigable for everyone, including those with disabilities such as visual impairments, hearing loss, and motor challenges. Building accessible web components not only fulfills legal requirements in many regions but also enhances your project’s reach and usability.
Understanding ARIA Roles and Attributes
Accessible Rich Internet Applications (ARIA) provides a set of attributes that can be added to HTML to improve accessibility. While semantic HTML elements like <button> and <form> already convey their purpose to assistive technologies, ARIA roles and attributes help clarify more complex components.
For instance, if you’re creating a custom dropdown menu, you can use ARIA attributes to convey its structure and behavior:
<div role="combobox" aria-expanded="false">
<input type="text" aria-autocomplete="list" aria-haspopup="true" />
<div role="listbox">
<div role="option">Option 1</div>
<div role="option">Option 2</div>
<div role="option">Option 3</div>
</div>
</div>
Creating an Accessible Button Component
Let’s start with a simple yet essential UI component: a button. While we could use the <button> element directly, we can create a stylized button using a <div> or <a> element while maintaining accessibility via JavaScript.
HTML Structure
<div id="myButton" tabindex="0" role="button" aria-pressed="false">
Click Me!
</div>
JavaScript Implementation
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
const pressed = button.getAttribute('aria-pressed') === 'true';
button.setAttribute('aria-pressed', !pressed);
});
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
button.click();
e.preventDefault();
}
});
In this example, we’ve created a non-button element that behaves like a button. We’ve set attributes to convey its role and state. The use of tabindex="0" allows it to be focusable, which is essential for keyboard navigation.
Building an Accessible Modal
Modals are another common component in web applications. It’s crucial to ensure they are accessible to users who rely on keyboard navigation and screen readers.
HTML Structure
<div id="modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle" style="display:none">
<h2 id="modalTitle">Modal Title</h2>
<p>This is an accessible modal window.</p>
<button id="closeModal">Close</button>
</div>
JavaScript to Control the Modal
const modal = document.getElementById('modal');
const closeModal = document.getElementById('closeModal');
function openModal() {
modal.style.display = 'block';
modal.setAttribute('aria-hidden', 'false');
closeModal.focus(); // Move focus to close button
}
function closeModalFunction() {
modal.style.display = 'none';
modal.setAttribute('aria-hidden', 'true');
document.getElementById('openModalButton').focus(); // Return focus to the trigger
}
closeModal.addEventListener('click', closeModalFunction);
// Close modal on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.style.display === 'block') {
closeModalFunction();
}
});
Focus Management
One crucial aspect of accessible components is managing focus. When a modal opens, focus should be shifted to an element inside the modal. Conversely, when the modal closes, focus should return to the element that triggered it. This is essential for keyboard users, as it allows them to continue navigating the application intuitively.
Creating an Accessible Tab Component
Tab navigation is a common interface pattern, allowing users to navigate through sections of content without leaving the page. Here’s how to implement an accessible tab component using vanilla JavaScript.
HTML Structure
<div role="tablist">
<div role="tab" aria-selected="true" tabindex="0">Tab 1</div>
<div role="tab" tabindex="0">Tab 2</div>
<div role="tab" tabindex="0">Tab 3</div>
</div>
<div role="tabpanel">
<p>Content for Tab 1</p>
</div>
JavaScript Implementation
const tabs = document.querySelectorAll('[role="tab"]');
const panels = document.querySelectorAll('[role="tabpanel"]');
tabs.forEach(tab => {
tab.addEventListener('click', (e) => {
tabs.forEach(t => t.setAttribute('aria-selected', 'false'));
tab.setAttribute('aria-selected', 'true');
panels.forEach(panel => panel.hidden = true);
const index = Array.from(tabs).indexOf(tab);
panels[index].hidden = false;
});
tab.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') {
const nextTab = tab.nextElementSibling || tabs[0];
nextTab.focus();
nextTab.click();
} else if (e.key === 'ArrowLeft') {
const prevTab = tab.previousElementSibling || tabs[tabs.length - 1];
prevTab.focus();
prevTab.click();
}
});
});
Form Elements and Accessibility
Forms are critical in web applications, and properly labeling elements is vital for screen readers. Always include explicit labels and announcements for input states.
Example Form Component
<form aria-labelledby="formTitle">
<h2 id="formTitle">Contact Us</h2>
<label for="name">Name:</label>
<input type="text" id="name" required />
<label for="email">Email:</label>
<input type="email" id="email" required />
<button type="submit">Send</button>
</form>
Live Feedback
Providing real-time feedback for form inputs is also vital. Here’s how to do it:
const form = document.querySelector('form');
const emailInput = document.getElementById('email');
emailInput.addEventListener('input', () => {
const message = emailInput.validity.valid ? 'Valid email!' : 'Invalid email, please correct it.';
emailInput.setAttribute('aria-describedby', 'emailFeedback');
document.getElementById('emailFeedback').textContent = message;
});
Best Practices for Building Accessible Components
- Use Semantic HTML: Always prefer semantic HTML over styling hacks for accessibility.
- Check Focus Order: Ensure focus order is logical and intuitive.
- Perform Regular Testing: Use accessibility testing tools like Lighthouse and screen readers to evaluate your components.
- Stay Updated: Follow web accessibility guidelines such as WCAG (Web Content Accessibility Guidelines) for the latest practices.
Conclusion
Building accessible components with vanilla JavaScript is more than just a good practice; it’s an ethical obligation. By leveraging semantic HTML, ARIA roles, and thoughtful event handling, you can create a more inclusive web experience. Remember, accessibility isn’t a one-time consideration; it should be an ongoing part of your development process. Let’s build a web that works for everyone!
