Key Takeaways
- Ensure semantic accuracy in your markup to enhance the compatibility with screen readers and other assistive technologies, utilizing WAI-ARIA attributes where necessary.
- Implement comprehensive keyboard support for your web components to ensure they can be fully operated without a mouse, following the ARIA guidelines for keyboard interactions.
- Validate visual accessibility by ensuring elements are visible in high contrast mode, sizable for those with visual impairments, and functional without reliance on color.
- Extend the accessibility of custom components by applying appropriate ARIA roles, states, and properties, mirroring native HTML functionalities as closely as possible.
- Regularly test accessibility features with tools and user feedback to identify and address potential barriers, ensuring a robust experience for all users.
What Does Web Component Accessibility Entail?
When talking about the accessibility of a component we usually consider the following aspects:- Markup semantics
- Keyboard support
- Visual accessibility
Markup Semantics
I’m sure you’ve heard about screen readers. A screen reader is a piece of assistive software that allows blind or visually impaired people to use applications by reading aloud information displayed on the screen. There are many screen readers out there, among them NVDA and JAWS for Windows, ChromeVox for Chrome, and VoiceOver for OS X. When an element receives focus, the screen reader offers information about it to the user. Thus when an HTML<input type="text">
is focused the user knows from the screen reader that they are dealing with text field (and can input something). But if the element is just a bare <div>
, the screen reader has nothing to say about it.
To solve this issue we can use WAI-ARIA (Web Accessibility Initiative – Accessible Rich Internet Applications) to add special ARIA attributes to extend the semantics of the component markup. These additional semantics help assistive technologies to identify properties, relationships, and states in your user interfaces. The practical guidelines for using ARIA can be found here: WAI-ARIA Authoring Practices, alternatively (for a quick refresher) you could read our Introduction to WAI-ARIA.
Keyboard Support
The goal is to make it possible to interact with a component using only the keyboard. WAI-ARIA defines behavior and the keyboard interactions for many UI controls. To know which keys should be supported by which component, find the description of your component or a similar one in the specification and use that. For instance, the multiselect is similar to the combobox. Even with keyboard accessibility in place, it’s good practice to let users know which keys/key combos to use to interact with the component (for example by providing some instructions in the application) since this might not be obvious.Visual Accessibility
Here we are talking about accessibility aspects related to the component’s appearance. Ensure that you can answer ‘yes’ to the following questions:- Are the elements and text big enough to clearly see them?
- Does your component look as expected in high contrast mode?
- Is it possible to use your component without colors?
Making the Multiselect Web Component Accessible
Now we’re going to make the multiselect more accessible using all of the techniques outlined above. Specifically, we’re going to:- extend the markup semantics
- add keyboard support
- validate its visual accessibility
Extending Markup Semantics
The accessibility rule of thumb is to use native HTML elements over custom ones. This means, if you can use a native HTML control with built-in accessibility, do so. Add ARIA attributes only if you really need to create a custom component. If you’d like to find out more about this, read Avoiding Redundancy with WAI-ARIA in HTML Pages. In our case the multiselect is a custom component, so we need to add ARIA attributes. First, let’s find a component similar to the multiselect in the ARIA specification. After a little research, it appears that the combobox looks and behaves similarly. Great, now let’s see which ARIA attributes we need to add according to the combobox description. From guidelines we can see that we need to add the following roles:role="combobox"
to the root element of the componentrole="listbox"
to the list of items in the popuprole="option"
to each item of the dropdown list
aria-expanded="true/false"
to the root element to indicate whether the component is opened or closedaria-selected="true/false"
to each item of the dropdown list to indicate selected state
combobox
and listbox
can be added directly to the markup of the component:
<div class="multiselect" role="combobox">
<div class="multiselect-field"></div>
<div class="multiselect-popup">
<ul class="multiselect-list" role="listbox">
<content select="li"></content>
</ul>
</div>
</div>
To add the role option
to each item of the list we loop over items in the refreshItems
method. This new method is called when the component is rendered:
multiselectPrototype.render = function() {
this.attachHandlers();
this.refreshField();
this.refreshItems();
};
multiselectPrototype.refreshItems = function() {
var itemElements = this.itemElements();
for(var i = 0; i < itemElements.length; i++) {
var itemElement = itemElements[i];
// set role and aria-selected property of an item
itemElement.setAttribute("role", "option");
itemElement.setAttribute("aria-selected", itemElement.hasAttribute("selected"));
}
};
multiselectPrototype.itemElements = function() {
return this.querySelectorAll('li');
};
The aria-expanded
attribute can be added to the control in the togglePopup
method which (as the name suggests) is responsible for showing and hiding the popup:
multiselectPrototype.togglePopup = function(show) {
this._isOpened = show;
this._popup.style.display = show ? 'block' : 'none';
// set aria-expanded property
this._control.setAttribute("aria-expanded", show);
};
We also initialize the aria-selected
property of items depending on their selected
attribute. The aria-selected
property should be maintained to reflect the current item’s selected state. We can do that in selectItem
and unselectItem
methods:
multiselectPrototype.selectItem = function(item) {
if(!item.hasAttribute('selected')) {
// set aria-selected property of selected item
item.setAttribute('aria-selected', true);
item.setAttribute('selected', 'selected');
this.fireChangeEvent();
this.refreshField();
}
this.close();
};
multiselectPrototype.unselectItem = function(item) {
// set aria-selected property of unselected item
item.setAttribute('aria-selected', false);
item.removeAttribute('selected');
this.fireChangeEvent();
this.refreshField();
};
And that’s it, the ARIA properties have been added. The next step is keyboard support.
Adding Keyboard Support
Let’s open the specification and look at the Keyboard Interactions section to see which interactions we need to support. Here is the basic set of keys to be able to use the multiselect with the keyboard only:Alt + Up/Down Arrow
– open/close the multiselectEsc
– close the multiselectUp/Down Arrow
– navigate through itemsEnter
– select an item when multiselect is openedBackspace
– unselect the last selected item
Making It Focusable
The very first step towards adding keyboard support is to make a component focusable. To do that we need to set the tabindex attribute, whose behavior differs depending on thetabindex
value:
- positive integer—defines the order of the element in keyboard focus navigation
0
– the order of the element in keyboard focus navigation is defined by the browser-1
– the element cannot be reached with keyboard focus navigation, but can now receive focus programatically using JavaScript’sfocus()
method.
tabindex
should be either -1
or 0
, because we cannot know the order of the element on the target page. Thus we set tabindex
on multiselect field to 0
directly in the markup:
<div class="multiselect-field" tabindex="0" aria-readonly="true"></div>
The next step is to handle the keydown
event on the multiselect:
multiselectPrototype.attachHandlers = function() {
this._control.addEventListener('keydown', this.keyDownHandler.bind(this));
...
};
The keyDownHandler
method calls the particular key handler depending on the event.which
property value:
multiselectPrototype.keyDownHandler = function(event) {
switch(event.which) {
case 8: // Backspace
this.handleBackspaceKey();
break;
case 13: // Enter
this.handleEnterKey();
break;
case 27: // Escape
this.handleEscapeKey();
break;
case 38: // Up Arrow
event.altKey ? this.handleAltArrowUpKey() : this.handleArrowUpKey();
break;
case 40: // Down Arrow
event.altKey ? this.handleAltArrowDownKey() : this.handleArrowDownKey();
break;
default:
return;
}
// prevent native browser key handling
event.preventDefault();
};
Once the key press is handled, we prevent the browser from carrying out its standard action by calling event.preventDefault()
.
Open/Close with Keyboard
TheAlt + Down Arrow
key combo should open the multiselect, while Alt + Up Arrow
and Esc
keys should close it:
multiselectPrototype.handleAltArrowDownKey = function() {
this.open();
};
multiselectPrototype.handleAltArrowUpKey = function() {
this.close();
};
multiselectPrototype.handleEscapeKey = function() {
this.close();
};
The open
and close
methods just call the togglePopup
method:
multiselectPrototype.open = function() {
this.togglePopup(true);
};
multiselectPrototype.close = function() {
this.togglePopup(false);
};
Navigate Items with the Keyboard
Firstly, we set thetabindex
of each multiselect item to -1
, so they become focusable:
multiselectPrototype.refreshItems = function() {
var itemElements = this.itemElements();
for(var i = 0; i < itemElements.length; i++) {
var itemElement = itemElements[i];
...
// set item tabindex attribute
itemElement.setAttribute("tabindex", -1);
}
// initialize focused item index
this._focusedItemIndex = 0;
};
The _focusedItemIndex
property stores the index of the focused item.
Up Arrow
and Down Arrow
keys allow the user to navigate over the items in the list maintaining the current focused item index:
multiselectPrototype.handleArrowDownKey = function() {
this._focusedItemIndex = (this._focusedItemIndex < this.itemElements().length - 1)
? this._focusedItemIndex + 1 // go to the next item
: 0; // go to the first item
this.refreshFocusedItem();
};
If the Down Arrow
is pressed at the end of the list, the focus goes to the first item.
If the Up Arrow
is pressed on the first item of the list, the focus goes to the last list item:
multiselectPrototype.handleArrowUpKey = function() {
this._focusedItemIndex = (this._focusedItemIndex > 0)
? this._focusedItemIndex - 1 // go to the previous item
: this.itemElements().length - 1; // go to the last item
this.refreshFocusedItem();
};
The refreshFocusedItem
method sets focus to the item with the index equal to _focusedItemIndex
:
multiselectPrototype.refreshFocusedItem = function() {
this.itemElements()[this._focusedItemIndex].focus();
};
Finally, we need to change the open
and close
methods so that, when the multiselect is opened the focus goes to the item with the index _focusedItemIndex
, and when the control is closed the focus goes back to the multiselect field:
multiselectPrototype.open = function() {
this.togglePopup(true);
this.refreshFocusedItem();
};
multiselectPrototype.close = function() {
this.togglePopup(false);
this._field.focus();
};
Now we can add some CSS to make the focused item look visually more appealing:
::content li:focus {
outline: dotted 1px #333;
background: #efefef;
}
Select/Deselect Item with Keyboard
TheEnter
key allows the user to select the current focused item. If the multiselect is opened we get the focused item and select it with selectItem
method:
multiselectPrototype.handleEnterKey = function() {
if(this._isOpened) {
var focusedItem = this.itemElements()[this._focusedItemIndex];
this.selectItem(focusedItem);
}
};
The Esc
key should remove the last selected item. If any selected items are present, we take the last one and deselect it by calling the unselectItem
method:
multiselectPrototype.handleBackspaceKey = function() {
var selectedItemElements = this.querySelectorAll("li[selected]");
if(selectedItemElements.length) {
this.unselectItem(selectedItemElements[selectedItemElements.length - 1]);
}
};
Now we have supported all necessary keyboard interactions, so the component can be used with the keyboard only.
Visual Accessibility
Component Size
The multiselect has relative sizes inem
and its size depends on the font size of the container. Thus it’s scalable and can be easily increased if necessary:
High Contrast Mode
People with low vision or other visual disabilities sometimes use high contrast mode. OS X allows users to enable high contrast in the settings, whereas Windows provides special High Contrast Themes. There is also a popular Chrome extension called (surprisingly) High Contrast that allows users to browse the web with high contrast color filters. Let’s see how our component looks in high contrast mode: It looks ok, but there is an issue: the selected items are hardly distinguishable from the non-selected ones. A little change of color for selected and focused items fixes the issue:Without Colors
Color accessibility is another important aspect of visual accessibility. There are many color-blind users exploring the web. This means that color should not be the only way to convey important information to the user. Here we can check how our component looks in grayscale mode (it’s usually provided by OS or special applications). Saying this, our component only consists of gray colors, so this is a check that we can skip.Demo
Having implemented all of the above, this is the final product:See the Pen Multiselect Web Component by SitePoint (@SitePoint) on CodePen.
Conclusion
To make a web component more accessible we need to be sure that:- The markup is semantic, so that assistive technologies such as screen readers can help users when they interact with the component. To do that try to use native HTML controls or, in the case of custom controls, add meaningful ARIA attributes.
- The component can be used with the keyboard only. To achieve that make your component focusable with
tabindex
. Implement keyboard interactions following the ARIA practical guidelines. - The component can be used in high contrast mode and without colors.
Frequently Asked Questions (FAQs) on Accessible Web Components
What are the key principles of creating accessible web components?
The key principles of creating accessible web components are perceivability, operability, understandability, and robustness. Perceivability means that the information and user interface components must be presentable to users in ways they can perceive. Operability means that the user interface components and navigation must be operable. Understandability means that the information and the operation of the user interface must be understandable. Robustness means that content must be robust enough that it can be interpreted reliably by a wide variety of user agents, including assistive technologies.
How can I make my web components more accessible?
There are several ways to make your web components more accessible. First, use semantic HTML as it provides meaning to your web content, making it easier for screen readers to interpret. Second, ensure that your website is keyboard-friendly. Third, use ARIA roles and properties to enhance the accessibility of your web components. Lastly, always test your website with accessibility tools to identify and fix any issues.
What is ARIA and how does it improve web accessibility?
ARIA stands for Accessible Rich Internet Applications. It is a set of attributes that define ways to make web content and web applications more accessible to people with disabilities. ARIA helps with dynamic content and advanced user interface controls developed with Ajax, HTML, JavaScript, and related technologies. It improves web accessibility by providing additional semantics to help assistive technologies convey appropriate information to users.
Why is keyboard accessibility important in web components?
Keyboard accessibility is important because many users rely on the keyboard to navigate and interact with websites. This includes users with motor disabilities who find using a mouse difficult, blind users who typically use a keyboard with screen reader software, and power users who prefer keyboard navigation for efficiency. Ensuring your web components are keyboard accessible enhances the user experience for these users.
How can I test the accessibility of my web components?
There are several tools and techniques you can use to test the accessibility of your web components. Automated tools like Lighthouse, WAVE, and AXE can help identify common accessibility issues. Manual testing techniques include keyboard testing, screen reader testing, and color contrast analysis. It’s also beneficial to involve users with disabilities in your testing process to gain valuable insights into real-world accessibility.
What are some common mistakes to avoid when creating accessible web components?
Some common mistakes to avoid include not providing alternative text for images, not ensuring sufficient color contrast, not providing keyboard access to all functionality, using complex tables without proper markup, and not properly labeling form elements. Avoiding these mistakes can greatly improve the accessibility of your web components.
How does semantic HTML improve web accessibility?
Semantic HTML uses specific tags to provide meaning to web content. This helps screen readers and other assistive technologies understand the content and present it in a useful way to users with disabilities. For example, using the
What are some examples of ARIA roles and properties?
ARIA roles and properties provide additional semantics to help assistive technologies understand the content. Examples of ARIA roles include “button”, “checkbox”, “dialog”, and “navigation”. Examples of ARIA properties include “aria-label”, “aria-required”, “aria-hidden”, and “aria-live”. These roles and properties can be used to enhance the accessibility of your web components.
How can I ensure my web components are robust and can be interpreted by a wide variety of user agents?
To ensure your web components are robust, follow the principles of progressive enhancement. Start with a basic layer of user experience that works in all browsers, then add enhancements for browsers that support them. Use valid and semantic HTML, CSS, and JavaScript. Test your website in different browsers and assistive technologies to ensure it works consistently.
What are some resources to learn more about web accessibility?
There are many resources available to learn more about web accessibility. The Web Accessibility Initiative (WAI) provides guidelines and techniques. The A11Y Project is a community-driven effort to make web accessibility easier. WebAIM provides comprehensive web accessibility resources. Additionally, many online courses and tutorials are available on platforms like Udemy, Coursera, and LinkedIn Learning.
Artem is a web developer with 8 years experience in back-end and front-end development. He worked at DevExpress creating DevExtreme and currently works at Criteo. He loves learning new web-related things and believes in open source.