mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(cycle): restore the wellness-tile entrance so the ring renders
The wheel tile, phase-education card, and dashboard cycle tile each set both data-revealed and .wellness-tile-rise on the same node. The keyframe selector is [data-revealed="true"] .wellness-tile-rise (descendant combinator), so it never matched a same-node pair — the tiles stayed at the rise rule's opacity: 0, invisible but still boxed. Lift data-revealed onto a contents wrapper (the documented pattern from the cycle insight summary card) so the entrance plays and the signature ring paints.
This commit is contained in:
1 parent
f0614a8080
commit
3bbc0016d9
3 files changed
+64
-42
No files matched your search
@@ -92,23 +92,28 @@ export function CycleRingTile({ className }: { className?: string }) {
|
||||
const hue = PHASE_HUE[wheel.phase];
|
||||
|
||||
return (
|
||||
<Link
|
||||
href="/cycle"
|
||||
data-slot="wellness-cycle-tile"
|
||||
data-metric="CYCLE"
|
||||
data-phase={wheel.phase}
|
||||
data-revealed={play ? "true" : undefined}
|
||||
// Match the score `RingTile` chrome: the phase's `--tile-hue` low-opacity
|
||||
// mix over `--card` + the faint film grain (the `.wellness-tile` family),
|
||||
// with a calm once-per-session rise. The per-phase hue arc inside the
|
||||
// ring is the only saturated thing on the tile.
|
||||
style={{ "--tile-hue": hue } as React.CSSProperties}
|
||||
className={cn(
|
||||
"wellness-tile focus-visible:ring-ring flex flex-col gap-4 rounded-xl p-5 focus-visible:ring-2 focus-visible:outline-none",
|
||||
play && "wellness-tile-rise",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
// `data-revealed` must sit on an ANCESTOR of `.wellness-tile-rise` — the
|
||||
// keyframe selector is `[data-revealed="true"] .wellness-tile-rise`
|
||||
// (descendant combinator). Both on the same node never matched, leaving the
|
||||
// tile stuck at the rise rule's `opacity: 0`. The `contents` wrapper carries
|
||||
// the flag without adding a layout box.
|
||||
<div data-revealed={play ? "true" : undefined} className="contents">
|
||||
<Link
|
||||
href="/cycle"
|
||||
data-slot="wellness-cycle-tile"
|
||||
data-metric="CYCLE"
|
||||
data-phase={wheel.phase}
|
||||
// Match the score `RingTile` chrome: the phase's `--tile-hue`
|
||||
// low-opacity mix over `--card` + the faint film grain (the
|
||||
// `.wellness-tile` family), with a calm once-per-session rise. The
|
||||
// per-phase hue arc inside the ring is the only saturated thing.
|
||||
style={{ "--tile-hue": hue } as React.CSSProperties}
|
||||
className={cn(
|
||||
"wellness-tile focus-visible:ring-ring flex flex-col gap-4 rounded-xl p-5 focus-visible:ring-2 focus-visible:outline-none",
|
||||
play && "wellness-tile-rise",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="text-foreground flex items-center gap-2">
|
||||
<Droplets className="h-5 w-5 shrink-0" aria-hidden="true" />
|
||||
<span className="truncate text-base leading-none font-semibold">
|
||||
@@ -131,6 +136,7 @@ export function CycleRingTile({ className }: { className?: string }) {
|
||||
{t(`cycle.phase.${wheel.phase}`)}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -159,20 +159,29 @@ export function CycleView() {
|
||||
<div className="grid gap-6 lg:grid-cols-[minmax(0,22rem)_minmax(0,1fr)] lg:items-start">
|
||||
{/* Wheel + compact phase card — grouped so they stick together. */}
|
||||
<div className="flex flex-col gap-4 lg:sticky lg:top-6">
|
||||
{/* Wheel — the signature ring on the premium wellness-tile surface. */}
|
||||
{/* Wheel — the signature ring on the premium wellness-tile surface.
|
||||
`data-revealed` must sit on an ANCESTOR of `.wellness-tile-rise`
|
||||
— the keyframe selector is `[data-revealed="true"]
|
||||
.wellness-tile-rise` (descendant combinator). Both on the same
|
||||
node never matched, leaving the tile stuck at the rise rule's
|
||||
`opacity: 0` (invisible but still boxed). The `contents` wrapper
|
||||
carries the flag without adding a layout box. */}
|
||||
<div
|
||||
data-slot="cycle-wheel-tile"
|
||||
data-revealed={play ? "true" : undefined}
|
||||
style={
|
||||
tileHue
|
||||
? ({ "--tile-hue": tileHue } as React.CSSProperties)
|
||||
: undefined
|
||||
}
|
||||
className={cn(
|
||||
"wellness-tile flex flex-col items-center gap-3 rounded-xl px-6 py-6",
|
||||
play && "wellness-tile-rise",
|
||||
)}
|
||||
className="contents"
|
||||
>
|
||||
<div
|
||||
data-slot="cycle-wheel-tile"
|
||||
style={
|
||||
tileHue
|
||||
? ({ "--tile-hue": tileHue } as React.CSSProperties)
|
||||
: undefined
|
||||
}
|
||||
className={cn(
|
||||
"wellness-tile flex flex-col items-center gap-3 rounded-xl px-6 py-6",
|
||||
play && "wellness-tile-rise",
|
||||
)}
|
||||
>
|
||||
{loading ? (
|
||||
<div className="flex h-[220px] items-center justify-center">
|
||||
<Loader2 className="text-primary h-8 w-8 animate-spin motion-reduce:animate-none" />
|
||||
@@ -216,6 +225,7 @@ export function CycleView() {
|
||||
{t("cycle.ring.firstPeriodCta")}
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Phase-education card beneath the wheel — the single instance,
|
||||
|
||||
@@ -107,18 +107,23 @@ export function PhaseEducationCard({
|
||||
const phaseName = phase ? t(`cycle.phase.${phase}`) : null;
|
||||
|
||||
return (
|
||||
<section
|
||||
data-slot="cycle-phase-education"
|
||||
data-revealed={animate ? "true" : undefined}
|
||||
data-phase={phase ?? "none"}
|
||||
aria-label={t("cycle.phaseEducation.title")}
|
||||
style={{ "--tile-hue": hue } as React.CSSProperties}
|
||||
className={cn(
|
||||
"wellness-tile rounded-xl px-5 py-5",
|
||||
animate && "wellness-tile-rise",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
// `data-revealed` must sit on an ANCESTOR of `.wellness-tile-rise` — the
|
||||
// keyframe selector is `[data-revealed="true"] .wellness-tile-rise`
|
||||
// (descendant combinator). Both on the same node never matched, leaving the
|
||||
// tile stuck at the rise rule's `opacity: 0`. The `contents` wrapper carries
|
||||
// the flag without adding a layout box.
|
||||
<div data-revealed={animate ? "true" : undefined} className="contents">
|
||||
<section
|
||||
data-slot="cycle-phase-education"
|
||||
data-phase={phase ?? "none"}
|
||||
aria-label={t("cycle.phaseEducation.title")}
|
||||
style={{ "--tile-hue": hue } as React.CSSProperties}
|
||||
className={cn(
|
||||
"wellness-tile rounded-xl px-5 py-5",
|
||||
animate && "wellness-tile-rise",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{/* Zone 1 — eyebrow + phase name with a hue dot (never colour-only). */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Sparkles
|
||||
@@ -196,6 +201,7 @@ export function PhaseEducationCard({
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user