Skip to content

[A11Y] [Medium] Tab navigation missing aria-selected state #1564

@continue

Description

@continue

Accessibility Issue: Tab Navigation Elements Need ARIA Labeling

WCAG Level: A
Severity: Medium
Category: ARIA Usage / Semantic HTML

Issue Description

The application uses custom tab navigation implemented with <div> elements that have role="tab". While the role is correctly applied, these tabs lack proper aria-selected state management and the tab lists lack role="tablist" containers.

User Impact

  • Affected Users: Screen reader users
  • Severity: Users cannot determine which tab is currently selected or navigate efficiently through tab interfaces

Violations Found

File: src/app/shared/components/settings/settings.component.html

Lines: 9-11

<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" [tabPanel]="tabPanel">
  <div routerLink="{{links[0].link}}" tabindex="1" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[0].link" (click)="activeLink = links[0].link">{{links[0].name}}</div>
  <div *ngIf="!+appConfig.SSO.rtlSSO" routerLink="{{links[1].link}}" tabindex="2" role="tab" mat-tab-link class="mat-tab-label" [active]="activeLink === links[1].link" [state]="{ initial: false }" (click)="activeLink = links[1].link">{{links[1].name}}</div>

Issue: Tabs have role="tab" but missing aria-selected state binding

File: src/app/cln/routing/routing.component.html

Line: 11

<div *ngFor="let link of links" routerLink="{{link.link}}" tabindex="1" mat-tab-link role="tab" class="mat-tab-label" [active]="activeLink === link.link" (click)="activeLink = link.link">{{link.name}}</div>

Recommended Fix

<nav mat-tab-nav-bar mat-stretch-tabs="false" mat-align-tabs="start" [tabPanel]="tabPanel" role="tablist" aria-label="Settings navigation">
  <div 
    routerLink="{{links[0].link}}" 
    role="tab" 
    mat-tab-link 
    class="mat-tab-label" 
    [attr.aria-selected]="activeLink === links[0].link"
    [attr.tabindex]="activeLink === links[0].link ? 0 : -1"
    (click)="activeLink = links[0].link"
    (keydown.arrowRight)="focusNextTab()"
    (keydown.arrowLeft)="focusPrevTab()">
    {{links[0].name}}
  </div>

Changes Made:

  1. Add role="tablist" to the nav container (or ensure Angular Material provides this)
  2. Add aria-label to tablist for context
  3. Bind aria-selected to active state
  4. Implement roving tabindex (only active tab in tab order)
  5. Add arrow key navigation for tab switching
  6. Remove positive tabindex values

Additional Instances

All files with tab navigation patterns:

  • src/app/shared/components/node-config/node-config.component.html
  • src/app/shared/components/node-config/services-settings/services-settings.component.html
  • src/app/cln/graph/graph.component.html
  • src/app/cln/transactions/transactions.component.html
  • src/app/cln/on-chain/on-chain.component.html
  • src/app/cln/reports/reports.component.html
  • src/app/lnd/graph/graph.component.html
  • src/app/lnd/transactions/transactions.component.html
  • src/app/lnd/routing/routing.component.html
  • src/app/eclair/graph/graph.component.html
  • And more...

Testing Instructions

  1. Use screen reader (NVDA/VoiceOver) to navigate tab interfaces
  2. Verify tab role is announced
  3. Verify selected state is announced
  4. Test arrow key navigation between tabs
  5. Verify tablist container is announced

Resources

Acceptance Criteria

  • All tabs have aria-selected state bound
  • Tab containers have role="tablist"
  • Roving tabindex implemented
  • Arrow key navigation works
  • Tested with screen reader (NVDA/VoiceOver)

Metadata

Metadata

Assignees

No one assigned

    Labels

    accessibilityAccessibility improvements for WCAG complianceseverity-mediumMedium severity accessibility issuewcag-aWCAG Level A compliance

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions