mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
fix(dashboard): flatten the hero band to match the tile + chart surfaces
The daily-verdict band wore a gradient surface and a health-score ring with a drop-shadow bloom, a green-band breathing pulse, a conic sheen, and a sweep entrance. It read louder than the chart cards and tiles it sits among. Drop the gradient: the band now wears the same bg-card + border + radius + padding as <TrendCard>, and the dose chip sits on a neutral muted surface (no card tint, blur, or shadow). Add a flat treatment to <ScoreRing> that suppresses the bloom, pulse, sheen, and entrance sweep, and pass it on the hero ring only. The wellness strip and anatomy view keep the full reveal. Reduced-motion handling is unchanged. Information is untouched — only the visual treatment changes.
This commit is contained in:
1 parent
a7b2b61b99
commit
651aa65c28
4 files changed
+51
-23
No files matched your search
@@ -705,6 +705,11 @@
|
||||
0 0 3px color-mix(in srgb, var(--ring-glow) 45%, transparent)
|
||||
);
|
||||
}
|
||||
/* Flat 2-D arc — no bloom, no pulse. The dashboard hero ring uses this so it
|
||||
reads as calmly as the chart + tile surfaces around it. */
|
||||
.wellness-ring-arc--flat {
|
||||
filter: none;
|
||||
}
|
||||
.wellness-ring-arc--pulse {
|
||||
/* Breathe a few times to celebrate a strong score, then settle — an
|
||||
infinite loop would repaint the (non-compositable) drop-shadow filter
|
||||
|
||||
@@ -361,7 +361,7 @@ describe("<DashboardHero> — verdict variants", () => {
|
||||
});
|
||||
|
||||
describe("<DashboardHero> — dose row", () => {
|
||||
it("prints the day tally with the v1.16.6 card chrome and the Pill icon", () => {
|
||||
it("prints the day tally with the neutral chip chrome and the Pill icon", () => {
|
||||
const html = render(
|
||||
baseSnapshot({
|
||||
medsToday: medsToday({ scheduledToday: 3, takenToday: 1 }),
|
||||
@@ -372,15 +372,13 @@ describe("<DashboardHero> — dose row", () => {
|
||||
/<div[^>]*data-slot="dashboard-hero-doses"[^>]*>/,
|
||||
);
|
||||
expect(doseRow).not.toBeNull();
|
||||
for (const cls of [
|
||||
"bg-card/65",
|
||||
"border-border/60",
|
||||
"rounded-xl",
|
||||
"shadow-sm",
|
||||
"backdrop-blur-sm",
|
||||
]) {
|
||||
// v1.18.1: chip sits on a neutral muted surface over the plain card —
|
||||
// no translucent card tint, no blur, no shadow (the gradient is gone).
|
||||
for (const cls of ["bg-muted/50", "border-border/60", "rounded-xl"]) {
|
||||
expect(doseRow![0]).toContain(cls);
|
||||
}
|
||||
expect(doseRow![0]).not.toContain("backdrop-blur");
|
||||
expect(doseRow![0]).not.toContain("shadow-sm");
|
||||
});
|
||||
|
||||
it("all doses resolved (taken + skipped) → 'Alle Dosen für heute erledigt'", () => {
|
||||
@@ -581,8 +579,17 @@ describe("<DashboardHero> — source pins", () => {
|
||||
expect(src).toMatch(/resolveDashboardVerdict\(snapshot,\s*new Date\(\)\)/);
|
||||
});
|
||||
|
||||
it("uses the hero-gradient chrome WITHOUT glow-purple", () => {
|
||||
expect(src).toContain("hero-gradient");
|
||||
it("wears the plain card surface — no gradient, no glow", () => {
|
||||
// v1.18.1: the band drops the gradient hero treatment and sits on the
|
||||
// same `bg-card` + border + radius as the surrounding chart cards.
|
||||
expect(src).toContain("bg-card");
|
||||
expect(src).toContain("border-border");
|
||||
expect(src).toContain("rounded-xl");
|
||||
expect(src).not.toContain("hero-gradient");
|
||||
expect(src).not.toContain("glow-purple");
|
||||
});
|
||||
|
||||
it("renders the score ring flat (no bloom / pulse / sheen / sweep)", () => {
|
||||
expect(src).toMatch(/<ScoreRing[^>]*\n(?:[^>]*\n)*?\s*flat\b/);
|
||||
});
|
||||
});
|
||||
@@ -15,8 +15,10 @@
|
||||
* band's anchor, larger and heavier than the verdict so the
|
||||
* personalised line is not drowned by the content below it), the
|
||||
* verdict sentence with its single CTA button, and the dose row;
|
||||
* - right column: the shared `<ScoreRing>` at `sm` geometry. A null
|
||||
* score renders the ring's provisional state at the identical
|
||||
* - right column: the shared `<ScoreRing>` at `sm` geometry, in its
|
||||
* `flat` treatment (no bloom / pulse / sheen / sweep) so the dial
|
||||
* sits as calmly as the surrounding chart cards. A null score
|
||||
* renders the ring's provisional state at the identical
|
||||
* 120 px footprint, so the column never collapses. The provisional
|
||||
* label is honest about WHY the score is null: when the snapshot
|
||||
* already carries score inputs (weight / BP summaries, mood
|
||||
@@ -220,11 +222,12 @@ export function DashboardHero({
|
||||
<section
|
||||
data-slot="dashboard-hero"
|
||||
className={cn(
|
||||
// `.hero-gradient` carries its own border tint; deliberately
|
||||
// no glow treatment — the band sits above the tile strip and
|
||||
// must read calmer than the /insights hero.
|
||||
"hero-gradient relative isolate overflow-hidden rounded-xl",
|
||||
"min-h-[8.75rem] px-4 py-5 sm:px-6 md:min-h-[9.5rem]",
|
||||
// The band sits in the same column as the chart cards + tile
|
||||
// strip, so it wears the same surface: plain `bg-card` with the
|
||||
// shared border + radius + padding (mirrors `<TrendCard>`). No
|
||||
// gradient, no glow — it reads as one of the dashboard tiles.
|
||||
"bg-card border-border relative isolate overflow-hidden rounded-xl border",
|
||||
"min-h-[8.75rem] p-4 md:min-h-[9.5rem] md:p-6",
|
||||
)}
|
||||
>
|
||||
<div className="flex h-full flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||
@@ -272,7 +275,7 @@ export function DashboardHero({
|
||||
</div>
|
||||
<div
|
||||
data-slot="dashboard-hero-doses"
|
||||
className="bg-card/65 border-border/60 inline-flex max-w-full items-center gap-2 rounded-xl border px-3 py-2 shadow-sm backdrop-blur-sm"
|
||||
className="bg-muted/50 border-border/60 inline-flex max-w-full items-center gap-2 rounded-xl border px-3 py-2"
|
||||
>
|
||||
<Pill
|
||||
className="text-muted-foreground h-4 w-4 shrink-0"
|
||||
@@ -302,6 +305,7 @@ export function DashboardHero({
|
||||
score={snapshot.healthScore?.score ?? null}
|
||||
band={snapshot.healthScore?.band}
|
||||
size="sm"
|
||||
flat
|
||||
label={
|
||||
snapshot.healthScore
|
||||
? t("dashboard.hero.scoreLabel")
|
||||
|
||||
@@ -77,6 +77,13 @@ export interface ScoreRingProps {
|
||||
* rings reveals left-to-right and each number trails its own arc.
|
||||
*/
|
||||
delayMs?: number;
|
||||
/**
|
||||
* Flat 2-D treatment: drops the drop-shadow bloom, the breathing pulse, the
|
||||
* conic sheen, and the sweep/count-up entrance. Used on the dashboard hero so
|
||||
* the ring sits as calmly as the surrounding chart + tile surfaces. The
|
||||
* /insights wellness strip + anatomy view keep the full premium reveal.
|
||||
*/
|
||||
flat?: boolean;
|
||||
/**
|
||||
* Optional "your normal" reference (0..100). When set + finite, a faint
|
||||
* thinner ghost arc renders under the live arc so the user sees today vs
|
||||
@@ -102,6 +109,7 @@ export function ScoreRing({
|
||||
hue,
|
||||
delayMs = 0,
|
||||
baseline,
|
||||
flat = false,
|
||||
className,
|
||||
}: ScoreRingProps) {
|
||||
const { t } = useTranslations();
|
||||
@@ -119,7 +127,9 @@ export function ScoreRing({
|
||||
const glow = RING_GLOW[hueKey];
|
||||
|
||||
const reduced = prefersReducedMotion();
|
||||
const shouldAnimate = animate && hasScore && !reduced;
|
||||
// `flat` forces the calm static treatment regardless of the `animate` prop:
|
||||
// no sweep, no count-up, no pulse, no sheen, no bloom.
|
||||
const shouldAnimate = animate && hasScore && !reduced && !flat;
|
||||
const dash = (clamped / 100) * C;
|
||||
|
||||
const hasBaseline = baseline != null && Number.isFinite(baseline) && hasScore;
|
||||
@@ -220,8 +230,9 @@ export function ScoreRing({
|
||||
{/* The progress arc — single-hue gradient, round cap, spring sweep, bloom. */}
|
||||
<circle
|
||||
className={cn(
|
||||
"wellness-ring-arc",
|
||||
shouldAnimate &&
|
||||
flat ? "wellness-ring-arc--flat" : "wellness-ring-arc",
|
||||
!flat &&
|
||||
shouldAnimate &&
|
||||
resolvedBand === "green" &&
|
||||
"wellness-ring-arc--pulse",
|
||||
)}
|
||||
@@ -241,8 +252,9 @@ export function ScoreRing({
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
{/* Conic specular sheen — sweeps the arc once on the strip reveal. */}
|
||||
<div className="wellness-ring-sheen" aria-hidden="true" />
|
||||
{/* Conic specular sheen — sweeps the arc once on the strip reveal.
|
||||
Omitted entirely in the flat treatment (dashboard hero). */}
|
||||
{flat ? null : <div className="wellness-ring-sheen" aria-hidden="true" />}
|
||||
{/* Centred number as real DOM text — design tokens + tabular-nums. */}
|
||||
<div className="pointer-events-none absolute inset-0 flex flex-col items-center justify-center">
|
||||
<span
|
||||
|
||||
Reference in new issue
Block a user