Commit Graph
6 Commits
Author SHA1 Message Date
Marc-André Bombeck 1768b1d971 ux: i18n leaks, focus rings, tap-target floor, admin chip-scroll, empty-state CTA vocabulary
Eight hotfix-ready UX/a11y findings from the v1.4.34.3 UX audit
land as one batch.

i18n leaks (3 primitives):
  - Dialog + Sheet close-X carried hardcoded `<span sr-only>Close</span>`
    so a German user heard 'Close, button' on every modal. Both lift
    `useTranslations()` and read `common.close`.
  - ChartSkeleton's sr-only loading announcement was English-only.
    The component is already a client island; reads `charts.loadingLabel`
    from each locale now. New key shipped in DE / EN / FR / ES / IT / PL.
  - Notifications-section breadcrumb `<nav aria-label="Breadcrumb">`
    was hardcoded. Routes through `t("nav.breadcrumb")` with a new
    key in each locale.

Focus rings (mobile + tablet nav):
  - Bottom-nav primary links + the More overflow trigger relied on a
    `text-muted-foreground` → `text-primary` colour transition only;
    keyboard / switch-control users saw no focus indicator at all.
    Added `focus-visible:ring-ring/50 focus-visible:ring-2
    focus-visible:outline-none focus-visible:ring-offset-2` matching
    the v1.4.33 sidebar treatment.
  - Mobile top-bar trigger had `focus:outline-none` without a
    `focus-visible` replacement. Same treatment applied.

Tap-target floor (WCAG 2.5.5):
  - Input / NativeSelect / Select / DateTimeInput were locked to
    h-10 (40 px) on every viewport. Switched to `h-11 sm:h-10` so
    mobile clears the 44 px floor while desktop keeps the 40 px norm
    that the Button primitive already converged on in v1.4.33.

Admin chip-strip auto-scroll-into-view:
  - The admin shell shipped 13 chips but lacked the
    `scrollIntoView({inline: 'center'})` effect the settings shell
    got in v1.4.33. Admins tapping a chip past the right edge landed
    on the new page with the strip scrolled to the left and the
    active chip off-screen. Copied the settings-shell pattern verbatim.

List-page subtitles on mobile:
  - Measurements / Mood / Medications pages hid their descriptive
    subtitle on `<sm` with `hidden sm:block`. Comparable apps keep
    the subtitle on mobile because it doubles as contextual help.
    Dropped the hide rule; the H1 still anchors the visual.

Insights empty-state CTA vocabulary:
  - `ALLOWED_ADD_TYPES` on `/measurements` accepted tokens (`GLUCOSE`,
    `TEMPERATURE`, `HEART_RATE`, `BMI`) that didn't match the
    `MEASUREMENT_TYPES` enum the form uses. Several `/insights/*`
    empty-state CTAs emitted those mismatched tokens and the dialog
    silently failed to open. Replaced with a single source of truth
    derived from `MEASUREMENT_TYPES` plus a normalisation map for
    legacy tokens. Added BODY_TEMPERATURE to the form's enum row.
    New unit test walks every `?add=` token emitted under
    `src/app/insights/**` and asserts it resolves to a real form type.
2026-05-17 00:34:00 +02:00
Marc-André Bombeck 585637d5ab fix(layout): harden bottom-nav so it cannot overlap the last viewport line
The mobile bottom tab-bar bled into the bottom of the visible area on
/settings/account and /settings/ai per the runtime audit. The bar
previously rendered as bg-card/80 (80% opacity, translucent) and
relied on the inner flex row's h-16 alone for its height — without
a min-height floor on the outer nav, a future variant or any flex-
collapse quirk could let the surface shrink under the 64px floor the
auth-shell counts on.

Pin the bar at min-h-16 on the outer nav (defence in depth against
flex collapse) and switch the surface from bg-card/80 to a solid
bg-card. The safe-area inset and fixed bottom-anchor stay as they
were. Page shells that scroll content beneath the bar are still
responsible for reserving pb-16 + safe-area on their main column;
this change makes the bar itself robust so any future page-shell
oversight produces a hard occlusion (visibly broken on review)
rather than a translucent bleed (silently shipped). Bottom-nav SSR
test stays green (4/4) — five primary anchors, More button,
aria-haspopup, 44px tap-target floor.

Refs: .planning/round-v1433-audit-runtime.md F14
2026-05-16 17:12:34 +02:00
Marc-André Bombeck 072eee6997 fix(nav): bottom-nav 5+More overflow protects 44px tap targets
The 7-item bottom nav collapsed each touch target to ~50 px wide on
a 375 px viewport — only just-passing WCAG 2.5.5's 44 px floor and
visibly cramped on iPhone-13-mini-class screens. The mobile audit
(`.planning/phase-A5-mobile-findings.md`) recommended 5+More.

Primary strip keeps the four most-used surfaces plus Insights:
Home, Measurements, Mood, Medications, Insights. Targets and
Achievements move into a Radix `<Sheet>` triggered by a sixth
"More" entry. Order matches the v1.4.15 layout for the four core
items so muscle memory holds.

The More button mirrors the same `min-h-11 min-w-11` hit area;
`aria-haspopup="dialog"` + `aria-expanded` keeps screen-reader
behaviour tight; the sheet renders bottom-anchored with safe-area
padding so the inner links stay reachable above the iPhone home
indicator. Active styling lights the More entry whenever any of
its overflow children is the current route, so the user always
knows which strip-level link "owns" their position.
2026-05-10 00:39:59 +02:00
Marc-André Bombeck 3de2e37ed5 fix(ux): touch targets, a11y on phase config, admin label i18n
UX/a11y residuals from 2026-04-26 audit:

- bottom-nav: touch targets sized to WCAG 2.5.5 minimum (44x44 CSS px)
  via min-h-11 min-w-11 on each Link. Nav height bumped to h-16 to
  accommodate. Visual icon stays at 20px so design doesn't shift.
- phase-config-dialog: decorative coloured dot now aria-hidden because
  the redundant text label ("Green"/"Yellow"/...) already conveys the
  phase. Screen readers no longer announce a meaningless 'image' node.
- admin: 'Web Push' and 'Bug Report' labels were hard-coded English in
  StatusItem and SettingsToggle calls; now go through t() with new
  admin.integrationWebPush / admin.integrationBugReport keys (en + de).
- medication-card: false-positive en-CA / en-US locale audit findings
  documented inline — those are intentional locale-independent helpers
  (en-CA gives YYYY-MM-DD for day-comparison, en-US gives a parseable
  string for the toBerlinDate timezone hack), not user-facing display.
2026-04-27 00:28:28 +02:00
Marc-André Bombeck 0c0c345e82 i18n: migrate from German-first to English-first default locale
Switch defaultLocale from "de" to "en" and make English the code-level
language throughout. All ~145 hardcoded German strings translated:

- API error messages (52 routes) now return English
- Zod validation messages in English
- Classification/analytics functions return English categories
- Notification type labels in English
- Component strings use i18n t() calls (heatmap, mood chart,
  medication form, intake history, password strength, insights card)
- ~50 new i18n keys in both de.json and en.json

Route migration:
- /stimmung → /mood, /zielwerte → /targets (new pages)
- 301 redirects in proxy.ts for legacy URLs
- Sidebar + bottom nav updated

Bump version to 1.0.1.
2026-03-12 21:31:48 +01:00
Marc-André Bombeck 38309932f3 Initial release v1.0.0
HealthLog — personal health-tracking PWA with weight, blood pressure,
pulse, mood tracking, medication compliance, Withings integration,
doctor report PDF export, and Dracula-themed UI.
2026-03-10 20:14:33 +01:00