Making Web Components Accessible
In order for the web to be accessible to people with disabilities, it is fundamental that different components of web development and interaction work together. Component accessibility is essential; you can’t create accessible web sites if your components are not accessible. As component developers, accessibility has always been a focus for us, and we have been constantly improving it.
Over the last few months, some of our clients started working closely with us, sharing their test results and user feedback with us. This has helped us validate and improve our accessibility support. In this article, we describe our experience making components more accessible. We plan to continue improving accessibility support by tracking ARIA standards and by using feedback from our customers.
Role attribute
The first step required to make components accessible is to use the role attribute. The role attribute bridges areas with accessibility issues that can't be managed with native HTML. It works by allowing you to specify attributes that modify the way an element is translated into the accessibility tree. For example, you can tell browsers and assistive technologies that a specific div element acts as a menu, grid, or listbox.
There are pre-defined ARIA roles recognized by browsers and assistive technologies. They can be classified in “widget,” “composite,” “document structure,” and “landmark” roles. You can find a complete list of roles in the The Roles Model section of the W3C documentation. Many components have parts that play related roles. For example, elements with the “grid” role typically contains elements with the “row” role, which in turn, contain elements with the “gridcell” role. Getting all the roles right in complex components is not a trivial task. To help with this, Chrome has an accessibility audit tool that analyzes the DOM and reports improper usage of the role attribute.
Accessible Rich Internet Applications (ARIA) attributes
Once you have the role attribute properly set on the elements that make up the component, the next step is to add ARIA attributes that describe the current state of the component parts. These attributes depend on the element’s role and may include, “aria-selected,” “aria-required,” and “aria-valuenow.”
There are also generic attributes such as “aria-label” and “aria-labelledby” which are application-dependent and harder to provide by developers of generic components. In these cases, it is important that components raise events at appropriate times so developers can add their own custom ARIA labels to the component’s elements.
ARIA labels and internationalization
The aria-label attribute is important for elements that do not have any text content. For example, buttons with images and no text. In these cases, assistive technologies use the aria-label attribute to provide context information for the element. Providing adequate aria-label values is a challenge for web components that may be used in different cultures. In these cases, the label values must be globalized to target the supported cultures.
Wijmo handles this by including over 40 culture files that contain UI strings (including aria-labels) for different cultures. The culture files are generated automatically by a tool that uses Microsoft’s Multilingual App Toolkit (MAT). The toolkit includes automatic machine translation and maintenance tools that allow us to track and update translations as needed. In our experience, generating and maintaining culture files manually is an option if you have only a few strings and a few cultures to support. In all other cases, a tool is essential to keep the culture files up to date.
Keyboard support
The addition of ARIA attributes to component elements is an important first step in accessibility support. Also important is consistent and efficient keyboard handling. The W3C WAI-ARIA Authoring Practices document describes the keyboard interactions commonly expected for common types of component.
Labelling custom components
Label elements are important because they help identify input elements and they improve mouse handling. Clicking on a label raises the click event for the labeled element. However, label elements can only be used to label “labelable” elements, which includes only a few specific HTML elements (button, input, textarea, select, and a few others).
To support labels, custom components have to use a technique such as the one described by Rob Dodson in his How to Label Custom Elements video. The basic idea is to ensure the label has an ID and then to link your component’s input element to the label using the aria-labelledby attribute.
All Wijmo input components use this technique, so you can write markup such as this:
<!-- component contained in the label --> <label>Combo <div id="cmb"></div> </label> <!-- label with "for" attribute--> <label for="theDate">InputDate</label> <div id="theDate"></div>
When the components are instantiated, they will automatically check for containing labels, or for labels that target the component’s host element. If a label is found, the control will ensure it has a unique ID and will automatically add an aria-labelledby attribute to the proper input element within the component, all automatically. Clicking the labels will automatically transfer the focus to the component’s input element as expected.
Roving Tab Index
Users expect the cursor keys to move the selection to the next/previous item (character, cell, tab, node) within the currently focused component. The tab key should move the focus to the next/previous component on the page.
Components that contain multiple child elements should make sure only one of their child elements is focusable. Failing to do so creates “keyboard traps” that frustrate users. To accomplish this, many components use a technique called “roving tabindex.” This technique consists of changing the “tabindex” attribute of a component’s child elements so as the user tabs through the page, the component represents a single stop, with the currently selected sub-element receiving the focus.
Many Wijmo components implement a roving tab index, including the FlexGrid, ListBox, Calendar, TabPanel, and TreeView components.
For more details, check Rob Dodson’s Roving TabIndex video or the Roving TabIndex video or the “Roving Tabindex” section on the Keyboard-navigable JavaScript widgets MDN article.
Focus Indicators
By default, browsers add a “focus ring” around the element that currently has the keyboard focus. This is important because without it, users may have a hard time telling which element currently has the focus. This is a basic requirement in the WCAG 2.0 checklist (2.4.7). Because of this, simply turning off the browser’s focus ring is usually not a good idea. But the default focus ring has problems:
- It may not fit with your design (e.g. blue outline on a blue background).
- It is inconsistent between browsers (blue line in Chrome, dotted line in IE/Edge).
- It works for HTML elements, but not for composite components.
Fortunately, there are easy ways to fix these issues. A keyboard focus indicator can take different forms. One common way is a caret within the text field to indicate that the text field has the keyboard focus. Another is a visual change to a button to indicate that that button has the keyboard focus.
This Udacity course has examples that show how you can use CSS to specify the appearance of the focus ring to achieve a consistent look that matches your design:
*:focus { outline: 2px solid rgba(90, 160, 215, .5); outline-offset: 2px; }
This takes care of the first two issues above. The last issue is more challenging. Take for example a simple ComboBox component:
The component is made up of HTML elements. The inner input element has the focus, but you can only see the right side of the focus ring, because the other sides are clipped to the containing element. Even if you tweaked the CSS to make the whole ring visible, the ring should not be around the input, but around the whole control (including the border and button).
To help developers address this issue, Wijmo components have a “wj-state-focus” pseudo-selector that allows you to identify the focused component. This selector allows you to write rules like this:
.wj-state-focus, a:focus, button:not(.wj-btn-default):focus, input:not(.wj-form-control):focus, input[type=checkbox]:focus { box-shadow: 0 0 5px 3px rgba(90, 160, 215, .5); }
This rule changes the focus indicator to highlight the whole component (it also applies the focus indicator to a few other elements which are not Wijmo components):
The “wj-state-focus” selector also works in scenarios with nested components. The image below shows a “ColumnFilterEditor” component which contains a focused “MultiSelect” component:
Learning more about web accessibility
Perfect accessibility is a moving target. Standards evolve, as do browsers, assistive tools, and components. To check the accessibility of the pages you create, use Chrome’s accessibility audit tool. It’s easy to use, comprehensive, and constantly getting better. If you have any suggestions or requests that would help us improve accessibility on our components, please let us know in the comments below.
For a complete introduction to all aspects of accessibility, including those related to application development, we strongly recommend Udacity’s Accessibility course. It presents a great overview of accessibility and has a nice balance between breadth and depth, emphasizing key points and explaining the reasoning behind several common design patterns.
To learn more about web components, take a look at two of our previous articles: Web Components - An Introduction and Practical Usage and Web Components Introduction: Creating Custom HTML Elements in 2018.