From 66816a4f889429ee0bcb3a64e317a483b7b469f4 Mon Sep 17 00:00:00 2001 From: Ulas Date: Thu, 16 Apr 2026 15:35:03 +0200 Subject: [PATCH] fix: resolve iOS PWA bottom space and keyboard zoom issues - pwa.css: body::after now uses var(--glass-bg) matching the nav's glass background exactly; z-index lowered to z-nav-1 so the nav renders on top in the overlap area (safe-area padding), removing the visible color mismatch that appeared as empty space - router.js: add iOS focusin/focusout handlers that temporarily set maximum-scale=1 on input focus to prevent WKWebView auto-zoom; restores original viewport content 150ms after blur so manual zoom remains available for accessibility --- public/router.js | 32 ++++++++++++++++++++++++++++++++ public/styles/pwa.css | 15 +++++++++------ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/public/router.js b/public/router.js index d6ba5e4..6d15ae4 100644 --- a/public/router.js +++ b/public/router.js @@ -589,6 +589,38 @@ if (window.visualViewport) { }); } +// -------------------------------------------------------- +// iOS PWA: Viewport-Zoom bei Tastatur-Erscheinen verhindern. +// iOS Safari/WKWebView zoomt ins Layout wenn ein Formularfeld fokussiert wird +// und stellt den Zoom nach Tastatur-Schliessen im Standalone-Modus nicht +// automatisch zurück → Menüpunkte verschwinden aus dem sichtbaren Bereich. +// +// Fix: maximum-scale=1 während des Focus setzt (verhindert Zoom), +// danach original Wert wiederherstellen (erhält manuelle Zoom-Möglichkeit +// für Barrierefreiheit). Nur auf iOS-Geräten aktiv. +// -------------------------------------------------------- +if (/iPhone|iPad|iPod/.test(navigator.userAgent)) { + const metaViewport = document.querySelector('meta[name="viewport"]'); + if (metaViewport) { + const originalContent = metaViewport.getAttribute('content'); + const noZoomContent = originalContent.replace(/maximum-scale=\d+/, 'maximum-scale=1'); + + document.addEventListener('focusin', ({ target }) => { + if (['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName)) { + metaViewport.setAttribute('content', noZoomContent); + } + }); + + document.addEventListener('focusout', ({ target }) => { + if (['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName)) { + // Kurze Verzögerung: iOS braucht ~150ms um Layout nach Tastatur- + // Schliessen wiederherzustellen, bevor scale zurückgesetzt wird. + setTimeout(() => metaViewport.setAttribute('content', originalContent), 150); + } + }); + } +} + // -------------------------------------------------------- // Initialisierung // -------------------------------------------------------- diff --git a/public/styles/pwa.css b/public/styles/pwa.css index 24b726c..a89c841 100644 --- a/public/styles/pwa.css +++ b/public/styles/pwa.css @@ -79,8 +79,11 @@ nav, /* iOS PWA: Schwarzen Streifen unter der Nav verhindern. * iOS reserviert den Home-Indicator-Bereich unterhalb des CSS-Viewports. - * Das ::after-Element bekommt denselben Blur+Hintergrund wie .nav-bottom, - * damit kein Farbunterschied zwischen Nav-Bar und Safe-Area-Bereich entsteht. */ + * body::after füllt diesen Bereich mit dem gleichen Glass-Hintergrund wie die Nav. + * z-index: z-nav - 1 (unter der Nav) - dadurch rendert die Nav immer oben. + * Im Überlappungsbereich (Nav-Padding = Safe-Area) liegt die Nav darüber; + * in einem eventuellen Spalt zwischen Nav-Unterkante und Displayrand liegt + * body::after sichtbar - optisch identisch mit der Nav. */ body::after { content: ''; position: fixed; @@ -88,9 +91,9 @@ nav, left: 0; right: 0; height: env(safe-area-inset-bottom, 0px); - background-color: color-mix(in srgb, var(--color-surface) 85%, transparent); - backdrop-filter: blur(16px) saturate(180%); - -webkit-backdrop-filter: blur(16px) saturate(180%); - z-index: var(--z-nav); + background-color: var(--glass-bg); + backdrop-filter: var(--blur-md) saturate(180%); + -webkit-backdrop-filter: var(--blur-md) saturate(180%); + z-index: calc(var(--z-nav) - 1); } }