mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
fix(a11y): lift new icon buttons to the 44 px tap-target floor
The per-card Coach button on the targets page rode the shadcn icon variant (size-10, 40 px) and the HealthScore delta explainer trigger collapsed to a 16 px chip. Both sit under the project's 44 px floor (WCAG 2.5.5). The targets Coach button gains min-h-11 min-w-11 on the icon variant; the delta explainer keeps a 12 px visible glyph but inflates the hit surface to 44 px and pulls the row stride back via negative margin so the optical chip stays small.
This commit is contained in:
1 parent
5570971f3d
commit
b3f88026f0
4 files changed
+45
-7
No files matched your search
@@ -55,6 +55,21 @@ describe("<HealthScoreDeltaExplainer>", () => {
|
||||
expect(trigger?.[0]).toContain('aria-label="What does this delta mean?"');
|
||||
});
|
||||
|
||||
it("lifts the tap target to the 44 px WCAG 2.5.5 floor", () => {
|
||||
// The glyph stays 12 px but the click surface inflates to 44 px
|
||||
// via `min-h-11 min-w-11` on the trigger; the parent row keeps
|
||||
// its 16 px stride because `-my-3 -mx-2` swallows the extra
|
||||
// reach. Pinning so a future refactor can't drop back to the
|
||||
// visual-chip-as-hit-target shape that failed WCAG 2.5.5.
|
||||
const html = ssr(<HealthScoreDeltaExplainer delta={-3} />);
|
||||
const trigger = html.match(
|
||||
/<button[^>]*data-slot="health-score-delta-explainer-trigger"[^>]*>/,
|
||||
);
|
||||
expect(trigger).not.toBeNull();
|
||||
expect(trigger?.[0]).toContain("min-h-11");
|
||||
expect(trigger?.[0]).toContain("min-w-11");
|
||||
});
|
||||
|
||||
it("renders the trigger in German with the localised aria-label", () => {
|
||||
const html = ssr(<HealthScoreDeltaExplainer delta={2} />, "de");
|
||||
const trigger = html.match(
|
||||
|
||||
@@ -65,13 +65,16 @@ export function HealthScoreDeltaExplainer({
|
||||
data-slot="health-score-delta-explainer-trigger"
|
||||
aria-label={triggerLabel}
|
||||
className={cn(
|
||||
// Icon-only chip sized to read at the delta line's height
|
||||
// without inflating the row. The 16 px hit target satisfies
|
||||
// the existing 11 px delta line's reading rhythm while the
|
||||
// outer focus ring still grants a comfortable tap target
|
||||
// because the ring sits outside the box.
|
||||
// Visible glyph stays 12 px so the chip reads at the delta
|
||||
// line's height without inflating the row. The button itself
|
||||
// sits at the 44 px WCAG 2.5.5 floor via `min-h-11 min-w-11`;
|
||||
// negative `-my-3 -mx-2` collapses the surrounding row back
|
||||
// to its 16 px stride so the optical chip stays small while
|
||||
// the hit surface lifts to 44 px. Transparent padding owns
|
||||
// the extra reach.
|
||||
"text-muted-foreground hover:text-foreground focus-visible:ring-ring/50",
|
||||
"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full",
|
||||
"inline-flex min-h-11 min-w-11 shrink-0 items-center justify-center rounded-full",
|
||||
"-my-3 -mx-2",
|
||||
"transition-colors focus-visible:ring-2 focus-visible:outline-none",
|
||||
className,
|
||||
)}
|
||||
|
||||
@@ -37,6 +37,21 @@ describe("<TargetCoachButton>", () => {
|
||||
expect(html).not.toMatch(/>Ask Coach</);
|
||||
});
|
||||
|
||||
it("lifts the tap target to the 44 px WCAG 2.5.5 floor", () => {
|
||||
// The icon variant alone ships `size-10` (40 px); the per-card
|
||||
// Coach button rides `min-h-11 min-w-11` on top so the hit
|
||||
// surface matches the medication-history and Coach drawer
|
||||
// buttons. Pinning the class so a future refactor can't drop
|
||||
// back to the 40 px shadcn default.
|
||||
const html = render({ ...baseProps, aiEnabled: true });
|
||||
const trigger = html.match(
|
||||
/<button[^>]*data-slot="target-coach-cta"[^>]*>/,
|
||||
);
|
||||
expect(trigger).not.toBeNull();
|
||||
expect(trigger?.[0]).toContain("min-h-11");
|
||||
expect(trigger?.[0]).toContain("min-w-11");
|
||||
});
|
||||
|
||||
it("renders NOTHING when aiEnabled is false (no broken-button state)", () => {
|
||||
const html = render({ ...baseProps, aiEnabled: false });
|
||||
expect(html).toBe("");
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { MessageCircle } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import type { CoachScope, CoachScopeSource } from "@/lib/ai/coach/types";
|
||||
|
||||
@@ -78,7 +79,11 @@ export function TargetCoachButton({
|
||||
data-slot="target-coach-cta"
|
||||
aria-label={accessibleLabel}
|
||||
title={accessibleLabel}
|
||||
className={className}
|
||||
// Lift the icon button to the 44 px WCAG 2.5.5 floor. The
|
||||
// shadcn `icon` variant ships `size-10` (40 px); the project
|
||||
// floor is 44 px (matches the medication-history button and the
|
||||
// Coach drawer cluster). Glyph stays `size-4`.
|
||||
className={cn("min-h-11 min-w-11", className)}
|
||||
>
|
||||
<MessageCircle className="size-4" aria-hidden="true" />
|
||||
</Button>
|
||||
|
||||
Reference in new issue
Block a user