Angular ESLint Rules for Keyboard Accessibility
Tags: angular, a11y, lint, tooling
Published: 2022-10-14 13:20:22

When using built-in HTML elements, the browser provides proper and expected keyboard support out of the box. Users can tab to a form input, use arrow keys to navigate through options in a select, or press a button with the Spacebar or Enter key. The recommended practice is to use semantic HTML elements and rely on the browser's built-in affordances for keyboard accessibility. Still, this advice is frequently not followed in favor of custom designed elements. Instead of using a button, developers sometimes add a click event to a span or div without realizing their implementation is not accessible to keyboard users. Ensuring accessible keyboard navigation also enables users of assistive technologies like [switch devices](https://axesslab.com/switches/) to interact with your application. Angular ESLint's accessibility rules can catch some of these common keyboard navigation pitfalls and provide helpful and immediate feedback on Angular template code both inline and in separate template files.

## How to Get Angular ESLint
If you're not already using [Angular ESLint](https://github.com/angular-eslint/angular-eslint), you can add it to an Angular project by running the schematic:
```
ng add @angular-eslint/schematics
```

The schematic adds a target to angular.json that enables the lint command on the project:
```
ng lint
```

Configure the rules in the  .eslintrc.json file under "*.html" overrides:
<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/70cyjdao7vnk4db3g0pc.png">
  <figcaption>ESLint template rule configuration in .eslintrc.json</figcaption>
</figure>

<hr>

Read on to learn more about the accessibility guidelines and best practices behind each of these rules.

## Rules for Accessible Keyboard Navigation

### No Positive Tab Index
#### `@angular-eslint/template/no-positive-tabindex`
[WCAG Success Criterion 2.4.3 Focus Order](https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html) dictates that elements on the page should receive focus in a sequential order that is meaningful to the content. The tab order should flow through the page from the header and navigation, through the main content, end at the footer, and circle back around. Built-in HTML elements that support interactive behaviors can be navigated to and receive focus by tabbing through the page.

Generally, the `tabindex` attribute isn't needed for buttons, links, and form controls. Assigning tabindex adds focus support to an element that can't otherwise receive focus. A tabindex greater than zero inserts elements into the focus order before elements without a tabindex or a tabindex of zero. A tabindex of -1 is a particular case that takes elements out of the focus order but allows them to receive focus programmatically. Enable the `no-positive-tabindex` rule to prevent usage of tabindex values other than 0 or -1.

### No Autofocus
#### `@angular-eslint/template/no-autofocus`
The `no-autofocus` rule discourages use of the `autofocus` attribute. Using autofocus can cause screen readers to jump to a control without context and break the expected tab navigation flow.

### Mouse Events Have Key Events
#### `@angular-eslint/template/mouse-events-have-key-events`
All content must be accessible with the keyboard alone. For example, if mousing over an element reveals additional content, there should be consideration for keyboard users who may navigate via tab to focus the trigger. The `mouse-events-have-key-events` rule ensures that elements with `(mouseover)` or `(mouseout)` event handlers also have corresponding keyboard support with `(focus)` or `(blur)`. Enabling this rule assists with following the WCAG technique [SCR2: Using redundant keyboard and mouse event handlers](https://www.w3.org/TR/WCAG20-TECHS/SCR2.html)

### Click Events Have Key Events
#### `@angular-eslint/template/click-events-have-key-events`
Wherever users can click, they should also be able to access via keyboard. The `click-events-have-key-events` rule ensures that elements with `(click)` event handlers also have at least one accompanying keyboard event, meaning `(keyup)`, `(keydown)`, `(keypress)`, or a key filtered variation like `(keyup.enter)`.

A `click-events-have-key-events` eslint rule violation appears when developers use a `(click)` event on an element that is not interactive by default and does not have built-in keyboard accessibility. For example, adding `(click)` to a `<span>` or a `<div>` to act like a button is not a button. A `<button>` does not need key events added because the browser triggers the click event with the Spacebar or Enter key.

Anchor elements (`<a>`) with `(click)` events that don't have an `href` or `routerLink` also violate this rule. Anchor links must include the `href` attribute for built-in accessibility and keyboard support. If an `<a>` has `(click)` and no `href`, should it instead be a `<button>`? Links and buttons are semantically different. Buttons are meant to do something within the view; whereas, links are for navigation to a different view or page (one exception being download links.)

Here's what this rule violation looks like with the [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) enabled in VS Code:
<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/21isoq6229xkupm43ype.png">
  <figcaption>Violation of rule @angular-eslint/template/click-events-have-key-events: click must be accompanied by either keyup, keydown or keypress event for accessibility.</figcaption>
</figure>

<hr>

Another thing to consider is that key event handlers on an element can only be triggered when that element has focus. When used on its own, the `click-events-have-key-events` rule could encourage developers to add a key event to elements that do not have native keyboard support and may not actually receive focus via tabbing. How do we confirm these custom elements we intend to work with both the mouse and keyboard can also receive focus?

_interactive-supports-focus enters the chat..._

### Accessibility Interactive Supports Focus
#### `@angular-eslint/template/accessibility-interactive-supports-focus`
_Note_: You must be on at least version [14.2.0](https://github.com/angular-eslint/angular-eslint/blob/main/CHANGELOG.md#1420-2022-11-15) of Angular ESLint to use the `accessibility-interactive-supports-focus` rule.

The intent of `accessibility-interactive-supports-focus` is to verify that HTML elements with interaction handlers (click, keydown, keyup, keypress) can receive focus either because the element is interactive by default (like a `<button>`, `<input>`, `<select>`, etc.) or by adding a tabindex to the element. When using tabindex, you'll also want to check your templates against that `no-positive-tabindex` rule mentioned previously. These keyboard navigation rules work in conjunction with each other to facilitate the development of accessible web applications.

## Part of a "Complete Breakfast"
Applying these rules with Angular ESLint can give developers immediate feedback on their template code and identify problems early in development. However, the lint checks are limited to the precompiled template nodes and static values. These template rules only apply to what's in the template, so they also cannot check Angular Component host properties or HostBindings for bound attributes. Browser-based testing tools that run against compiled application code do a more thorough evaluation by validating elements in relationship to each other and within the entire page. Use a combination of automated and manual accessibility testing alongside Angular ESLint for more thorough accessibility checks on your Angular applications.

Stay tuned for my next post on [Angular ESLint Rules for ARIA](https://dev.to/angular/angular-eslint-rules-for-aria-3ba1) (Accessible Rich Internet Applications)

**References:**
* [Keyboard Accessibility](https://webaim.org/techniques/keyboard/) on WebAIM
* [Assistive Technologies – The Switch](https://axesslab.com/switches/) on Axess Lab
* [WCAG Success Criterion 2.4.3 Focus Order](https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html) on W3C's Understanding WCAG 2.1
* [SCR2: Using redundant keyboard and mouse event handlers](https://www.w3.org/WAI/WCAG21/Techniques/client-side-script/SCR2) on W3C's Techniques for WCAG 2.1
* [Links vs. Buttons in Modern Web Applications](https://marcysutton.com/links-vs-buttons-in-modern-web-applications) on MarcySutton.com