fix(ui): conform the wellbeing card and fill lone grid items full-width

The mental-wellbeing instrument card carried a font-medium title and a
gap-4 body, drifting from the medication card it should mirror; bring its
title to text-lg font-semibold and its body to the shared space-y-3.5
rhythm.

Several insight grids left a lone trailing tile stranded at half width
with an empty column beside it. Gate each multi-column grid on having
more than one item so a single tile spans the full row, and give the
fixed five-card mood-correlation grid a trailing-card span so it no
longer orphans on the two-up breakpoint.

Add a little bottom breathing room to the device-score tile so its
rolling-average line no longer reads flush against the card edge.
This commit is contained in:
Marc-André Bombeck committed 2026-06-30 12:29:17 +02:00
1 parent 304f0c7057
commit ca067bfe3d
7 files changed
+58 -10

No files matched your search

@@ -784,7 +784,14 @@ export function VitalsDashboard({ batch, layout, className }: DashboardProps) {
aria-label={
isLoading ? t("insights.derived.vitals.loadingLabel") : undefined
}
className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3"
className={cn(
"grid grid-cols-1 gap-3",
// Multi-column only when the skeleton row or more than one tile
// fills it; a lone tile spans the full width rather than sitting
// in a one-third column with dead space beside it.
(isLoading || vitalTiles.length > 1) &&
"sm:grid-cols-2 lg:grid-cols-3",
)}
>
{isLoading
? Array.from({ length: 6 }).map((_, i) => (
@@ -806,7 +813,12 @@ export function VitalsDashboard({ batch, layout, className }: DashboardProps) {
/>
<div
data-slot="vitals-mobility-grid"
className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3"
className={cn(
"grid grid-cols-1 gap-3",
// A lone mobility tile spans the full width rather than
// orphaning a one-third column.
mobilityTiles.length > 1 && "sm:grid-cols-2 lg:grid-cols-3",
)}
>
{mobilityTiles.map((tile) => tile.node)}
</div>
@@ -121,7 +121,7 @@ export function DeviceScoreTile({
</CardAction>
) : null}
</CardHeader>
<CardContent className="space-y-3">
<CardContent className="space-y-3 pb-1">
{isLearning ? (
<LearningGate
compact
@@ -83,15 +83,21 @@ const STRENGTH_KEY: Record<CorrelationResult["strength"], string> = {
function MoodCorrelationCard({
kind,
data,
className,
}: {
kind: MetricKind;
data: MoodMetricCorrelationData;
className?: string;
}) {
const { t } = useTranslations();
const hasResult = data.result != null && data.n >= 5;
return (
<Card data-slot="mood-correlation-card" data-kind={kind}>
<Card
data-slot="mood-correlation-card"
data-kind={kind}
className={className}
>
<CardHeader className="pb-2">
<div className="flex items-start justify-between gap-2">
<CardTitle className="text-sm font-semibold">
@@ -165,7 +171,15 @@ export function MoodCorrelationCards({
<MoodCorrelationCard kind="steps" data={steps} />
<MoodCorrelationCard kind="pulse" data={pulse} />
<MoodCorrelationCard kind="weight" data={weight} />
<MoodCorrelationCard kind="bloodPressure" data={bloodPressureSystolic} />
{/* Five fixed cards: on the two-up sm grid the trailing card lands
alone in its row, so it spans full width there; the three-up lg
grid leaves it paired (row of 3 + row of 2) and it stays one
column. */}
<MoodCorrelationCard
kind="bloodPressure"
data={bloodPressureSystolic}
className="sm:col-span-2 lg:col-span-1"
/>
</div>
);
}
@@ -14,6 +14,7 @@ import {
import type { ComponentType } from "react";
import { useTranslations } from "@/lib/i18n/context";
import { cn } from "@/lib/utils";
/**
* v1.8.6 — narrative takeaway feed.
@@ -69,7 +70,12 @@ export function MoodNarrativeFeed({ items }: { items: MoodNarrativeItem[] }) {
// ladder. Lay them two-up on anything wider than a phone; a lone
// takeaway still spans the row rather than orphaning half of it.
<ul
className="grid gap-2 sm:grid-cols-2"
className={cn(
"grid gap-2",
// A lone takeaway spans the row rather than orphaning half of it;
// two or more lay out two-up on anything wider than a phone.
items.length > 1 && "sm:grid-cols-2",
)}
data-slot="mood-narrative-feed"
aria-label={t("insights.mood.narrative.title")}
>
@@ -4,6 +4,7 @@ import { Activity, Gauge, Moon, Repeat, Target, Waves } from "lucide-react";
import { useInsightsAnalytics } from "@/hooks/use-insights-analytics";
import { useTranslations } from "@/lib/i18n/context";
import { cn } from "@/lib/utils";
import { SectionHeading } from "@/components/insights/section-heading";
import { DeviceScoreTile } from "@/components/insights/device-score-tile";
import { DeviceScoreGridSkeleton } from "@/components/insights/device-score-tile-skeleton";
@@ -111,7 +112,14 @@ export function SleepQualitySection({ enabled }: { enabled: boolean }) {
</p>
}
/>
<div className="grid gap-4 sm:grid-cols-2">
<div
className={cn(
"grid gap-4",
// Two-up only when more than one metric has data; a lone tile spans
// the full row rather than leaving a half-width orphan.
present.length > 1 && "sm:grid-cols-2",
)}
>
{present.map((tile) => (
<DeviceScoreTile
key={tile.type}
+9 -1
View File
@@ -15,6 +15,7 @@ import { formatDate } from "@/lib/format";
import { formatReferenceRange } from "@/lib/labs/reference-range";
import { formatLabReading, formatLabValue } from "@/lib/labs/format-value";
import { useTranslations } from "@/lib/i18n/context";
import { cn } from "@/lib/utils";
import { queryKeys } from "@/lib/query-keys";
import { applyOrder, useModuleListPrefs } from "@/lib/module-list-prefs";
@@ -247,7 +248,14 @@ export function LabList({ onAddFirst }: { onAddFirst?: () => void } = {}) {
// the line-1 chip, the panel as the category badge. v1.18.10 (#3) — no delete
// control on the tile; the trash icon lives only on the value detail view.
return (
<ul className="grid list-none gap-4 p-0 sm:grid-cols-2">
<ul
className={cn(
"grid list-none gap-4 p-0",
// A lone lab spans the full row rather than orphaning half of it;
// two or more fall into the two-up grid.
groups.length > 1 && "sm:grid-cols-2",
)}
>
{truncated ? (
<li className="sm:col-span-2">
<p className="text-muted-foreground text-xs">
@@ -36,9 +36,9 @@ export function InstrumentCard({
return (
<Card className="h-full gap-3" data-slot="instrument-card">
<CardContent className="flex h-full flex-col gap-4 p-4">
<CardContent className="flex h-full flex-col space-y-3.5 p-4">
<div className="flex flex-col gap-0.5">
<span className="font-medium">
<span className="text-lg leading-none font-semibold">
{t(`mentalHealth.instrument.${key}`)}
</span>
<span className="text-muted-foreground text-xs">