The medication cards stamped the upcoming schedule dose with Tailwind
stock text-purple-400, which reads ~3.1:1 on the white Alucard light
card and fails WCAG AA for body text. Add a theme-aware --dose-accent
token: the exact text-purple-400 oklch in dark mode (so the dark card
stays pixel-identical) and a darker AA-safe purple on the light card,
mirroring how the semantic feedback tokens land each tone above AA in
both themes. The cards switch to text-dose-accent in the follow-up
commit.
- Range pills: make the radiogroup a single tab stop with roving tabindex and
Arrow / Home / End key handling, matching the WAI-ARIA radiogroup pattern.
Previously every pill was its own tab stop and the arrow keys were dead.
- Range delta caption: render a neutral dash + "no change" for a perfectly
stable metric instead of a down-arrow with "−0%". Adds the `noChange`
string across all six locales.
- Scrollbar gutter: scope the document-level `scrollbar-gutter: stable` to the
body-scrolled shells (login / register / onboarding / legal) via a
`data-scroll` attribute the shell sets, so the height-locked authenticated
branch no longer reserves a second, never-painted gutter on classic
scrollbar platforms.
- Mood tiles: a lone in-target / stability tile now spans full width instead
of leaving a half-width orphan beside it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit 12b434a0c262e29ced1ac0aaf631db6678c3ad05)
The authenticated shell scrolls inside its own `<main>` (the body is height-
locked with `flex h-dvh`), and that `<main>` centres its content with
`mx-auto`. A tall section paints the vertical scrollbar and narrows the
content box; a short one hides it and widens the box, so the whole column —
including the admin/settings sidebar — recentres a few px sideways on every
view switch. The admin "Insights Quality" ↔ "Integrations" toggle was the
visible case.
Reserve the gutter with `scrollbar-gutter: stable` on the `<main>` scroll
viewport, and on `html` for the body-scrolled login / onboarding / standalone
pages. The content box width is now constant whether or not the bar is
painted, so the left nav no longer shifts when a scrollbar appears. The fix is
global: every signed-in route renders inside the same `<main>`, so the same
shift on settings, insights, and dashboard navigation is fixed at once. The
height-locked authenticated body never scrolls the document, so no double
gutter results.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit c1459e5ee42b7f7acbfa933a9eb7d1f5a1c6890a)
The status pill painted full-saturation Dracula green/orange/red text on
a 12-14% same-hue tint. That fails WCAG AA (4.5:1) for the out tone on
the muted insights reference panel in dark mode (3.87:1) and for every
tone on a white card in light mode (1.2-2.7:1), since the pill used the
raw Dracula palette rather than the theme-aware feedback tokens.
Switch the pill to dedicated theme-aware tokens (status-pill-in/near/out)
and a 10% tint: dark mode keeps Dracula green/orange and lifts the out
tone off #ff5555 to a lighter red; light mode adopts the darker
AA-corrected family already used for the chart and destructive colours.
All three tones now clear 4.5:1 on both bg-card and bg-muted/30 in both
themes (worst case 4.62:1).
Largest feature delta in the v1.4.x line. Insights expands into seven dedicated metric sub-pages; GLP-1 medication tracking lands end-to-end (drug-level chart with Research Mode acknowledgment, EMA titration ladder, cadence + compliance, side-effect taxonomy, pen/vial inventory); cross-source priority becomes a two-axis resolver; per-user timezone threads through ten analytics surfaces; Withings activity + sleep v2 sync; onboarding rebuilt as a nested-route wizard; Personal Records ship end-to-end; Health Score provenance accordion; Coach runs native first-party prompts across six locales with a 1800-assertion refusal-probe matrix; OpenAPI drift gate hard-fails; multi-arch GHCR image. Migrations 0043 through 0060 are additive and forward-only. PROMPT_VERSION 4.24.0 → 4.25.0 with GROUND RULE 9 (refuses dose recommendations) and GROUND RULE 15 (refuses drug-level estimates with MDR + MDCG 2021-24 cites).
See CHANGELOG.md for the full per-section delta.
Maintainer-voice sweep across `src/`. ~190 references to the
maintainer by first name in code comments and JSDoc neutralised to
"the maintainer", "the live tenant", "users", or "the user" depending
on context. Three sed passes covering ~50 distinct patterns
(`Marc reported`, `Marc wants`, `Marc kept`, `Marc rolled back`, etc.).
Test fixtures (`displayName: "Marc B."`, `username: "marc"`,
`<HeroStrip userName="Marc">`) kept as opaque test data per the brief.
CHANGELOG normalisation. The v1.4.14 + v1.4.15 entries paired a
German bullet with its English translation by historical design — the
in-app copy is English-default and the user docs are English, so the
mixed-language style was a holdover from the early-marathon notes.
A one-off node script extracted the `_..._` English paragraph from
each bilingual bullet and rewrote the entry as that text. ~44 bullets
reduced; one Docs bullet that lost a leading word in the regex
fixed by hand.
CLAUDE.md → CONTRIBUTING-AI.md rename so the filename is no longer
AI-vendor-specific. `CONTRIBUTING.md` reference updated. `AGENTS.md`
stays in place for multi-agent compatibility (the conventions doc is
shared with Codex / Cursor / etc.).
Wave-4 state wrap. STATE.md now records C1–C5 + D + D2 as complete;
v1421-backlog.md grew a "Closed in v1.4.22" header listing the 20
items the marathon landed; `.planning/phase-W4-report.md` documents
the per-commit decisions, test deltas, and the 23 items deferred to
v1.4.23 with reasoning.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New `src/components/insights/hero-strip.tsx` — replaces the v1.4.16
`<InsightsPageHero>` band with the wider strip from the v1.4.20
redesign:
- locale-aware time-of-day greeting (morning / afternoon / evening
/ night → 4 i18n keys, with "night" mapping to "Good evening" in
both locales since 03:00 + Guten Morgen reads odd)
- briefing-paragraph subtitle (falls back to a generic line when
no briefing is cached)
- personal-baseline + 'Generated <relative-time>' meta band
- 3-button action row: weekly-report (disabled, B4), ask-the-coach
(disabled, B2), re-run analysis (wired through onRegenerate).
Disabled buttons surface a 'Coming soon' tooltip via title= so
we don't drag in a Radix tooltip provider for static labels.
- <SuggestedPrompts> chip strip below the action band; onPickPrompt
is a TODO until B2 lands the Coach drawer (parent passes a no-op)
New CSS utilities in `globals.css`:
- .hero-gradient — Dracula purple → pink linear gradient over card
- .glow-purple — soft purple aura via box-shadow
- @keyframes pulseDot + .pulse-dot — for the future "live" dot
affordance; reduced-motion users get a static dot
Old `<InsightsPageHero>` stays in place (used by the dashboard
preview) and gains a JSDoc @deprecated note pointing to the new
component. EN + DE translations added in lockstep.
Test suite: +22 specs covering greeting buckets per locale + per
hour, paragraph fallback, disabled-button affordance, regenerate
wiring, suggested-prompts mount.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Production probe at /admin/api-tokens on a Pixel-5 viewport pinned the
scrollbar that kept showing up after two earlier fixes: it lives in
the AdminShell mobile section strip (13 entries, scrollWidth ~1700 vs
clientWidth ~360), not in the api-tokens table. The strip sits right
above the api-tokens card, which is why every previous attempt
targeted the table while the bar stayed put.
Adds a `no-scrollbar` utility in globals.css (scrollbar-width: none +
::-webkit-scrollbar { display: none }) and applies it to the strip in
both AdminShell and SettingsShell. Swipe and keyboard-arrow scrolling
keep working; the painted bar simply doesn't draw.
New AdminShell unit suite locks in the class so a future refactor
can't silently regress it; settings-shell test gets the matching
assertion. The api-tokens e2e spec gains a regression check that the
strip declares `no-scrollbar` and overflow-x-auto together.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the v1.4.6 deferred polish item — feedback / mood / medication
status indicators now ask for *meaning* (success / warning / info)
instead of hard-coding `text-dracula-green` / `bg-dracula-orange/15`.
Adds three CSS aliases to `globals.css`:
--success → dracula-green (positive outcome)
--warning → dracula-orange (caution / attention)
--info → dracula-cyan (neutral informational)
Dark mode (Dracula): each alias resolves to the same hex as before, so
the visual is pixel-identical to v1.4.6 — what changes is the *intent*
of the class.
Light mode (Alucard): the raw Dracula greens and oranges fail AA
contrast on a white card. The new tokens get higher-contrast
counterparts (#14720a, #a34d14, #036a96) that match the existing
`--destructive` / `--chart-*` light-mode swap.
Migrated callers:
- `src/components/admin/feedback-inbox-section.tsx`:
- BUG category badge → `destructive` (semantic — bug *is* broken)
- "published to GitHub" badge → `success`
- `src/components/medications/medication-card.tsx`:
- in-window status → `success`
- very-overdue status → `warning`
- "late" intermediate stays on `dracula-yellow` (yellow doesn't
map to either semantic bucket — it's mid-warning)
- `src/components/settings/ai-section.tsx`:
- "ChatGPT connected" badge + success toast → `success`
- "Connection expired" badge + raw-data warning → `warning`
Charts (`src/components/charts/**`) untouched per
`feedback_charts_visual_identity.md`. Other dracula-token callsites in
settings/* (telegram, ntfy, web-push, account, about, test-connection)
are deferred to v1.5.1 — broader sweep, not in tonight's scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related visual fixes for the dashboard tile strip and every
secondary string in the app:
- TrendCard now fills its grid cell. As a flex-child the card was
taking content width, which made tiles in the same row 122-166 px
wide on a 375 px viewport instead of even shares of the row.
Adding `w-full` to the wrapping `flex` element gives every tile
the same horizontal share that the parent grid hands out.
- `--muted-foreground` was identical to `--foreground` in both
themes, so labels, "Ø7d:" prefixes, units, and inactive nav items
all rendered at the same brightness as primary text. Dark theme
now uses `#9aa3b3` (Dracula comment), light uses `#5b6273` —
WCAG-AA contrast against `--background` and `--card` holds at
≥ 4.5:1 in both modes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace hardcoded hex color values with Dracula CSS var tokens
(text-dracula-purple, text-dracula-green, etc.) for theme consistency.
Add prefers-reduced-motion support for insight animations, aria-expanded
on the data quality toggle, and extract nested ternary into lookup map.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New insight card components for the health advisor feature:
- InsightAdvisorCard: full structured display with primary recommendation,
findings, correlations, recommendations, and collapsible data quality
- InsightStatusCard: compact per-metric status card with classification indicator
- Subtle fade-in animation for insight cards in globals.css
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HealthLog — personal health-tracking PWA with weight, blood pressure,
pulse, mood tracking, medication compliance, Withings integration,
doctor report PDF export, and Dracula-themed UI.