refactor align with Bootstrap 5.3 best practices

- Use data-bs-theme="light" on <html> instead of bg-light text-dark on <body> (modern Bootstrap 5.3 color mode API) - Replace all text-muted with text-body-secondary (5.3 naming) - Replace bg-white on navbar with bg-body (theme-aware) - Add skip-to-content link (visually-hidden-focusable) - Add id="main-content" to <main> as skip link target - Use lead class on dashboard subtitle - Replace col-form-label on <legend> with fs-6 (proper usage) - Remove duplicate aria-label on inner graph-control div

Commit
9de8d16eb5f659a0dc96e47b0e74fc0315c25022
Author
GPT-5 medium <codex@openai.com>
Author date
Committer
GPT-5 medium <codex@openai.com>
Committer date
Changed files
roles/dashboard/public/js/dashboard/charts.js
index 283c2f50..fdb0b6eb 100644..100644
@@ -133,7 +133,7 @@
133 133
134 134 const percent = ((last - first) / Math.abs(first)) * 100;
135 135 const sign = percent >= 0 ? "+" : "";
136 Removed: const color = percent > 0 ? "text-success" : percent < 0 ? "text-danger" : "text-muted";
136 Added: const color = percent > 0 ? "text-success" : percent < 0 ? "text-danger" : "text-body-secondary";
137 137
138 138 element.className = `small fw-normal ${color}`;
139 139 element.textContent = `${sign}${percent.toFixed(1)}%`;
roles/dashboard/templates/dashboard/correlation-card.html.ep
index 048a67ad..018084f7 100644..100644
@@ -3,7 +3,7 @@
3 3 <div class="card">
4 4 <div class="card-header">
5 5 <h2 class="h5 mb-0">Dissolved Oxygen vs ORP</h2>
6 Removed: <p class="text-muted small mb-0 mt-1">Correlation scatter plot</p>
6 Added: <p class="text-body-secondary small mb-0 mt-1">Correlation scatter plot</p>
7 7 </div>
8 8 <div class="card-body">
9 9 <div class="chart-wrap">
roles/dashboard/templates/dashboard/downloads.html.ep
index a7bf97c8..8f8e9080 100644..100644
@@ -6,7 +6,7 @@
6 6 <div class="card">
7 7 <div class="card-header">
8 8 <h1 class="h5 mb-0">Downloads</h1>
9 Removed: <p class="text-muted small mb-0 mt-1">Download recorded DAQ readings as a CSV or Excel file.</p>
9 Added: <p class="text-body-secondary small mb-0 mt-1">Download recorded DAQ readings as a CSV or Excel file.</p>
10 10 </div>
11 11
12 12 <div class="card-body">
@@ -14,7 +14,7 @@
14 14 <form action="/downloads/readings.csv" method="get" class="downloads-form d-flex flex-column gap-3">
15 15
16 16 <fieldset>
17 Removed: <legend class="fw-bold col-form-label pt-0 mb-2">Probes</legend>
17 Added: <legend class="fs-6 fw-bold pt-0 mb-2">Probes</legend>
18 18 <p class="form-text mt-0">Leave all boxes clear to download every probe.</p>
19 19 <div>
20 20 % for my $probe ($probes->@*) {
@@ -27,7 +27,7 @@
27 27 </fieldset>
28 28
29 29 <fieldset>
30 Removed: <legend class="fw-bold col-form-label pt-0 mb-2">Date range</legend>
30 Added: <legend class="fs-6 fw-bold pt-0 mb-2">Date range</legend>
31 31 <div class="btn-group flex-wrap download-range-control" role="group" aria-label="Date range">
32 32 <input class="btn-check" type="radio" name="date_range" id="range-today" value="today" data-download-range data-from="<%= $to %>" data-to="<%= $to %>">
33 33 <label class="btn btn-outline-secondary" for="range-today">Today</label>
roles/dashboard/templates/dashboard/graph-control.html.ep
index 5469c173..e9783235 100644..100644
@@ -1,7 +1,7 @@
1 1 <!-- -*- mode: web; -*- -->
2 2
3 3 <div class="graph-control-scroll overflow-x-auto" tabindex="0" role="region" aria-label="<%= $aria_label %>">
4 Removed: <div class="graph-control d-inline-flex gap-2 flex-nowrap" aria-label="<%= $aria_label %>">
4 Added: <div class="graph-control d-inline-flex gap-2 flex-nowrap">
5 5 <div class="input-group input-group-sm range-control" aria-label="Adjust range">
6 6 <button
7 7 class="btn btn-outline-secondary"
roles/dashboard/templates/dashboard/index.html.ep
index 38d89377..4d23355b 100644..100644
@@ -7,7 +7,7 @@
7 7 <section class="mb-4" aria-label="DAQ status overview">
8 8 <div class="mb-3">
9 9 <h1 class="display-5 fw-bold mb-1">FAPG DAQ Dashboard</h1>
10 Removed: <p class="text-muted mb-2">Live aquaponic sensor readings</p>
10 Added: <p class="lead text-body-secondary mb-2">Live aquaponic sensor readings</p>
11 11 <nav aria-label="Quick links">
12 12 <ul class="nav nav-pills">
13 13 <li class="nav-item"><a class="nav-link" href="/probes">Probes</a></li>
@@ -18,7 +18,7 @@
18 18 </div>
19 19
20 20 <section class="reading-quick-status" aria-label="Sensor reading quick status">
21 Removed: <h2 class="h6 fw-semibold text-uppercase text-muted mb-3">Live readings</h2>
21 Added: <h2 class="h6 fw-semibold text-uppercase text-body-secondary mb-3">Live readings</h2>
22 22 <div class="row g-3" data-reading-quick-status>
23 23 % for my $probe ($probes->@*) {
24 24 <div class="col-12 col-sm-6 col-xl-3">
@@ -31,12 +31,12 @@
31 31 <div class="card-body d-flex flex-column gap-2">
32 32 <div class="reading-quick-header">
33 33 <h3 class="fs-6 fw-bold mb-0"><%= $probe->{label} %></h3>
34 Removed: <p class="text-muted small mb-0" data-reading-quick-value>Waiting for data</p>
34 Added: <p class="text-body-secondary small mb-0" data-reading-quick-value>Waiting for data</p>
35 35 </div>
36 36 <div class="reading-range" role="meter" aria-valuemin="0" aria-valuemax="100" aria-valuenow="50">
37 37 <span class="reading-range-marker" data-reading-quick-marker></span>
38 38 </div>
39 Removed: <div class="reading-range-labels d-grid gap-1 small text-muted">
39 Added: <div class="reading-range-labels d-grid gap-1 small text-body-secondary">
40 40 <span data-reading-quick-min></span>
41 41 <span class="fw-bold text-center" data-reading-quick-target></span>
42 42 <span class="text-end" data-reading-quick-max></span>
roles/dashboard/templates/dashboard/probe-card.html.ep
index 66500941..12e8b9dc 100644..100644
@@ -5,9 +5,9 @@
5 5 <div class="probe-card-title min-w-0">
6 6 <h1 class="h5 mb-0">
7 7 <a class="text-body text-decoration-none" href="/probes/<%= $probe->{key} %>"><%= $probe->{label} %></a>
8 Removed: <span class="small fw-normal text-muted" data-variation="<%= $probe->{key} %>"></span>
8 Added: <span class="small fw-normal text-body-secondary" data-variation="<%= $probe->{key} %>"></span>
9 9 </h1>
10 Removed: <p class="text-muted small mb-0 mt-1">
10 Added: <p class="text-body-secondary small mb-0 mt-1">
11 11 Unit: <strong><%= $probe->{unit} %></strong>
12 12 </p>
13 13 </div>
@@ -36,7 +36,7 @@
36 36 <canvas id="chart-<%= $probe->{key} %>" role="img" aria-label="<%= $probe->{label} %> readings chart"></canvas>
37 37 </div>
38 38 % unless (stash('single_probe')) {
39 Removed: <p class="text-muted small mt-2 mb-0 reading-status placeholder-glow" data-status="<%= $probe->{key} %>">
39 Added: <p class="text-body-secondary small mt-2 mb-0 reading-status placeholder-glow" data-status="<%= $probe->{key} %>">
40 40 <span class="placeholder col-8">Waiting for data…</span>
41 41 </p>
42 42 % }
roles/dashboard/templates/dashboard/status.html.ep
index 2331edeb..e91cc510 100644..100644
@@ -6,7 +6,7 @@
6 6 <div class="card">
7 7 <div class="card-header">
8 8 <h1 class="h5 mb-0">DAQ Status</h1>
9 Removed: <p class="text-muted small mb-0 mt-1">Current connection status for the hub and sensor probes.</p>
9 Added: <p class="text-body-secondary small mb-0 mt-1">Current connection status for the hub and sensor probes.</p>
10 10 </div>
11 11
12 12 <div class="list-group list-group-flush">
@@ -25,7 +25,7 @@
25 25 </h2>
26 26 <span class="badge status-pill is-unknown" data-status-page-state>Checking</span>
27 27 </div>
28 Removed: <div class="row row-cols-1 row-cols-sm-2 g-1 mt-1 small text-muted">
28 Added: <div class="row row-cols-1 row-cols-sm-2 g-1 mt-1 small text-body-secondary">
29 29 <div class="col">
30 30 <span class="fw-semibold text-uppercase status-node-dt">Node:</span>
31 31 <span data-status-page-node>Checking…</span>
roles/dashboard/templates/layouts/default.html.ep
index 50089dd0..fac43a4c 100644..100644
@@ -1,7 +1,7 @@
1 1 <!-- -*- mode: web; -*- -->
2 2
3 3 <!doctype html>
4 Removed: <html lang="en">
4 Added: <html lang="en" data-bs-theme="light">
5 5 <head>
6 6 <meta charset="utf-8">
7 7 <title><%= title %></title>
@@ -16,9 +16,11 @@
16 16 crossorigin="anonymous">
17 17 <link rel="stylesheet" href="/css/app.css">
18 18 </head>
19 Removed: <body class="bg-light text-dark">
19 Added: <body>
20 20
21 Removed: <nav class="navbar navbar-expand-md bg-white border-bottom shadow-sm sticky-top mb-3" aria-label="Dashboard navigation">
21 Added: <a class="visually-hidden-focusable" href="#main-content">Skip to main content</a>
22 Added:
23 Added: <nav class="navbar navbar-expand-md bg-body border-bottom shadow-sm sticky-top mb-3" aria-label="Dashboard navigation">
22 24 <div class="container-fluid px-3">
23 25
24 26 <a class="navbar-brand fw-bold text-success" href="/">FAPG</a>
@@ -73,14 +75,14 @@
73 75 </div>
74 76 </nav>
75 77
76 Removed: <main class="container">
78 Added: <main id="main-content" class="container">
77 79 %= include 'dashboard/toolbar', breadcrumbs => (stash('breadcrumbs') // []), graph_control_aria_label => stash('graph_control_aria_label')
78 80 <%= content %>
79 81 </main>
80 82
81 Removed: <footer class="text-center text-muted small py-3 mt-4 bg-body-tertiary border-top">
83 Added: <footer class="text-center text-body-secondary small py-3 mt-4 bg-body-tertiary border-top">
82 84 <p class="mb-0">
83 Removed: <a class="text-muted" href="https://www.lafermeaquaponique.com/">lafermeaquaponique.com</a>
85 Added: <a class="text-body-secondary" href="https://www.lafermeaquaponique.com/">lafermeaquaponique.com</a>
84 86 </p>
85 87 <p class="mb-0">Copyright &copy; 2026 Marius Peter</p>
86 88 </footer>