Sandi Barr
Posted on January 10, 2023
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:
-
@angular-eslint/template/accessibility-alt-text
-
@angular-eslint/template/accessibility-elements-content
-
@angular-eslint/template/accessibility-label-has-associated-control
-
@angular-eslint/template/accessibility-table-scope
-
@angular-eslint/template/no-distracting-elements
-
@angular-eslint/template/button-has-type
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">
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:
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>
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">
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"]
}
]
}
]
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>
Option: Custom Control Components
Configure "controlComponents"
for "my-custom-control"
:
"@angular-eslint/template/accessibility-label-has-associated-control": [
"error",
{
"controlComponents": ["my-custom-control"]
}
]
Validation on a custom control nested inside a label with an implicit association:
<label>
Custom
<my-custom-control></my-custom-control>
</label>
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>
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:
- Lighthouse in Chrome DevTools
- Accessibility pane in Chrome DevTools
- WebAIM Wave Browser Extension
- Axe DevTools Browser Extension
- Angular Testing Library
- AxePuppeteer for Puppeteer
- Cypress-axe library for Cypress
References:
- Understanding SC 1.1.1:Non-text Content (Level A) on W3C's Understanding WCAG 2.1
- Decorative Images on W3C Web Accessibility Initiative
- Providing Accessible Names and Description on W3C Web Accessibility Initiative ARIA Authoring Practices Guide
- Tables Tutorial on W3C Web Accessibility Initiative
- Understanding SC 2.3.1:Three Flashes or Below Threshold (Level A) on W3C's Understanding WCAG 2.1
- Understanding SC 2.2.2:Pause, Stop, Hide (Level A) on W3C's Understanding WCAG 2.1
Posted on January 10, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.