Files
HealthLog/src/components/insights/section-heading.tsx
T
Marc-André Bombeck 86f416fb2a Unify Insights overview section headings onto one icon system
Every overview section now leads with a single SectionHeading above its
card: a Lucide icon in the foreground colour and an h2 title at one size
and weight. Previously the overview mixed four heading dialects — a
purple Sparkles glyph on the briefing, no icon on Trends, a white-icon
tile header on the score and vitals strips, and in-card titles buried
inside the period-in-review, cycle-summary and signals cards.

- Add SectionHeading (icon + title + optional trailing action).
- Briefing: purple icon to white; heading moves through SectionHeading.
- Trends: add a TrendingUp icon (was icon-less); subtitle becomes the
  trailing action.
- Health scores + vitals: migrate the section headers off TileHeader.
- Period in review, signals of the day, device notifications: lift the
  in-card title out to a heading above the card.
- Signals card min-height drops to match its slimmer body now that the
  title row left the card.
2026-06-07 14:15:10 +02:00

71 lines
2.5 KiB
TypeScript

import type { ComponentType, ReactNode } from "react";
import { cn } from "@/lib/utils";
/**
* v1.15.10 — the one section heading for the Insights overview.
*
* Every top-level overview section (health scores, daily briefing, vitals,
* trends, period-in-review, cycle summary, signals of the day) leads with
* this identical heading ABOVE its card: a leading Lucide icon and an `<h2>`
* title, both in the foreground colour (white in dark mode — never a per-
* section accent hue), at one size + weight. An optional `action` slot pins a
* trailing affordance (a subtitle, a meta control) to the right edge without
* breaking the row's alignment.
*
* The overview used to mix three heading dialects: a `text-lg` `<h2>` with a
* purple Sparkles glyph (briefing), a `text-lg` `<h2>` with NO icon (trends),
* a `text-base` `TileHeader` with a white icon (scores / vitals), and in-card
* titles buried inside the period-narrative, cycle-summary and signals cards.
* Side by side they read as four different surfaces. `SectionHeading` pins the
* single contract so the system can't drift: icon `h-5 w-5 shrink-0
* text-foreground`, title `<h2>` `text-base font-semibold`, `gap-2` between
* icon and title. The caller owns the `space-y-3` gap to the card below.
*
* RSC-safe: no hooks, no browser API.
*/
interface SectionHeadingProps {
/**
* Leading glyph. The caller passes the Lucide component itself
* (`icon={Sparkles}`), not a pre-sized node — `SectionHeading` owns the
* size and colour so every overview heading matches.
*/
icon: ComponentType<{ className?: string }>;
/** Heading text rendered inside the `<h2>`. */
title: 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. */
id?: string;
className?: string;
}
export function SectionHeading({
icon: Icon,
title,
action,
id,
className,
}: SectionHeadingProps) {
return (
<div
data-slot="section-heading"
className={cn(
"flex flex-wrap items-center justify-between gap-2",
className,
)}
>
<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>
{action ? (
<div className="flex shrink-0 items-center gap-2">{action}</div>
) : null}
</div>
);
}