Files
HealthLog/src/components/insights/target-adjust-button.tsx
T
Marc-André Bombeck 93bdbd8e0c fix(insights): match the header target gear to the Coach icon size
The target gear painted a heavier 44 px box on mobile against the Coach
icon's flat 40 px, so the header cluster read as two mismatched icons.
Settle the gear on a flat 40 px box that equals the Coach sparkle on every
breakpoint, and restore the 44 px WCAG 2.5.5 touch target through a
transparent ::before overlay rather than a larger painted box. The shared
icon-button size stays untouched, so other icon affordances are unaffected.
2026-06-04 18:20:38 +02:00

69 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { Settings2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { useTranslations } from "@/lib/i18n/context";
import { useTargetAdjust } from "@/lib/insights/target-adjust-context";
/**
* Header gear that opens the per-metric target editor.
*
* The "Adjust target range" affordance used to sit inside the target
* reference card; it now lives in the Insights category-page header
* (`<SubPageShell>`), to the left of the Coach launch icon, so the card
* stays a read surface. The button is a thin wrapper around
* `useTargetAdjust()`:
*
* - renders nothing when no provider is mounted or no editable target
* has registered for the page (e.g. a metric without a numeric
* target band, where the card paints nothing), so the header never
* shows a dead control;
* - opens the primary registered target's `<TargetEditSheet>` on click.
*
* Sized to match `<CoachLaunchButton variant="icon">` so the gear + the
* Coach sparkle read as a consistent icon cluster top-right of the
* heading.
*/
export function TargetAdjustButton({ className }: { className?: string }) {
const { t } = useTranslations();
const adjust = useTargetAdjust();
// No provider, or no editable target registered for this page.
if (!adjust || !adjust.canAdjust) return null;
// Reuse the action's existing copy ("Adjust target range" /
// "Zielbereich anpassen") — the gear performs exactly what the old
// in-card link did, so the same string is the right accessible label.
const label = t("insights.subPage.target.adjustLink");
return (
<Button
type="button"
variant="ghost"
size="icon"
data-slot="target-adjust-trigger"
aria-label={label}
title={label}
onClick={() => adjust.requestAdjust()}
className={cn(
// The `icon` size is a flat 40 px — identical to the sibling Coach
// sparkle icon — so the gear + Coach read as one even-weighted icon
// cluster top-right of the heading on every breakpoint.
//
// The 40 px visual box is below the WCAG 2.5.5 (44 px) floor, so a
// transparent `::before` overlay extends the *hit* area by 6 px on
// every side (40 + 2×6 = 52 px) without enlarging the painted box.
// This keeps the touch target comfortable while the gear stays
// visually equal to the Coach icon.
"text-muted-foreground hover:text-foreground relative size-10",
"before:absolute before:-inset-1.5 before:content-['']",
className,
)}
>
<Settings2 className="size-4" aria-hidden="true" />
</Button>
);
}