diff --git a/src/app/globals.css b/src/app/globals.css index 4696df20e..20db09ff5 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -448,6 +448,21 @@ display: none; } +/* + * `.scroll-touch` — momentum scrolling for horizontal scrollers on iOS + * WebKit. `-webkit-overflow-scrolling: touch` restores the inertial + * flick the default `overflow-x-auto` row loses on touch; `overscroll- + * behavior: contain` keeps a flick that reaches the end of the row from + * chaining into the document scroll (the "rubber-band drags the whole + * page" paper-cut). Applied to the in-app horizontal pill / tab strips + * (insights tab strip, cycle tab list) so the gesture matches the + * native feel the bottom-sheet scrollers already get for free. + */ +.scroll-touch { + -webkit-overflow-scrolling: touch; + overscroll-behavior: contain; +} + /* ── Insight card animations ─────────────────────── */ @keyframes insight-fade-in { from { diff --git a/src/components/cycle/cycle-view.tsx b/src/components/cycle/cycle-view.tsx index 74e6b9bea..37562ae52 100644 --- a/src/components/cycle/cycle-view.tsx +++ b/src/components/cycle/cycle-view.tsx @@ -255,7 +255,7 @@ export function CycleView() { triggers size to their content (`text-xs`, no `flex-1`) and the strip scrolls cleanly if needed; from `sm` up they reclaim the equal-width `flex-1` layout. */} - + {t(ctaLabelKey)} ) : ( @@ -432,7 +432,7 @@ export function CoachInput({ data-slot="coach-input-send" aria-label={t("insights.coach.send")} title={t("insights.coach.send")} - className="size-9 shrink-0 rounded-xl" + className="size-11 shrink-0 rounded-xl sm:size-9" > {isStreaming ? ( - {rowContent} - + {rowContent} + ); } return ( -
{rowContent} -
+ ); } diff --git a/src/components/insights/insights-tab-strip.tsx b/src/components/insights/insights-tab-strip.tsx index 01577f8eb..fdcbda4b0 100644 --- a/src/components/insights/insights-tab-strip.tsx +++ b/src/components/insights/insights-tab-strip.tsx @@ -709,7 +709,7 @@ function InsightsTabStripImpl({ // gives that room back to the layout so the strip height is // unchanged. `px-1 -mx-1` does the same horizontally so the // first/last pill's focus ring isn't clipped at the row edges. - className="-mx-1 -my-1 flex min-w-0 flex-1 [scrollbar-width:none] gap-2 overflow-x-auto px-1 py-1 [&::-webkit-scrollbar]:hidden" + className="scroll-touch -mx-1 -my-1 flex min-w-0 flex-1 [scrollbar-width:none] gap-2 overflow-x-auto px-1 py-1 [&::-webkit-scrollbar]:hidden" > {tabs.map((tab) => { if (tab.kind === "link") { diff --git a/src/components/measurement-reminders/vorsorge-dashboard-card.tsx b/src/components/measurement-reminders/vorsorge-dashboard-card.tsx index 0c03f88ef..3d5396c8b 100644 --- a/src/components/measurement-reminders/vorsorge-dashboard-card.tsx +++ b/src/components/measurement-reminders/vorsorge-dashboard-card.tsx @@ -22,6 +22,7 @@ import { useTranslations } from "@/lib/i18n/context"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { ListRow } from "@/components/ui/list-row"; import { ResponsiveSheet } from "@/components/ui/responsive-sheet"; import { Skeleton } from "@/components/ui/skeleton"; import { MeasurementForm } from "@/components/measurements/measurement-form"; @@ -119,10 +120,12 @@ export function VorsorgeDashboardCard() { const due = relativeDueKey(reminder.nextDueAt, now); const isLinked = reminder.measurementType != null; return ( -
  • +
  • {resolveLabel(reminder, t)} @@ -139,8 +142,8 @@ export function VorsorgeDashboardCard() {

    -
  • + + ); })} diff --git a/src/components/measurement-reminders/vorsorge-section.tsx b/src/components/measurement-reminders/vorsorge-section.tsx index ca1763e05..ab129c9ed 100644 --- a/src/components/measurement-reminders/vorsorge-section.tsx +++ b/src/components/measurement-reminders/vorsorge-section.tsx @@ -794,8 +794,8 @@ function VorsorgeCard({