From 56a3008cf0a7c634ef9cd3fdbfd94ec4826fe025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 8 Oct 2026 17:23:57 +0200 Subject: [PATCH] fix(mobile): keep form fields at 16 px on every touch screen iOS and iPadOS Safari zoom the page into any focused field whose text is below 16 px. The fields switched to 14 px at a width breakpoint (`md:text-sm`, `sm:text-sm`), which a phone held sideways (844 px) and every iPad pass, and the native select was 14 px at every width, so the profile settings zoomed on a portrait phone too. A `pointer-fine:` variant (a mouse or trackpad is the primary pointer) now gates the compact size on Input, Textarea, NativeSelect, DateField, TimeField and the Coach composer; touch screens keep 16 px at any width and the desktop look is unchanged. --- src/app/globals.css | 14 ++++++++++++++ .../insights/coach-panel/coach-input.tsx | 2 +- src/components/ui/__tests__/textarea.test.tsx | 8 +++++--- src/components/ui/date-field.tsx | 10 ++++++---- src/components/ui/input.tsx | 2 +- src/components/ui/native-select.tsx | 5 ++++- src/components/ui/textarea.tsx | 13 ++++++++----- src/components/ui/time-field.tsx | 4 ++-- 8 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 91d72e44d..208d1ebd6 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -32,6 +32,20 @@ @custom-variant dark (&:is(.dark *)); +/* + * `pointer-fine:` — a mouse or trackpad is the primary pointer. Form fields + * take the compact 14 px type only under it: iOS and iPadOS Safari zoom the + * page into any focused field whose text is smaller than 16 px, and a width + * breakpoint alone (`md:text-sm`) handed 14 px to a phone held sideways + * (844 px) and to every iPad. Touch-first screens keep the 16 px base at + * every width. + */ +@custom-variant pointer-fine { + @media (hover: hover) and (pointer: fine) { + @slot; + } +} + @theme inline { /* Extra-small breakpoint (30rem / 480px) for the narrowest phones — the default scale starts at sm (640px), which leaves a gap between an iPhone SE diff --git a/src/components/insights/coach-panel/coach-input.tsx b/src/components/insights/coach-panel/coach-input.tsx index a6813f540..d651dd7d0 100644 --- a/src/components/insights/coach-panel/coach-input.tsx +++ b/src/components/insights/coach-panel/coach-input.tsx @@ -380,7 +380,7 @@ export function CoachInput({ autoCapitalize="sentences" rows={1} className={cn( - "order-1 w-full min-w-0 flex-1 resize-none bg-transparent text-base leading-relaxed outline-none sm:text-sm", + "order-1 w-full min-w-0 flex-1 resize-none bg-transparent text-base leading-relaxed outline-none sm:pointer-fine:text-sm", "px-2 py-1.5", "max-h-[9.5rem] overflow-auto", "[scrollbar-width:thin] [scrollbar-color:color-mix(in_srgb,var(--primary)_35%,transparent)_transparent]", diff --git a/src/components/ui/__tests__/textarea.test.tsx b/src/components/ui/__tests__/textarea.test.tsx index 6535a0b02..f8e6c4fc6 100644 --- a/src/components/ui/__tests__/textarea.test.tsx +++ b/src/components/ui/__tests__/textarea.test.tsx @@ -4,10 +4,12 @@ import { renderToStaticMarkup } from "react-dom/server"; import { Textarea } from "../textarea"; describe("