mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
fix(a11y): keyboard-reachable admin log rows, range-bar marker, heatmap cells
Admin app-log rows opened their JSON detail dialog through a click-only <tr> with no keyboard path; the Action cell now wraps a real <button> that carries the same handler plus a descriptive aria-label, leaving the row onClick as a pointer convenience. The target range-bar's current-value marker showed value + band only in a hover-only tooltip on a non-focusable div. It is now focusable (tabIndex, role=img) with an aria-label that reuses the same value / range / delta copy the tooltip already renders, so focus opens the tooltip and a screen reader gets the text alternative either way. Mood and medication-compliance heatmap day cells were pointer-only SVG rects. Each cell is now focusable with its own per-day aria-label (reusing the existing tooltip copy), and Escape dismisses any open tooltip — pinned (touch) or focus-shown. Reference: 2026-07-17 a11y audit, H1 + M1 + M2.
This commit is contained in:
1 parent
247bc4f33c
commit
4c436d7dfc
15 files changed
+243
-27
No files matched your search
@@ -57,4 +57,16 @@ describe("<RangeBar>", () => {
|
||||
});
|
||||
expect(html).toContain("var(--destructive)");
|
||||
});
|
||||
|
||||
it("makes the marker focusable with a text alternative for the value + range (2026-07-17 a11y audit M1)", () => {
|
||||
const html = render({
|
||||
value: 72,
|
||||
min: 60,
|
||||
max: 100,
|
||||
unit: "bpm",
|
||||
});
|
||||
expect(html).toContain('tabindex="0"');
|
||||
expect(html).toContain('role="img"');
|
||||
expect(html).toContain('aria-label="Current: 72 bpm');
|
||||
});
|
||||
});
|
||||
@@ -92,6 +92,17 @@ export function RangeBar({
|
||||
: t("targets.aboveTarget", { delta: delta.toFixed(1), unit })
|
||||
: t("targets.inTarget");
|
||||
|
||||
// 2026-07-17 a11y audit (M1) — the marker's value + range live only in a
|
||||
// hover-only Radix tooltip on a non-focusable `<div>`. Reusing the same
|
||||
// three strings the tooltip already shows as one text alternative closes
|
||||
// 1.1.1 (no separate copy to drift) — `tabIndex` below makes the trigger
|
||||
// focusable, which is all Radix needs to also open the tooltip on focus.
|
||||
const markerAriaLabel = [
|
||||
t("targets.currentValue", { value: String(value), unit }),
|
||||
t("targets.targetRangeValue", { min: String(min), max: String(max), unit }),
|
||||
deltaText,
|
||||
].join(". ");
|
||||
|
||||
return (
|
||||
<div className="space-y-1.5" data-slot="target-range-bar">
|
||||
<div className="bg-muted/50 relative h-3 w-full overflow-hidden rounded-full">
|
||||
@@ -129,7 +140,10 @@ export function RangeBar({
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className="absolute top-1/2 h-5 w-5 -translate-x-1/2 -translate-y-1/2 cursor-pointer rounded-full border-2 shadow-sm"
|
||||
role="img"
|
||||
tabIndex={0}
|
||||
aria-label={markerAriaLabel}
|
||||
className="focus-visible:ring-ring absolute top-1/2 h-5 w-5 -translate-x-1/2 -translate-y-1/2 cursor-pointer rounded-full border-2 shadow-sm focus-visible:ring-2 focus-visible:outline-none"
|
||||
style={{
|
||||
left: `${position}%`,
|
||||
backgroundColor: markerColor,
|
||||
|
||||
Reference in new issue
Block a user