Linting Custom Components with the axe Accessibility Linter for VS Code
A walkthrough for linting custom components in VS Code
This article shows how to configure the axe Accessibility Linter extension for Visual Studio Code (VS Code) to find accessibility errors in your custom components.
This article is for users of the axe Accessibility Linter extension for VS Code. If you are a user of the axe DevTools Linter REST endpoint, see Linting Custom Components with the REST Endpoint instead.
If you would like to read an overview of linting custom components, see Linting Custom Components.
To use this walkthrough, you should have the following installed:
When you use the extension to lint source code, any accessibility errors are shown in VS Code with a red wavy underline. For example, the following HTML shows the use of the img element without an alt attribute, which is an accessibility error.
(This is an oversimplified example for demonstrating linting rather than an actual real-world example.)
The extension highlights the line in error and provides a tooltip when you hover your mouse cursor over the error. Because this img element doesn't have an alt attribute, you'll get an accessibility error from the extension in VS Code:
For this example, a developer created a custom component called custom-image. The following sample shows example usage of the custom-image custom component:
For this example, the custom-image component creates an img element with a path attribute (which is mapped to a src attribute by the custom control's implementation). The extension shows no error because the extension has no mapping between between custom-image and img even though the output img element is missing an alt attribute:
If you provide a mapping between custom-image to img, axe DevTools Linter can map your custom component as a standard HTML element and locate accessibility errors. You can specify the mapping by using the global-components configuration option in an axe-linter.yml configuration file:
The extension now highlights the accessibility error and provides a tooltip when you hover your cursor over the error:
You can also indicate the same mapping as above with either of these syntaxes:
Or, alternatively, by abbreviating element as el:
When you use an element mapping, all attributes from the custom component are copied to the emitted element, and that emitted element is linted.
You can add an alt attribute to your custom-image to fix the accessibility problem:
<custom-image path="images/image.jpg" alt="alt text"></custom-image>
There is no longer an error so VS Code doesn't display the red wavy undeline:
If your custom image component instead uses a different attribute to indicate alternative text, you can specify that attribute in the configuration. For instance, suppose your custom-image component uses an alternative-text attribute instead of alt, as shown below:
<custom-image path="images/image.jpg" alternative-text="alt text"></custom-image>
In this case, you could specify a mapping between the alternative-text attribute and the alt attribute as shown with the attributes array in an axe-linter.yml file as shown below:
- alternative-text: alt
This global-components configuration is slightly different from the earlier mapping of one custom component to one HTML element. With only elements, you use a mapping from a key (custom-image) to a value (img). With the inclusion of the attributes array, you are now required to use the element (or el) property to specify the emitted HTML element.
This change fixes the error, and there is no red wavy underline shown in VS Code.
Because you specified the attributes array in the configuration, when the extension maps from custom-image to img, only the attributes matching those in the attributes array are copied to the emitted HTML element.
You can also abbreviate attributes as attrs:
- alternative-text: alt
Suppose you use a custom-button component as follows:
<custom-button aria-controls="expand-region" aria-expanded="false" aria-colindex="1" message="Show Region"></custom-button>
There are two problems with this usage:
- If you map this custom-button component directly to a button element, there will be no text content to be displayed on the button. The component author's intent, however, is that the message attribute should be used as text content: <button> value of the message attribute </button>
- The emitted button element has an implicit role of button so the aria-colindex attribute is incorrect and should be removed.
As is the default, this HTML will not result in an error because there is no mapping between custom-button and button. However, if you create a simple mapping between custom-button and button as shown below:
You will receive two errors from VS Code:
To address the first problem (text content for the button element coming from a message attribute, identified above as button-name in the VS Code tooltip), you can use the special <text> value which maps an attribute to the emitted element's text content. In this case the message attribute's text should be copied to the emitted button element's text content.
To configure the extension that the message attribute should be considered as text content for the HTML button element, you can use the special <text> value in an axe-linter.yml configuration file:
- message: <text>
Because you defined the message attribute as <text>, you told the extension to consider that attribute as replacing textual content of the HTML button element with the value of the message attribute.
Unfortunately, by using the attributes array, the only attribute that was passed through to the emitted button element was only the message attribute; any attributes not in the attributes array are not passed through. This means that the incorrect aria-colindex was not caught by the extension.
You can use the special aria-* value to pass all the ARIA attributes, as shown below:
- message: <text>
This error occurs because the button element has an implicit role="button", using aria-colindex is invalid with buttons, and all ARIA attributes are copied to the emitted element due to the aria-* attribute value.
If you'd used only element mapping (where the mapping does not use the attributes array), all attributes would be, by default, copied to the button element. The configuration for this case was shown earlier:
Along with the error shown in VS Code:
The above example shows that a practical first step when beginning to lint custom components would be to start with an element mapping (thereby copying all attributes to the emitted, standard HTML element) and then seeing what attributes need to be added to the configuration:
- Whether any of the custom component's attributes should be mapped to different attributes.
- Whether you need to use <text> or aria-*.
Default attributes let you set values for attributes in your configuration file rather than map one attribute to another. For example, the following sample configuration shows a custom-menu component mapped to an li element with a role of menu:
Because the role attribute has a default value of menu, set in the configuration file, users do not need to specify a role attribute when they use the custom-menu component in their code. The implication is that your custom component's implementation creates these attributes on the output element and sets their values rather than requiring users to set them when they use your component.
Optionally, the name value is set to null in the configuration, which causes axe DevTools Linter to ignore any role attributes that users have specified on custom-menu in linted code.
The value specified with default can be a string, number, or boolean and will be converted to a string in the output attribute.