All Docs
FeaturesMaking Tax DigitalUpdated March 20, 2026

Accessibility Improvements: Fixing Dual h1 Headings and ARIA Tab Patterns

Accessibility Improvements: Fixing Dual h1 Headings and ARIA Tab Patterns

Release: v1.0.437 · Control: A11Y-07 · Category: Screen Reader


This release resolves two structural accessibility issues identified under control A11Y-07 that affected screen reader users on the dashboard and settings pages.

What Was Wrong

Two <h1> Elements on the Dashboard

Every web page should have exactly one <h1> — the top-level heading that defines the document's main topic. The dashboard was rendering two:

  1. A <h1> inside DashboardHeader displaying the page title sourced from the PAGE_TITLES lookup.
  2. A second <h1> inside dashboard/page.tsx for the personalised welcome message.

When a screen reader user navigates by headings (a common technique to quickly scan a page), two competing <h1> elements produce a confusing and ambiguous document outline. Automated accessibility auditors also flag this as a failure.

Settings Tabs Announced as Plain Buttons

The SettingsTabs component rendered its navigation tabs as <button> elements with no ARIA semantics. Without the ARIA tab pattern in place:

  • Screen readers announced each tab as a generic button, giving no indication it was part of a tab interface.
  • There was no programmatic signal for which tab was currently active (aria-selected).
  • Assistive technologies could not expose the relationship between a tab control and the panel it reveals.

What Changed

Single <h1>dashboard-header.tsx

The page title rendered by DashboardHeader has been demoted from <h1> to a <p> or <span> element. Its visual appearance (size, weight, colour) is unchanged — only the semantic element has been corrected. The <h1> on each dashboard page now belongs solely to the main content area, giving every page a clear, unambiguous top-level heading.

// Before
<h1 className="text-lg font-semibold">{title}</h1>

// After
<p className="text-lg font-semibold">{title}</p>

Full ARIA Tab Pattern — SettingsTabs

The settings tab strip now implements the complete ARIA Authoring Practices tab pattern:

// Tab container
<div role="tablist">

  // Each tab button
  <button
    role="tab"
    aria-selected={active === tab.id}
    id={`tab-${tab.id}`}
  >
    {tab.label}
  </button>

</div>

// Each panel
<div
  role="tabpanel"
  aria-labelledby={`tab-${tab.id}`}
>
  {/* panel content */}
</div>

With these changes:

  • Screen readers announce the widget as a tab list and identify each item as a tab.
  • aria-selected is true on the active tab and false on all others, so the current state is always communicated.
  • aria-labelledby on each panel links it back to its tab, so navigating into a panel reveals which tab controls it.

Who Is Affected

These fixes benefit:

  • Users who navigate with a screen reader (NVDA, JAWS, VoiceOver, TalkBack).
  • Users who navigate by keyboard only.
  • Automated accessibility audit tools (axe, Lighthouse, Wave) — both issues were reportable failures.

No visual changes have been made. The dashboard and settings pages look identical to previous versions.

Affected Files

FileChange
src/app/dashboard/dashboard-header.tsx<h1><p> for page title in DashboardHeader; role="tablist", role="tab", aria-selected, role="tabpanel", aria-labelledby added to SettingsTabs