Angular ESLint Rules for ARIA

Sandi Barr - Nov 14 '22 - - Dev Community

The acronym ARIA stands for Accessible Rich Internet Applications. The purpose of ARIA is to make web content more accessible for people with disabilities. ARIA can enhance accessibility in custom-designed user interface controls and components, but incorrect use of ARIA can hinder accessibility and create a worse experience than no ARIA at all. Standard, semantic HTML components don't need ARIA to be accessible. As the saying goes, no ARIA is better than bad ARIA.

Advanced use of ARIA is a specific discipline that should be encapsulated into custom UI components that are used to create a consistent experience across an application. Creating such a reusable component library is a common practice in Angular development. Angular ESLint has rules for checking ARIA, but Angular does not add this validation out of the box. This post walks you through how to add Angular ESLint to a component library project, then dives deeper into the ARIA rules. I'll cover what each rule looks for in the Angular template code and how it uses the underlying libraries to validate ARIA roles and attributes.

Add Angular ESLint to an Angular Library Project

In the previous post I covered how to add Angular ESLint to an Angular project. This post shows how to create an Angular library project with Angular ESLint support.

To generate a new Angular workspace run the ng new schematic without creating the default application:



ng new my-workspace --create-application false
cd my-workspace


Enter fullscreen mode Exit fullscreen mode

Add Angular ESLint to the workspace with the ng add schematic:



ng add @angular-eslint/schematics  


Enter fullscreen mode Exit fullscreen mode

Generate an Angular library using Angular ESLint's library schematic that runs the Angular library schematic under the hood then adds support for Angular ESLint:



ng generate @angular-eslint/schematics:lib


Enter fullscreen mode Exit fullscreen mode

The workspace will have both root and project level eslint configuration. The project .eslintrc.json files extend from the root level config, so rules and their options can be set at the global level and added or overridden at the project level. Configure the ARIA template rules under "*.html" overrides:

code editor showing eslint configuration


Root and project level .eslintrc.json config



Next let's take a deeper look at the two ARIA rules from Angular ESLint that are configured in the .eslintrc.json above:

Angular ESLint ARIA Rules

The Angular ESLint ARIA rules run against Angular template code both inline and in separate template files and identify incorrect usage of WAI-ARIA roles and their associated ARIA attributes. Ensuring WAI-ARIA criteria are met enables user agents and assistive technologies to gather information about and interact with web content and controls.

These rules originated from the eslint-plugin-jsx-a11y project and were ported over and implemented for Angular ESLint. Angular ESLint and eslint-plugin-jsx-a11y both use the aria-query and axobject-query libraries to query information about ARIA roles and attributes.

Rule: Valid ARIA

@angular-eslint/template/accessibility-valid-aria

The accessibility-valid-aria rule checks aria-* attributes to be valid ARIA. The rule looks for HTML elements with bound or plain text ARIA attributes. Then it looks up the ARIA property definition from the aria-query library to get the ARIA attribute value type and allowed values.

If aria-query doesn’t have a definition for the matched ARIA attribute, the rule reports an invalid attribute value violation with the suggested fix to remove it:

code editor showing eslint violation


Violation of rule @angular-eslint/template/accessibility-valid-aria: The aria-control is an invalid ARIA attribute

If the ARIA attribute value is a literal value, it checks the value to be of the defined value type and one of the allowed values if provided. If the ARIA attribute value doesn’t match the ARIA property definition, the rule reports a violation that the attribute’s value is invalid:

code editor showing eslint violation


Violation of rule @angular-eslint/template/accessibility-valid-aria: The aria-valuenow has an invalid value

The template parser can only check the nodes and static values in the template itself, so a dynamically bound attribute value does not get flagged as a violation:

code editor with no eslint violations


No violation on bound attribute value


Rule: Role Has Required ARIA

@angular-eslint/template/accessibility-role-has-required-aria

Note: You must be on at least version 14.2.0 of Angular ESLint to use the accessibility-role-has-required-aria rule.

This rule matches HTML elements with a plain text role attribute and checks that the given role has the required ARIA attributes using the role definition from the aria-query library. For example, the switch role definition has one required property, aria-checked.

Using role mappings from the axobject-query library, it determines if the HTML element's semantic role matches the given role. The role="switch" can be applied to an <input type="checkbox"/> and the required aria-checked property is covered by the element’s semantic role. A <span> element does not have those same semantic properties and would be flagged as a violation if given the role="switch" without aria-checked:

