iOS focus-zoom guard in @media (pointer: coarse) is beaten by any Svelte scoped font-size; needs !important
iOS Safari / WKWebView (iOS 26, Capacitor 8.5 shell; same on iOS 17+) zooms the page when a focused text control has a computed font-size under 16px. To stop it app-wide I added, in the global stylesheet: @media (pointer: coarse) { input:not([type=radio]):not([type=checkbox]):not([type=file]), textarea, select { font-size: max(16px, 1em); } }. Most inputs stopped zooming, but one inline name-edit input inside a Svelte 4.2 component (which has .edit input { font-size: 14px } in its <style> block) still zoomed on tap in the app and getComputedStyle(input).fontSize read 14px under an emulated coarse pointer. There is no error: the media query itself was matching (other controls read 16px). Tried moving the global rule later in the cascade and adding an html prefix; no change.
Root cause: specificity, not cascade order. Svelte scopes component styles by appending a generated class to every selector, so .edit input { ... } compiles to something like .edit.svelte-abc123 input.svelte-abc123 (0,2,1 or higher), which outranks the global input:not([type=radio])... selector no matter where it sits in the sheet; @media adds no specificity. Any component-scoped rule with a font-size (the same applies to CSS Modules, styled-components, Vue scoped styles) silently defeats a global focus-zoom guard.
Fix: make the guard !important, which is the one legitimate use here since the rule is a device-level invariant, not styling:
/* iOS zooms the page when a focused control's font is under 16px.
* !important: a component's scoped rule otherwise outranks this. */
@media (pointer: coarse) {
input:not([type='radio']):not([type='checkbox']):not([type='file']),
textarea,
select {
font-size: max(16px, 1em) !important;
}
}max(16px, 1em) keeps larger controls at their own size. Do not reach for maximum-scale=1 / user-scalable=no in the viewport meta: it disables pinch zoom for everyone on the web build. Verify with Chrome/puppeteer touch emulation (isMobile: true, hasTouch: true makes (pointer: coarse) match; chrome-headless-shell 151) and read getComputedStyle(el).fontSize after focusing: it must be 16px.