Add a subtitle slot to the insights overview section heading

Detail pages already render a one-line description under their heading;
the overview section headings had no slot for one. Add an optional
subtitle to SectionHeading, rendered in the same muted typography, and
wire it into the obvious top-level sections: Trends (folding its existing
right-aligned caption into the slot), Your health scores, and Your
vitals.
This commit is contained in:
Marc-André Bombeck committed 2026-06-27 13:02:41 +02:00
1 parent f5e9d875cc
commit 07b65ade88
6 files changed
+31 -8

No files matched your search

+2
View File
@@ -3240,6 +3240,7 @@
},
"vitals": {
"sectionTitle": "Deine Vitalwerte",
"sectionSubtitle": "Ruhewerte von Herz-Kreislauf und Atmung im Vergleich zu deiner eigenen Baseline",
"loadingLabel": "Vitalwerte werden geladen",
"loadError": "Deine Vitalwerte konnten gerade nicht geladen werden.",
"building": "Typischer Bereich entsteht — {count} von {target} Tagen",
@@ -3366,6 +3367,7 @@
},
"scores": {
"sectionTitle": "Deine Gesundheitswerte",
"sectionSubtitle": "Zusammengesetzte Werte für Erholung, Stress und Belastung aus deinen verbundenen Geräten",
"loadError": "Deine Wellness-Werte konnten gerade nicht geladen werden.",
"recovery": "Erholung",
"stress": "Stress",
+2
View File
@@ -3240,6 +3240,7 @@
},
"vitals": {
"sectionTitle": "Your vitals",
"sectionSubtitle": "Resting cardiovascular and respiratory signals against your own baseline",
"loadingLabel": "Loading your vitals",
"loadError": "Could not load your vitals right now.",
"building": "Building your typical range — {count} of {target} days",
@@ -3366,6 +3367,7 @@
},
"scores": {
"sectionTitle": "Your health scores",
"sectionSubtitle": "Composite recovery, stress and strain read from your connected devices",
"loadError": "Could not load your wellness scores right now.",
"recovery": "Recovery",
"stress": "Stress",
@@ -729,6 +729,7 @@ export function VitalsDashboard({ batch, layout, className }: DashboardProps) {
<SectionHeading
icon={HeartPulse}
title={t("insights.derived.vitals.sectionTitle")}
subtitle={t("insights.derived.vitals.sectionSubtitle")}
/>
<div
data-slot="vitals-dashboard-error"
@@ -774,6 +775,7 @@ export function VitalsDashboard({ batch, layout, className }: DashboardProps) {
<SectionHeading
icon={HeartPulse}
title={t("insights.derived.vitals.sectionTitle")}
subtitle={t("insights.derived.vitals.sectionSubtitle")}
/>
<div
data-slot="vitals-dashboard-grid"
@@ -211,6 +211,7 @@ export function WellnessScores({
<SectionHeading
icon={Activity}
title={t("insights.derived.scores.sectionTitle")}
subtitle={t("insights.derived.scores.sectionSubtitle")}
/>
<div
data-slot="wellness-scores-error"
@@ -394,6 +395,7 @@ export function WellnessScores({
<SectionHeading
icon={Activity}
title={t("insights.derived.scores.sectionTitle")}
subtitle={t("insights.derived.scores.sectionSubtitle")}
/>
<div
data-slot="wellness-scores-grid"
+22 -3
View File
@@ -34,6 +34,14 @@ interface SectionHeadingProps {
icon: ComponentType<{ className?: string }>;
/** Heading text rendered inside the `<h2>`. */
title: ReactNode;
/**
* Optional one-line description rendered directly under the `<h2>`, in the
* same muted typography the detail pages use beneath their headings
* (`text-muted-foreground text-sm`). Opt-in per section so the overview can
* carry the "descriptive line under every heading" rule without forcing a
* subtitle where none reads well.
*/
subtitle?: ReactNode;
/** Optional trailing affordance pinned to the right edge of the row. */
action?: ReactNode;
/** Optional id on the `<h2>`, e.g. for an `aria-labelledby` link. */
@@ -44,6 +52,7 @@ interface SectionHeadingProps {
export function SectionHeading({
icon: Icon,
title,
subtitle,
action,
id,
className,
@@ -58,9 +67,19 @@ export function SectionHeading({
>
<div className="flex min-w-0 items-center gap-2">
<Icon className="text-foreground h-5 w-5 shrink-0" aria-hidden="true" />
<h2 id={id} className="text-base font-semibold">
{title}
</h2>
<div className="min-w-0">
<h2 id={id} className="text-base font-semibold">
{title}
</h2>
{subtitle ? (
<p
data-slot="section-heading-subtitle"
className="text-muted-foreground text-sm"
>
{subtitle}
</p>
) : null}
</div>
</div>
{action ? (
<div className="flex shrink-0 items-center gap-2">{action}</div>
+1 -5
View File
@@ -200,11 +200,7 @@ export function TrendsRow({
<SectionHeading
icon={TrendingUp}
title={t("insights.trendsRow.title")}
action={
<p className="text-muted-foreground text-xs">
{t("insights.trendsRow.subtitle")}
</p>
}
subtitle={t("insights.trendsRow.subtitle")}
/>
{/* v1.4.22 A4 — equal-height cards. The annotation prose
below each chart varies in length, which used to leave the