fix(insights): normalize stat-strip and target panel header rhythm

The stat-strip and target-summary panels were bare card-styled divs with
a flat space-y gap, so their tile header sat at a different offset from
the Card-wrapped tiles (assessment, mood, medication) on the same subpage
spine. Wrap both in Card / CardHeader pb-2 / CardContent so the
header-to-body offset matches. All content and data-slots are preserved;
the panel semantics ride on role + aria-label.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.8 committed 2026-06-05 12:58:29 +02:00
1 parent f1f6018f6e
commit 036370fc88
2 files changed
+63 -45

No files matched your search

+30 -20
View File
@@ -4,6 +4,7 @@ import type { ComponentType } from "react";
import { Sigma } from "lucide-react";
import { useFormatters, useTranslations } from "@/lib/i18n/context";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { TileHeader } from "@/components/insights/tile-header";
import type { DataSummary } from "@/lib/analytics/trends";
@@ -100,35 +101,44 @@ export function MetricStatStrip({
];
return (
<section
// Card-wrapped so the header-to-body offset matches the `<CardHeader
// pb-2>` tiles on the same subpage spine (assessment, mood, medication)
// rather than the old bare-div `space-y-3`. The grid keeps its own
// data-slots; the section semantics ride on `role` + `aria-label`.
<Card
data-slot="metric-stat-strip"
role="group"
aria-label={
seriesLabel
? `${t("insights.subPage.stats.label")} — ${seriesLabel}`
: t("insights.subPage.stats.label")
}
className="bg-card border-border space-y-3 rounded-xl border p-4"
className="gap-2 py-4 md:gap-3 md:py-5"
>
{seriesLabel ? (
<TileHeader icon={icon ?? Sigma} title={seriesLabel} />
<CardHeader className="pb-2">
<TileHeader icon={icon ?? Sigma} title={seriesLabel} />
</CardHeader>
) : null}
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4 [&>div]:min-h-[56px]">
{cells.map((cell) => (
<div
key={cell.key}
data-slot="metric-stat"
data-stat={cell.key}
className="space-y-1"
>
<p className="text-muted-foreground text-[10px] tracking-wide uppercase">
{cell.label}
</p>
<p className="text-lg font-semibold tabular-nums">
{format(cell.value)}
</p>
<CardContent>
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4 [&>div]:min-h-[56px]">
{cells.map((cell) => (
<div
key={cell.key}
data-slot="metric-stat"
data-stat={cell.key}
className="space-y-1"
>
<p className="text-muted-foreground text-[10px] tracking-wide uppercase">
{cell.label}
</p>
<p className="text-lg font-semibold tabular-nums">
{format(cell.value)}
</p>
</div>
))}
</div>
))}
</div>
</section>
</CardContent>
</Card>
);
}
@@ -8,6 +8,7 @@ import { useAuth } from "@/hooks/use-auth";
import { queryKeys } from "@/lib/query-keys";
import { useTranslations } from "@/lib/i18n/context";
import { convertGlucose, resolveGlucoseUnit } from "@/lib/glucose";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { RangeBar } from "@/components/targets/range-bar";
import { ConsistencyStrip } from "@/components/targets/consistency-strip";
import { TargetStatusPill } from "@/components/targets/target-status-pill";
@@ -297,10 +298,14 @@ function TargetReferencePanel({
target.consistency7d.length > 0;
return (
<div
// Card-wrapped so the header-to-body offset matches the `<CardHeader
// pb-2>` tiles on the same subpage spine rather than the old bare-div
// `space-y-3`. The body keeps its inter-row `space-y-3` inside
// `CardContent`; every data-slot is preserved.
<Card
data-slot="metric-target-summary"
data-target-type={target.type}
className="bg-card border-border space-y-3 rounded-xl border p-4"
className="gap-2 py-4 md:gap-3 md:py-5"
>
{/* Row 1: the canonical tile header — a single-word "Ziel" / "Target"
heading (the range numbers live on the range bar below, so the
@@ -309,27 +314,29 @@ function TargetReferencePanel({
per-context label (Fasting / Postprandial / …) carries real,
non-duplicative info, so it stays as a sub-caption under the
header. */}
<TileHeader
icon={Target}
title={t("insights.target.heading")}
right={
target.classification ? (
<TargetStatusPill
classification={target.classification}
range={range}
unit={unit}
source={target.source}
/>
) : null
}
/>
{heading ? (
<p className="text-muted-foreground text-[0.6875rem] font-medium tracking-[0.06em] uppercase">
{heading}
</p>
) : null}
{/* Row 2: range bar — where today's value sits inside the band. */}
<CardHeader className="pb-2">
<TileHeader
icon={Target}
title={t("insights.target.heading")}
right={
target.classification ? (
<TargetStatusPill
classification={target.classification}
range={range}
unit={unit}
source={target.source}
/>
) : null
}
/>
{heading ? (
<p className="text-muted-foreground text-[0.6875rem] font-medium tracking-[0.06em] uppercase">
{heading}
</p>
) : null}
</CardHeader>
<CardContent className="space-y-3">
{/* Row 2: range bar — where today's value sits inside the band. */}
{target.current != null ? (
<RangeBar
value={target.current}
@@ -393,7 +400,8 @@ function TargetReferencePanel({
{t("targets.sourceLabel", { source: target.source })}
</span>
)}
</div>
</div>
</div>
</CardContent>
</Card>
);
}