Quantcast
Viewing latest article 10
Browse Latest Browse All 32

ARIA roles and attributes in Angular

  1. Web Accessibility (A11y) in Angular – Introduction
  2. Accessibility Testing Tools for Angular
  3. Accessible Angular Routes
  4. ARIA roles and attributes in Angular

ARIA (short for Accessible Rich Internet Applications) roles and attributes are used to improve Accessibility of our Angular apps – especially for users who rely on assistive technologies like screen readers, voice control, or alternative input devices. Using ARIA is essential for building inclusive, user-friendly web apps.

About ARIA

ARIA was developed by the World Wide Web Consortium’s Web Accessibility Initiative (WAI) to enhance the accessibility of dynamic web content. The WAI also is the organization behind the Web Content Accessibility Guidelines (WCAG), which provide a comprehensive framework for A11y – more about WCAG in the intro of this A11y blog series.

Image may be NSFW.
Clik here to view.
WAI

Originating in the early 2000s, ARIA was created to bridge the gaps in native HTML, ensuring that modern, interactive and single-page applications are usable by people with disabilities Image may be NSFW.
Clik here to view.
♿

ARIA in Angular

In Angular, ARIA roles and attributes can be easily integrated into your components. You can use them directly in your HTML view templates, just like any other HTML attribute. Angular also provides built-in support for ARIA through directives and bindings, making it easier to manage ARIA properties dynamically.

When using static values, you can simply add them to your HTML elements or components:

<!-- Static ARIA attributes require no extra -->
<button type="button" aria-label="Close">X</button>

However, when you want to bind ARIA attributes dynamically, you should use Angular's property binding syntax ("[]" square brackets) including the "attr." prefix:

<!-- Dynamic ARIA attribute property binding with "attr." prefix -->
<button type="button" [attr.aria-label]="myActionLabel">…</button>

ARIA vs. Semantic HTML

While ARIA is a powerful tool for enhancing accessibility, it should be used as a last resort. Native semantic HTML elements and attributes are preferred whenever possible, as they provide built-in accessibility features that ARIA does not have to replicate. Always prefer native elements like <button>, <label>, <nav> and <header> over their ARIA counterparts. For example, instead of using role="button" on a <div>, use a <button> element.

ARIA Roles

Speaking about ARIA roles, they define what an HTML element is or how it should behave – when native HTML elements are not applicable. They tell assistive technologies how to interpret an element and its purpose within the page. Roles are particularly useful for custom components that don’t have native semantic meaning.

Most commonly used ARIA roles

Here is a not exhaustive list of some ot the most commonly used ARIA roles:

  • role="button" (prefer <button>) indicates an interactive element that triggers an action.
  • role="main" (prefer <main>) the main content of your app – typically <router-outlet />.
  • role="complementary" (prefer <aside>) denotes content that complements – like a sidebar.
  • role="navigation" (prefer <nav>) denotes a section of navigation links – like your route nav.
  • role="alert" used for important messages that should be immediately announced.
  • role="dialog" signifies a modal or popup window.
  • role="listbox" used for a widget that allows the user to select from a list of options.
  • role="tablist" composes a tabbed interface.
  • role="tab" represents a selectable tab in a tabbed interface.
  • role="tabpanel" the container for the content associated with a tab.

Find a comprehensive list of all ARIA roles by MDN.

Examples

This could be a <app-dialog> component:

<app-dialog role="dialog" aria-labelledby="dialogTitle" aria-modal="true" cdkFocusTrap>
  <h2 id="dialogTitle">{{ dialogTitle() }}</h2>
  <p>{{ dialogContent() }}</p>
  <button type="button" (click)="onClose()">Close</button>
</app-dialog>

In this simple example, the role="dialog" indicates that this is a dialog window. The aria-labelledby attribute associates the dialog with its title, and aria-modal="true" indicates that the dialog is modal, meaning it prevents interaction with the rest of the page until closed. Note that we also use the cdkFocusTrap directive to ensure that (keyboard navigation) focus is trapped within the dialog while it is open.

Another example could be a tab interface:

<app-tablist role="tablist">
  @for (tab of tabs(); track tab.id) {
    <button role="tab" aria-controls="panel_{{ tab.id }}" id="tab_{{ tab.id }}"
            [attr.aria-selected]="activeTab() === tab.id ? 'true' : 'false'">
      {{ tab.label }}
    </button>
  } @empty {
    No tabs available.
  }
