Until now every <ResponsiveSheet> consumer (except export-section) inlined the
form footer inside the body slot, so the primitive's sticky-pinned bottom rail
on the Sheet branch only painted empty. The Save / Cancel row therefore
scrolled with the body and slid out from under the soft keyboard on mobile.
This rewires the action-row through the footer prop on all seven page-level
mounts (dashboard quick-entry measurement + mood, /measurements, /mood,
/medications, side-effects sheet, inventory add sheet, intake-history edit +
create sheets, medication create/edit sheet). Each form receives an optional
footerSlot prop; when set, the kebab + Cancel + Save row is portalled into
the ResponsiveSheet footer slot so the bottom-sheet branch can sticky-pin it.
The Save button keeps its <form> association via the HTML form attribute so
submit-on-Enter and native validation continue to work end-to-end.
Move `<PasswordInput>` from `src/components/settings/password-input.tsx`
to `src/components/ui/password-input.tsx` so the auth surfaces (login
and register) and every settings card that masks a secret consume one
primitive. The two surfaces previously diverged on toggle markup and
aria-label wiring; the new home in `ui/` keeps them in lock-step.
While the primitive moves, lift the icon-only show/hide toggle to a
44 px hit area per WCAG 2.5.5 — `inline-flex h-11 w-11` wrapper with
the eye icon centred inside. The input itself gets `pr-12` so user
input never collides with the toggle glyph.
Consumer imports updated in:
- `src/components/settings/account-section.tsx`
- `src/components/settings/ai-section.tsx`
- `src/components/settings/integrations-section.tsx`
- `src/components/settings/ntfy-card.tsx`
- `src/components/settings/telegram-card.tsx`
- `src/components/admin/_shared.tsx`
The `<DialogContent>` close-X tap-target lift to `min-h-9 min-w-9`
landed in the parallel `ResponsiveSheet` commit because both buckets
share the same `dialog.tsx` file. See Decision I — 36 px is the
intentional exception for fixed icon-only close affordances.
W10 design-review carry-over items. M7 lifts the medication-form
day-of-week buttons, the more-horizontal trigger, and the schedule
label-input gaps to the 44 px / space-y-2 standard used by every
other settings surface. L1 pairs every animate-spin/pulse/bounce
with motion-reduce:animate-none across the component tree so users
with prefers-reduced-motion get a still surface. L3 adds the
aria-controls + panel-id pairing to the inventory + coach-evidence
<details> blocks for parity with the Health Score provenance
accordion. L4 inserts a sr-only h4 per therapy-timeline entry so
screen-reader users can jump between medication blocks.
Follow-up to 9a74f8e — that commit's pathspec didn't pick up the
implementation files, only the new test. This commit lands the actual
EmptyState wiring described in the previous message:
- src/components/measurements/measurement-list.tsx — Activity icon,
filter-aware empty state, optional onAddFirst CTA prop.
- src/components/mood/mood-list.tsx — Smile icon, mirroring filter
semantics + optional onAddFirst.
- src/app/measurements/page.tsx + src/app/mood/page.tsx — pass
onAddFirst so the empty-state CTA opens the same dialog as the
header button.
- src/app/medications/page.tsx — refactor inline icon+text+button
block to the EmptyState primitive (Pill icon).
- src/app/achievements/page.tsx — Trophy icon for the no-achievements
state, reusing the existing noneUnlockedYet string.
Together with 9a74f8e the four feature lists now share a consistent
empty-state surface with the rest of the app.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.