From be082b7bf905e29d26cebae730b2dd387fa9d059 Mon Sep 17 00:00:00 2001 From: nexxo Date: Mon, 27 Jul 2026 14:59:12 +0200 Subject: [PATCH] Stop iOS zooming the page on every tap into a field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Safari zooms when a focused input is smaller than 16px, and then leaves the page zoomed — so on an iPhone every form throws the layout about, and the way back is a pinch. The usual fix is the meta-viewport switch, which suppresses the zoom by disabling pinch-zoom entirely: it takes a real accessibility feature away from everyone to solve a cosmetic problem for some. Sizing the fields at 16px on touch devices removes the reason instead. !important on purpose: it has to beat every text-size utility on every field in the application, and a rule that loses to `text-sm` on one form is worth nothing. Checkboxes and radios are left alone — no text to inflate, and sizing them there breaks the box. Also -webkit-text-size-adjust, so iOS stops inflating body copy in landscape. Co-Authored-By: Claude Opus 5 --- resources/css/app.css | 65 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/resources/css/app.css b/resources/css/app.css index 88c0fdd..43c2caf 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -24,6 +24,8 @@ font-family: var(--font-sans); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; + /* Stops iOS inflating body text when the phone is turned to landscape. */ + -webkit-text-size-adjust: 100%; } body { @@ -51,6 +53,69 @@ } } +@layer components { + /* Registration marks — the corner crosses on a printed form. A pseudo + element rather than markup: they are decoration, and a screen reader + announcing "plus, plus" on every panel would be noise. Tailwind cannot + express ::before content, so this is the one place it lives in CSS. */ + .doc-marks { + position: relative; + } + + .doc-marks::before, + .doc-marks::after { + content: "+"; + position: absolute; + font-family: var(--font-mono); + font-size: 0.78rem; + line-height: 1; + color: var(--border-strong); + pointer-events: none; + } + + .doc-marks::before { + top: -6px; + left: -6px; + } + + .doc-marks::after { + bottom: -6px; + right: -6px; + } + + /* The rule that precedes an eyebrow label, in the accent. Same reasoning: + decorative, and it has to sit on the text baseline of a flex row. */ + .doc-eyebrow::before { + content: ""; + display: block; + width: 22px; + height: 1px; + background: var(--accent); + flex: none; + } +} + +/* iOS zooms the whole page when a field smaller than 16px takes focus, and + * then leaves the page zoomed — every tap into a form throws the layout about. + * + * There is no way to decline it: the meta-viewport switch that used to suppress + * it also kills pinch-zoom, which takes a genuine accessibility feature away + * from everyone to fix a cosmetic problem for some. Making the fields 16px on + * touch devices removes the reason instead. + * + * !important, and deliberately: this has to beat every text-size utility on + * every field in the application, and a rule that loses to `text-sm` on one + * form is worth nothing. Checkboxes and radios are excluded — they have no text + * to inflate and sizing them here would break their box. + */ +@media (pointer: coarse) { + input:not([type='checkbox']):not([type='radio']), + select, + textarea { + font-size: 16px !important; + } +} + @media (prefers-reduced-motion: reduce) { *, *::before,