</app-tablist>
@for (tab of tabs(); track tab.id) {
  <app-tab role="tabpanel" aria-labelledby="tab_{{ tab.id }}" id="panel_{{ tab.id }}">
    {{ tab.content }}
  </app-tab>
}

In this example, we have a tabbed interface. The role="tablist" indicates that this is a list of tabs. Each tab has the role="tab" and is associated with its corresponding panel using aria-controls. The aria-selected attribute indicates which tab is currently selected. The panels have the role="tabpanel" and are associated with their respective tabs using aria-labelledby.

ARIA Attributes

ARIA attributes provide additional details about an HTML element’s state, properties or relationships. They enhance the semantic meaning of your Angular components and other HTML elements, especially when default HTML does not fully describe an element’s behavior.

We distinguish between states and properties. States are dynamic and can change over time, while properties are static and describe the element's characteristics.

Most commonly used ARIA attributes

Widget attributes (states and properties)

  • aria-disabled (state/property): indicates whether an element is disabled or not – not necessary if native disabled is used.
  • aria-required (property): indicates that user input is required before submitting a form – not necessary if native required is used.
  • aria-expanded (state): communicates whether an element, such as a collapsible menu, is expanded or collapsed.
  • aria-hidden (state): indicates whether an element should be exposed to assistive technologies.
  • aria-invalid (state): indicates whether the value of an input field is valid or invalid.

Live region attributes (states)

  • aria-live: specifies how updates to content should be announced to the user (e.g., assertive for error messages).
  • aria-busy: indicates whether an element is currently being updated (e.g., loading spinner).

Drag-and-Drop attributes (states)

  • aria-grabbed: indicates whether an element is currently being dragged (e.g., true when dragging).

Relationship attributes (properties)

  • aria-label: provides a text alternative for elements that may not have visible text.
  • aria-labelledby: identifies an element (or elements) that labels the current element.
  • aria-describedby: identifies an element (or elements) that describes the current element.
  • aria-controls (state): references the element(s) whose content is controlled by the current element.

And here is again the full list of all ARIA attributes by MDN.

Examples

A toggle button using aria-label and aria-expanded:

<button
  type="button"
  aria-label="Toggle navigation menu"
  [attr.aria-expanded]="isMenuOpen() ? 'true' : 'false'"
  (click)="onToggleNav()">
  <i class="icon-menu"></i>
</button>

In this simple example, the aria-label attribute provides a text alternative for the button, indicating its purpose. The aria-expanded attribute indicates whether the navigation menu is currently open or closed. This is important for screen reader users, as it helps them understand the state of the button.

Using aria-live combined with role="alert" for an error message:

@let showFromErrors =
  flightSearchForm.controls.from.errors &&
  flightSearchForm.controls.from.touched;

<input
  [...]
  id="fromAirport"
  [attr.aria-invalid]="!!showFromErrors"
  [attr.aria-describedby]="showFromErrors ? 'fromAirportErrors' : null"
/>
@if (showFromErrors) {
  <app-flight-validation-errors
    aria-live="assertive"
    role="alert"
    id="fromAirportErrors"
    fieldLabel="From"
    [errors]="flightSearchForm.controls.from.errors"
  />
}

In this example, the aria-live attribute is set to assertive, which means that the screen reader will announce the error message immediately when it appears. The role="alert" indicates that this is an important message. Additionally, the aria-invalid attribute is used to indicate that the input field has validation errors and the aria-describedby attribute is used to associate the error message with the input field. Note that the aria-describedby attribute is set to null when there are no errors. This ensures that it's not present in the rendered DOM when not needed.

Accessibility Workshop

For those looking to deepen their Angular expertise, we offer a range of workshops – both in English and German:

Conclusion

Implementing ARIA in Angular is essential for building web apps that are both accessible and inclusive. By leveraging ARIA roles and attributes appropriately, developers can bridge gaps where native HTML falls short, ensuring that all users have a positive experience. This approach not only meets accessibility standards but also lays the foundation for user-centric and future-proof design. For more information on Angular and accessibility, check out my Ally blog series.

This blog post was written by Alexander Thalhammer. Follow me on Linkedin, X or giThub.

References

The post ARIA roles and attributes in Angular appeared first on ANGULARarchitects.


Viewing latest article 10
Browse Latest Browse All 32

Trending Articles