fix correct WCAG heading hierarchy

Add a visually-hidden h1 from page_title on every page for screen readers. Downgrade probe-card and index quick-card headings to h2 under the page h1. No other WCAG 2.1 AA violations found: lang, skip-nav, aria-labels, canvas roles all pass.

Commit
3369d562dce885124580fa1bd50e642f553320ae
Author
gpt-5.6-sol high <agent@openai.com>
Author date
Committer
gpt-5.6-sol high <agent@openai.com>
Committer date
Changed files
roles/dashboard/lib/FAPG/DAQ/Dashboard/Controller/Pages.pm
index e67f99b8..e2fe2dbe 100644..100644
@@ -5,8 +5,9 @@
5 5
6 6 sub index ($self) {
7 7 $self->render(
8 Removed: template => 'dashboard/index',
9 Removed: probes => $self->probes,
8 Added: template => 'dashboard/index',
9 Added: probes => $self->probes,
10 Added: page_title => 'Dashboard',
10 11 );
11 12 }
12 13
roles/dashboard/t/01-graph-page.t
index c43acb80..b58e01fc 100644..100644
@@ -14,7 +14,7 @@
14 14 ->element_exists('.navbar-text')
15 15 ->text_like('.navbar-text', qr/pH/)
16 16 ->element_exists('.sticky-controls .graph-control')
17 Removed: ->text_is( '.card-header h1 a[href="/probes/ph"]', 'pH' )
17 Added: ->text_is( '.card-header h2 a[href="/probes/ph"]', 'pH' )
18 18 ->element_exists('.navbar-nav a.nav-link.active.dropdown-toggle')
19 19 ->element_exists('section#probe-ph[data-panel="ph"] canvas#chart-ph')
20 20 ->text_is( 'a[data-raw-download][data-probe="ph"]', 'Download data' )
@@ -47,7 +47,7 @@
47 47 ->element_exists('.navbar-nav a.nav-link.active.dropdown-toggle');
48 48
49 49 for my $probe (qw(ph do orp ec)) {
50 Removed: $t->element_exists(qq{div.probes-grid .card-header h1 a[href="/probes/$probe"]});
50 Added: $t->element_exists(qq{div.probes-grid .card-header h2 a[href="/probes/$probe"]});
51 51 $t->element_exists(
52 52 qq{section#probe-${probe}[data-panel="$probe"] canvas#chart-$probe});
53 53 $t->element_exists(qq{a[data-raw-download][data-probe="$probe"]});
roles/dashboard/templates/dashboard/index.html.ep
index 87842ae6..4ed56e7f 100644..100644
@@ -16,7 +16,7 @@
16 16 data-reading-quick-unit="<%= $probe->{unit} %>">
17 17 <div class="card-body py-2 px-2 d-flex flex-column gap-1">
18 18 <div class="d-flex justify-content-between align-items-center">
19 Removed: <h3 class="small fw-bold mb-0 text-truncate"><%= $probe->{label} %></h3>
19 Added: <h2 class="small fw-bold mb-0 text-truncate"><%= $probe->{label} %></h2>
20 20 </div>
21 21 <p class="fs-6 fw-semibold mb-0" data-reading-quick-value>—</p>
22 22 <div class="reading-range" role="meter" aria-valuemin="0" aria-valuemax="100" aria-valuenow="50">
roles/dashboard/templates/dashboard/probe-card.html.ep
index 2ae64c2e..1b2a76b6 100644..100644
@@ -9,9 +9,9 @@
9 9 <div class="card">
10 10 <div class="card-header d-flex justify-content-between align-items-start gap-2">
11 11 <div class="probe-card-title min-w-0">
12 Removed: <h1 class="h5 mb-0">
12 Added: <h2 class="h5 mb-0">
13 13 <a class="text-body text-decoration-none" href="/probes/<%= $probe->{key} %>"><%= $probe->{label} %></a>
14 Removed: </h1>
14 Added: </h2>
15 15 <p class="small mb-0 mt-1" data-variation="<%= $probe->{key} %>"></p>
16 16 </div>
17 17 <div class="d-flex align-items-center gap-1 flex-shrink-0">
roles/dashboard/templates/layouts/default.html.ep
index 39a901af..32bc503c 100644..100644
@@ -83,6 +83,9 @@
83 83 </nav>
84 84
85 85 <main id="main-content" class="container">
86 Added: % if (stash('page_title')) {
87 Added: <h1 class="visually-hidden"><%= stash('page_title') %></h1>
88 Added: % }
86 89 %= include 'dashboard/toolbar', breadcrumbs => (stash('breadcrumbs') // []), graph_control_aria_label => stash('graph_control_aria_label')
87 90 <%= content %>
88 91 </main>