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 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, 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:
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 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
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 enabled in VS Code:
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 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 (Accessible Rich Internet Applications)
References:
- Keyboard Accessibility on WebAIM
- Assistive Technologies – The Switch on Axess Lab
- WCAG Success Criterion 2.4.3 Focus Order on W3C's Understanding WCAG 2.1
- SCR2: Using redundant keyboard and mouse event handlers on W3C's Techniques for WCAG 2.1
- Links vs. Buttons in Modern Web Applications on MarcySutton.com