Angular ESLint Rules for Accessible HTML Content

Sandi Barr - Jan 10 '23 - - Dev Community

Content accessibility for built-in HTML elements is the third and final category in this series on Angular ESLint accessibility rules. These rules validate several HTML attributes that developers commonly overlook regarding accessibility. They check for alt text on images, accessible content in buttons, links, and headings, and proper form labels and table header associations. Angular ESLint accessibility rules provide immediate guidance on accessibility best practices right in the code, resulting in more accessible and user-friendly Angular applications.

The Rules

Previous articles in this series discussed how to add Angular ESLint and configure these rules in .eslintrc.json config files, so let's get straight into the rules:


Rule: Alt Text

@angular-eslint/template/accessibility-alt-text

The accessibility-alt-text rule validates that all images have alternative text. Images must have an alt attribute to meet WCAG 2.1 Success Criterion 1.1.1: Non-text Content that states all non-text content should have a text alternative that serves the equivalent purpose.

Follow these guidelines for providing meaningful and concise alt text:

  • Alt text should express the relevant detail in an image to stand in for the same purpose, meaning, and intent.
  • Alt text should not be redundant or repeat information from the image caption.
  • Alt text should be short and to the point, ideally 150 characters or less.

When there is no alt attribute on an <img>, some screen readers will announce the image src instead. Including an empty alt attribute for decorative images indicates to screen readers that these images do not convey additional meaning or information:



<img alt="" src="decorative.gif">


Enter fullscreen mode Exit fullscreen mode

In addition to checking for the alt attribute on <img> elements, the accessibility-alt-text rule also validates <input type="image">, <area>, and <object> elements, which support these attributes as alternative text:

HTML element alternative text attributes
<img> alt
<input type="image"> alt, aria-label, aria-labelledby
<area> alt, aria-label, aria-labelledby
<object> aria-label, aria-labelledby, title

Rule: Elements Have Content

@angular-eslint/template/accessibility-elements-content

The accessibility-elements-content rule ensures that <a>, <button>, and <h1> - <h6> elements have content. Screen readers announce these elements by their role and name (and level for headings.) Links, buttons, and headings are semantic elements with inherent meaning and implicit roles, and the accessible name for these elements comes from their content, title, or aria-label.

A button, link, or heading name may also come from a child element with plain text content, a title, or aria-label. Don't add redundant and unnecessary aria-labels where the accessible name comes from a child element. Use the Accessibility pane in Chrome DevTools to inspect an element's computed name:

Accessibility pane in Chrome DevTools showing button name computed from SVG title


Rule: Label Has Associated Control

@angular-eslint/template/accessibility-label-has-associated-control

The HTML <label> element gives an accessible name to form controls. Labels are important for helping users understand the purpose of <input>, <select>, <textarea>, <output>, <meter>, and <progress> elements.

  • A control can have more than one <label>.
  • A <label> cannot be associated with more than one control.
  • Do not use <label> independently without an associated control.

A <label> is associated with a form control with either an implicit or explicit association, and both styles are widely supported. Nesting a control inside <label> creates an implicit association between the label and control:



<label>
  <input type="checkbox">
  Blue
</label>


Enter fullscreen mode Exit fullscreen mode

Whereas, assigning an id to the control and using a matching for attribute on the <label> creates an explicit association:



<label for="city">City</label>
<input id="city" type="text">


Enter fullscreen mode Exit fullscreen mode

The accessibility-label-has-associated-control rule determines if a label has an implicit association with a control nested inside the label or if the label has the for attribute. The rule does not look elsewhere in the template for a control with an id matching the label's for attribute and does not validate explicit label and control pairings. This is a natural limitation of static code analysis tools like Angular ESLint, so you’ll want to use additional testing techniques to validate the compiled application. The for attribute could be a bound value, and Angular ESLint template rules do not validate bound values. Rules are generally limited to validating individual template nodes and their children, and the control could even be in another component's template separate from the label.

Configuration Options for Label Has Associated Control

The accessibility-label-has-associated-control rule supports the configuration options labelComponents and controlComponents. The labelComponents option adds validation for custom label components. The controlComponents option configures the rule to recognize an implicit association on custom controls nested inside a label.

Option: Custom Label Components

Configure "labelComponents" for "my-custom-label" with "forControl":