code editor showing eslint violation


Violation of rule @angular-eslint/template/accessibility-role-has-required-aria: The span with role="switch" does not have required ARIA properties: aria-checked

The complete set of roles with required ARIA properties covered by the accessibility-role-has-required-aria rule is as follows:

WAI-ARIA role Required ARIA
checkbox aria-checked
menuitemcheckbox aria-checked
menuitemradio aria-checked
radio aria-checked
switch aria-checked
heading aria-level
option aria-selected
treeitem aria-selected
combobox aria-controls
aria-expanded
meter aria-valuenow
slider aria-valuenow
scrollbar aria-controls
aria-valuenow

Suggestion for Adoption

Enabling Angular ESLint's accessibility rules all at once in a codebase with existing accessibility issues can produce a long list of violations that might initially seem overwhelming and difficult to prioritize. Enable these sets of rules incrementally to tackle one category at a time:

  1. Keyboard Accessibility
    All content and functionality must be usable with the keyboard. Keyboard access is essential for assistive technologies and should be a top priority. The article in this series on Angular ESLint Rules for Keyboard Accessibility covers the lint rules that can help. You won't be able to automate validation of all keyboard functionality with lint rules or automated testing. Be sure to incorporate manual interaction testing into your process to ensure a logical focus order is maintained along with a visible focus ring.

  2. ARIA Roles and Attributes
    Providing accessible components from a shared library helps standardize your approach to UI/UX and accessibility across your application(s). Isolate most ARIA to your custom UI components and use Angular ESLint's ARIA rules to validate best practices.

  3. Content and Relationships
    These rules ensure accessibility for non-text content and content relationships in forms and tables. Alternative text should convey the purpose and context for non-text content. Ensuring content accessibility also requires human input and manual review to make sure the content is meaningful and presented consistently. Stay tuned for my last article in this series about the Angular ESLint Accessibility Rules for Content and Relationships.


But Wait, There's More? There Sure Could Be

The eslint-plugin-jsx-a11y library, where these Angular ESLint ARIA rules originate, has even more rules for validating ARIA that could be borrowed from and added to Angular ESLint. These rule additions could also leverage aria-query and axobject-query to validate ARIA usage and encourage better practices with semantic HTML.

Even More ARIA Rules in eslint-plugin-jsx-a11y

I would also like to implement these rules for Angular ESLint:

  • aria-role

    Elements with ARIA roles must use a valid, non-abstract ARIA role.
    Valid ARIA roles must be used in order for user agents to understand and interact with those elements according to specifications. Abstract roles are the foundational roles upon which other WAI-ARIA roles are built and organized and are not to be used directly.

  • role-supports-aria-props

    Enforce that elements with explicit or implicit roles defined contain only aria-* properties supported by that role.
    It does no good to add ARIA attributes to elements with roles that don't support those attributes.

  • no-aria-hidden-on-focusable

    Enforce that aria-hidden="true" is not set on focusable elements.
    Using aria-hidden hides an element and its children from the accessibility API. Elements that aren't accessible to assistive technologies should not be able to receive focus via keyboard.

  • no-interactive-element-to-noninteractive-role

    ARIA roles should not be used to convert an interactive element to a non-interactive element.
    This rule prevents interactive elements meant as controls from being assigned non-interactive roles meant for content or containers. A content role such as meter, for example, could not be assigned to an <input> or <button> element.

  • no-noninteractive-element-to-interactive-role

    ARIA roles should not be used to convert a non-interactive element to an interactive element.
    Elements meant for content or containers, such as <main>, <h1>, and <li>, should not be assigned interactive roles meant for controls such as the button or checkbox roles.

  • no-redundant-roles

    ARIA roles should not redundantly match an element's default/implicit role already set by the browser.
    An <input type="checkbox"> element already has the implicit role checkbox, so adding role="checkbox" would be redundant.

  • prefer-tag-over-role

    Enforces using semantic DOM elements over the ARIA role property.
    This rule prevents content or container elements like <span> or <div> from being assigned roles like banner or heading where a built-in, semantic HTML element is preferable.

🎉 Shout out in the comments if you think having these rules in Angular ESLint would improve accessibility practices in your Angular codebase!




References:
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .