From 338808eb5f27d4918e47dbf82c6c442c722bb71a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 25 Jul 2026 14:12:01 +0200 Subject: [PATCH] refactor(ui): route the sleep-composition header through the tile header The stage-composition chart card was the last insights tile with a hand-rolled title row. It now uses the canonical header primitive; the subtitle and the window toggles keep the shape they had, since the toggles have to drop to their own row on a phone. Also sweeps the last off-scale spacing: the two dense tiles inside a recommendation move onto the sanctioned inner-tile step, and the not-found button onto the scale. The banned twenty-pixel step is now absent from the tree. The chart body is untouched. --- src/app/not-found.tsx | 2 +- .../__tests__/sleep-stage-stacked-bar.test.tsx | 3 +++ src/components/insights/recommendation-card.tsx | 4 ++-- .../insights/sleep-stage-stacked-bar.tsx | 15 ++++++++++----- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index 368492db0..0413a3f69 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -49,7 +49,7 @@ export default async function NotFound() { {t("notFound.backToDashboard")} diff --git a/src/components/insights/__tests__/sleep-stage-stacked-bar.test.tsx b/src/components/insights/__tests__/sleep-stage-stacked-bar.test.tsx index 9aedff7a4..b8e48baf1 100644 --- a/src/components/insights/__tests__/sleep-stage-stacked-bar.test.tsx +++ b/src/components/insights/__tests__/sleep-stage-stacked-bar.test.tsx @@ -51,6 +51,9 @@ describe("", () => { const html = render(); expect(html).toContain("Stage composition"); expect(html).toContain("Last 23 nights"); + // The title routes through the canonical tile header rather than a + // hand-rolled title row (UI-STANDARDS §1 / §8). + expect(html).toContain('data-slot="tile-header"'); }); it("exposes an accessible label that announces the nights covered", () => { diff --git a/src/components/insights/recommendation-card.tsx b/src/components/insights/recommendation-card.tsx index 59d5cd911..83467c75c 100644 --- a/src/components/insights/recommendation-card.tsx +++ b/src/components/insights/recommendation-card.tsx @@ -240,7 +240,7 @@ function RationaleCard({ return (
diff --git a/src/components/insights/sleep-stage-stacked-bar.tsx b/src/components/insights/sleep-stage-stacked-bar.tsx index 84f32fe75..0203dc2d8 100644 --- a/src/components/insights/sleep-stage-stacked-bar.tsx +++ b/src/components/insights/sleep-stage-stacked-bar.tsx @@ -14,7 +14,8 @@ import { } from "recharts"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import { TileHeader } from "@/components/insights/tile-header"; import { useTranslations } from "@/lib/i18n/context"; import { formatDurationMinutes } from "@/lib/i18n/duration"; import { resolveIntlLocale } from "@/lib/format-locale"; @@ -205,10 +206,14 @@ export function SleepStageStackedBar({ breakdown }: SleepStageStackedBarProps) {
-
- - {t("insights.sleep.compositionTitle")} - +
+ {/* The canonical tile header — the title used to be a bare + `CardTitle` in a hand-rolled row. The window toggles stay a + sibling of the title+subtitle pair rather than riding the + header's `right` slot: the subtitle belongs under the title, + and the toggles need to drop to their own row below `sm` + (the sanctioned inline-action-row shape). */} + {t("insights.sleep.compositionSubtitle", { nights: breakdown.nights,