From 197ecd4c74f5534ef034c7f00efdb1cd59bbb83c Mon Sep 17 00:00:00 2001 From: Florent Tapponnier Date: Tue, 2 Jun 2026 14:59:51 +0200 Subject: [PATCH] fix: keep sticky header on regular mobile, only drop it in iOS WebViews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous PR (#267) dropped sticky on all mobile to dodge the iOS 26 WebKit jitter bug. Overkill — the symptom only manifests visibly inside in-app WebViews (Telegram, Instagram, Facebook, Twitter, TikTok) where the host's own URL-bar overlay animates on top of WebKit's misposition. Regular mobile Safari and Chrome iOS render sticky cleanly enough for end users. Mechanism: - Inline script in layout.tsx head detects iOS WebView via UA (no `Safari/` token on an iOS device = WKWebView in-app host) and adds the `ios-webview` class on before React hydrates. - globals.css scopes a non-sticky override to that class on the site header at < md viewports. - site-header.tsx exposes a stable identifier class (`site-header-root`) and stays sticky-top-0 by default everywhere else. No flash on first paint because the class lands during the same render tick as the dark-mode detection script next to it. Build + typecheck clean. --- src/app/globals.css | 14 ++++++++++++++ src/app/layout.tsx | 12 ++++++++++++ src/components/site-header.tsx | 18 ++++++++---------- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 6a6bda59..b0c43984 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -338,3 +338,17 @@ html { html { overflow-x: clip; } + +/* iOS in-app WebView (Telegram, Instagram, Facebook, Twitter, TikTok) + downgrade only: drop sticky on the site header so the iOS 26 WebKit + jitter bug (bugs.webkit.org/297779) cannot expose a gap at the top. + The `ios-webview` class is set by an inline script in layout.tsx + based on userAgent (iOS device with no `Safari/` token = WebView). + Regular mobile Safari / Chrome iOS keep sticky because the symptom + does not visibly trigger there. Desktop is sticky everywhere. */ +@media (max-width: 767px) { + html.ios-webview .site-header-root { + position: relative; + top: auto; + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 6ae2e892..84324b72 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -124,6 +124,18 @@ export default function RootLayout({ __html: `(function(){try{var t=localStorage.getItem('ocb-theme');var d=t==='dark'||(!t&&window.matchMedia('(prefers-color-scheme: dark)').matches);if(d)document.documentElement.classList.add('dark');}catch(e){}})();`, }} /> + {/* Detect iOS in-app WebView (Telegram, Instagram, FB, Twitter, etc.). + These hosts hit the iOS 26 WebKit fixed/sticky jitter bug + (bugs.webkit.org/297779) harder than regular Safari because their + own URL-bar overlay animates the layout viewport on top of the + WebKit regression. CSS below downgrades the site header to + non-sticky only when this class is present. Inline so the class + is on the html element before React hydrates — no flash. */} +