diff --git a/docs/ui-guidelines.md b/docs/ui-guidelines.md index 224a8c21d..1afa09ad3 100644 --- a/docs/ui-guidelines.md +++ b/docs/ui-guidelines.md @@ -48,7 +48,7 @@ Tailwind's default 4-pixel scale. Reach for these stops first: | ------------------------------------ | ------------- | ------ | | Inline gap between icon and label | `gap-1` | 4 | | Tight stack inside a card | `gap-2` | 8 | -| Default form-field stack | `space-y-3` | 12 | +| Label-to-field stack (form fields) | `space-y-2` | 8 | | Section block (heading + body) | `space-y-4` | 16 | | Page section separator | `space-y-6` | 24 | | Dashboard tile-row gap, hero spacing | `gap-4` | 16 | @@ -65,13 +65,18 @@ these classes first: | Use | Class | | ---------------------------------- | --------------------------------------- | | Page title (h1) | `text-2xl font-semibold tracking-tight` | -| Section heading (h2) | `text-lg font-semibold` | -| Card title | `text-base font-medium` | +| Section heading / section card | `text-lg font-semibold` | +| Tile / inner card title | `text-base font-semibold` | | Body text | `text-sm` | | Muted helper / metadata | `text-sm text-muted-foreground` | | Microcopy / footer | `text-xs text-muted-foreground` | | Tabular data (latency, large nums) | `font-mono tabular-nums` | +Card titles come in exactly two sizes: `text-lg font-semibold` for a +section-level card (one per page region) and `text-base font-semibold` +for tiles and inner cards. Don't introduce a third level — a smaller +heading is a sign the content wants a list row, not a card. + Never use `font-bold` for emphasis — bump to `font-semibold` instead and let the design system breathe. @@ -381,7 +386,16 @@ We aim for **WCAG 2.1 AA**, with the following non-negotiables: - Color contrast ≥ 4.5:1 for normal text, 3:1 for large text and UI components. Verified by `@axe-core/playwright` in CI on every page. - Touch targets ≥ 44×44 CSS px on bottom-nav and primary action buttons - (WCAG 2.5.5). + (WCAG 2.5.5). Two idioms implement the floor, and they are deliberate: + - **Form controls** (`Input`, `NativeSelect`, `SelectTrigger` default + size): `h-11 sm:h-10` is baked into the primitives — 44 px on + mobile, back to the 40 px desktop tier. Never re-add `min-h-11` on + top of these; it forces 44 px on desktop and breaks the row rhythm. + - **Buttons on dense touch surfaces** (medication detail, dashboard + quick actions): `min-h-11 sm:min-h-9` — 44 px mobile floor, + compact `sm` tier on desktop. Plain `min-h-11` without the `sm:` + reset is legacy spelling; don't introduce new ones, and align a + button to its direct neighbours when editing one. - Focus is always visible: shadcn's `focus-visible:ring` is preserved on every customized component. - Every interactive element has an accessible name. Icon-only buttons diff --git a/messages/de.json b/messages/de.json index 06d7c9a49..0703401ba 100644 --- a/messages/de.json +++ b/messages/de.json @@ -643,7 +643,7 @@ "slotNone": "Kein bestimmter Slot", "takenAtLabel": "Datum und Uhrzeit", "takenAtHint": "Standard ist jetzt; wähle ein früheres Datum und eine frühere Uhrzeit, um eine vergangene Dosis zu erfassen.", - "skippedLabel": "Als ausgelassen erfassen", + "skippedLabel": "Als übersprungen erfassen", "noMedications": "Lege zuerst ein Medikament an, dann kannst du Einnahmen dafür erfassen.", "cancel": "Abbrechen", "submit": "Einnahme erfassen", diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx index af8958252..99bf541ed 100644 --- a/src/app/auth/login/page.tsx +++ b/src/app/auth/login/page.tsx @@ -151,9 +151,9 @@ export default function LoginPage() { disabled={loading} > {loading && mode === "passkey" ? ( - + ) : ( - + )} {t("auth.loginWithPasskey")} @@ -173,7 +173,7 @@ export default function LoginPage() { size="lg" onClick={() => setMode("password")} > - + {t("auth.loginWithPassword")} ) : ( @@ -220,9 +220,9 @@ export default function LoginPage() { disabled={loading} > {loading && mode === "password" ? ( - + ) : ( - + )} {t("auth.login")} diff --git a/src/app/auth/register/page.tsx b/src/app/auth/register/page.tsx index 26150ab1f..417d5f5d3 100644 --- a/src/app/auth/register/page.tsx +++ b/src/app/auth/register/page.tsx @@ -191,9 +191,9 @@ export default function RegisterPage() { disabled={loading} > {loading ? ( - + ) : ( - + )} {t("auth.register")} diff --git a/src/app/bugreport/page.tsx b/src/app/bugreport/page.tsx index e00dd5930..0273dea5f 100644 --- a/src/app/bugreport/page.tsx +++ b/src/app/bugreport/page.tsx @@ -243,9 +243,9 @@ export default function BugReportPage() { `div.space-y-2 > Button > Loader-Circle`). */} {loading ? ( - + ) : ( - + )} {t("bugreport.submit")} diff --git a/src/app/insights/loading.tsx b/src/app/insights/loading.tsx index 5e2eabad1..1131ba611 100644 --- a/src/app/insights/loading.tsx +++ b/src/app/insights/loading.tsx @@ -8,29 +8,30 @@ * by less than a row. Pure server component — no client hooks, no data * fetch; Next.js renders it instantly while the segment streams. */ +import { Skeleton } from "@/components/ui/skeleton"; export default function InsightsLoading() { return (
-
-
+ +
{/* Chart card */}
-
-
+ +
{/* Assessment card */}
-
-
+ +
-
-
-
+ + +
); diff --git a/src/app/insights/scores/[metric]/page.tsx b/src/app/insights/scores/[metric]/page.tsx index 0aa922bf4..3e3c82932 100644 --- a/src/app/insights/scores/[metric]/page.tsx +++ b/src/app/insights/scores/[metric]/page.tsx @@ -96,7 +96,7 @@ export default function CompositeScorePage() { className="-ml-2 w-fit" > -