"@angular-eslint/template/accessibility-label-has-associated-control": [
  "error",
  {
    "labelComponents": [
      {
        "selector": "my-custom-label",
        "inputs": ["forControl"]
      }
    ]
  }
]


Enter fullscreen mode Exit fullscreen mode

Validation on a custom label component to either require the forControl input or to have a nested control:



<my-custom-label forControl="myId">
  Custom
</my-custom-label>
<input id="myId" type="text"/>

<my-custom-label>
  Custom
  <input type="text"/>
</my-custom-label>


Enter fullscreen mode Exit fullscreen mode

Option: Custom Control Components

Configure "controlComponents" for "my-custom-control":



"@angular-eslint/template/accessibility-label-has-associated-control": [
  "error",
  {
    "controlComponents": ["my-custom-control"]
  }
]


Enter fullscreen mode Exit fullscreen mode

Validation on a custom control nested inside a label with an implicit association:



<label>
  Custom
  <my-custom-control></my-custom-control>
</label>


Enter fullscreen mode Exit fullscreen mode

Rule: Table Scope

@angular-eslint/template/accessibility-table-scope

The accessibility-table-scope rule validates the scope attribute for table headers. The scope attribute specifies which cells belong with a table header (<th>.) Table header scope accepts the following values:

  • row: associates a table header with all the cells in that row.
  • col: associates a table header with all the cells in that column.
  • rowgroup: associates a table header that spans multiple rows with all the cells in that row group.
  • colgroup: associates a table header that spans multiple columns with all the cells in that column group.

When table header scope is not specified, browsers and assistive technologies infer the relationship between table headers and their cells. Tables with headers in a single row or column do not need the scope attribute. More complex tables with irregular, multi-level, or both row and column headers should explicitly define which cells belong to which headers by using the scope attribute on their table headers or the id attribute on table headers with the headers attribute on table cells.


Rule: No Distracting Elements

@angular-eslint/template/no-distracting-elements

The no-distracting-elements rule disallows usage of the <blink> and <marquee> elements, which are both deprecated and no longer recommended for use.

The <blink> element is more than distracting. Flashing content can trigger seizures in people with photosensitive epilepsy. WCAG 2.1 Success Criterion 2.3.1 dictates that there are no more than three flashes in a one second period or that the flash is below the threshold.

The scrolling content in a <marquee> element can create barriers for anyone who struggles with moving objects or people with cognitive disabilities like attention deficit disorder. WCAG 2.1 Success Criterion 2.2.2 states that users must be able to stop or hide any moving, blinking, scrolling, or auto-updating information.


Bonus Rule: Button Has Type

@angular-eslint/template/button-has-type

I mention this last rule as a bonus because it is not specifically an accessibility rule but is commonly missed and can lead to surprising functionality. The button-has-type rule checks for the type attribute on HTML <button> elements. A <button> inside a <form> without a type acts as a submit button and submits the form when pressed:



<form>
  <label><input type="checkbox">Yes</label>

  <button>Submits the Form</button>

  <button type="button">Not a Submit</button>

  <button type="reset">Resets the Form</button>

  <button type="submit">Submits the Form</button>
</form>


Enter fullscreen mode Exit fullscreen mode

That's All, Folks!

The Angular ESLint rules covered in this series enable developers to create more accessible and user-friendly Angular applications by helping to ensure keyboard accessibility, ARIA compliance, and accessible HTML content. The first article discusses rules for ensuring that all interactive elements are reachable with a keyboard and that focus is not improperly managed. The second article discusses Angular ESLint rules to check that ARIA roles have the required attributes and that ARIA attributes are valid. This third and final article discusses Angular ESLint rules to ensure accessible HTML content, such as image alt text, accessible names for form controls, buttons, links, and headings, and table heading scope. That covers all the rules pertaining to accessibility available with Angular ESLint.

Static code analysis with Angular ESLint has the advantage of identifying issues early in development, but performing further automated and manual browser-based testing is essential for ensuring full accessibility. These Angular ESLint accessibility rules inspect individual nodes and elements used in the template code. It is also important to validate accessibility within the context of the entire application to check that headings are properly nested, that there are no nested interactive controls, that landmarks like <header>, <aside>, <nav>, and <main> are used and labeled correctly, etc. Incorporating accessibility testing into each stage of the development and release process ensures that our Angular applications are usable by a wide range of users, including those with disabilities.

Recommended Testing Tools and Libraries:

References:

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .