Skip to content

feat(mat-tree): Support for semantically correct links in <mat-tree> for page navigation #31570

@bilgino

Description

@bilgino

Feature Description

I am using the Angular Material <mat-tree> component for the (primary) site navigation menu in our web application. However, this creates problems regarding WCAG compliance and accessibility.

According to the ARIA Authoring Practices Guide (APG), tree structures can be used for page navigation as demonstrated in the Treeview Navigation Example. The guidelines specify that:

  1. aria-current="page" should be used (instead of aria-selected) to indicate the current page
  2. Navigation should be implemented using <a> tags

Angular Material however enforces this default role structure:
mat-tree → role="tree"
mat-nested-tree-node → role="treeitem"

...there is an issue when trying to use semantically correct links (<a> tags) within <mat-nested-tree-node> – for example, with aria-current="page".

When we intend to use the <mat-tree> component as a recursive tree-based site navigation, the question becomes how to correctly integrate and semantically mark up an <a> tag. Ideally, the <a> tag itself should receive the role="treeitem" and the aria-current="page" attribute when it represents the current page.

Currently, it's unclear to us how the <mat-tree> component can be extended to meet this specific requirement for web links.

Removing the <a> tag and replacing it with manual navigation (this.router.navigate()) is not an option due to synchronization problems etc.

The following code snippet shows our current approach:

<mat-tree [dataSource]="treeDataSource" [treeControl]="treeControl" class="nested-tree">
  <mat-nested-tree-node [attr.aria-current]="linkActive.isActive ? 'page' : null"
    *matTreeNodeDef="let item; when: hasChild">
    <div
      class="mat-tree-node"
      matTreeNodeToggle
      routerLinkActive="menu-nav--highlighted"
      [routerLinkActiveOptions]="{ exact: true }"
      #linkActive="routerLinkActive">
        <button mat-mini-fab color="light" [attr.aria-label]="'toggle ' + item.title" tabindex="-1">
          <mat-icon [svgIcon]="treeControl.isExpanded(item) ? 'arrow-down-20' : 'arrow-right-20'">
          </mat-icon>
        </button>
      <a
        class="menu-nav__link tree-node-link"
        [routerLink]="item.route"
        tabindex="-1"
        [attr.aria-label]="item.title"
        [attr.title]="item.title"
        rel="noopener noreferrer"
        (click)="onNodeToggle($event, item)">
        {{ item.title }}
      </a>
    </div>
    <div [ngClass]="{ hidden: !treeControl.isExpanded(item) }" role="group">
      <ng-container matTreeNodeOutlet></ng-container>
    </div>
  </mat-nested-tree-node>
</mat-tree>

I would appreciate an official way or best practice to semantically implement accessible links with aria-current inside the mat-tree pattern.

Thank you in advance for any feedback or proposed solutions!

Use Case

A way to configure mat-tree so that:

  1. Links are correctly identified as treeitem
  2. aria-current="page" can be set for the current page
  3. Navigation works optimally both via keyboard and screen reader

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: cdk/treefeatureThis issue represents a new feature or feature request rather than a bug or bug fix

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions