Skip to content

iPhone Chrome viewport and community keyboard follow-up

Follow-up to 2026-09-08-mobile-resume.md. Reports: the dark bottom strip still appears briefly on return from another app, and a much larger strip covers community replies while typing. Both screenshots also show the top bar missing, consistent with the app shifting upward.

Evidence

  • The previous controller assigned a keyboard-adjusted pixel height to html, body, #root, and the app shell. It kept that height while suspended. Its keyboard height also included visualViewport.offsetTop, mixing position with size.
  • overflow: hidden leaves those wrappers programmatically scrollable. Focus scrolling can therefore move the entire app. The old recovery deferred while a text entry was focused and otherwise used two animation frames; late geometry depended on 250ms/1000ms retries.
  • Read-only PostHog queries on September 8 found Chrome iOS community ui_recovery events correcting html.scrollTop by 79px (10:21 UTC), and keyboard height transitions from 766px to 427px (12:00 UTC). These establish that the geometry/scroll path occurs in production, not that a particular event is the reporting user's screenshot. No reply text or account data was needed.
  • Production /app/community returned HTTP 200, CF-Cache-Status: DYNAMIC, Cache-Control: no-store, must-revalidate, no-cache, and CDN-Cache-Control: no-store. No CDN purge or cache-policy change is indicated.
  • Browser references: overflow hidden versus clip, VisualViewport, and WebKit keyboard panning report. The last report documents a browser behavior, not a reproduction on this phone.

Changes

  • Keep document wrappers at their normal full height. On mobile, fix the body and app shell in place and use overflow: clip on outer wrappers where supported. Real page/chat/editor scroll containers retain their scrolling behavior. Existing hidden overflow is the compatibility fallback.
  • Use native viewport CSS when no text entry is focused; clear temporary keyboard height/origin on suspension and blur. This avoids retaining a short pixel height for the next return.
  • During text entry, use the visual viewport height exactly and apply its vertical offset as the shell's top, without a transform that would change fixed-position descendants' containing block. Safe area and browser chrome are no longer added to that height.
  • Install before initial paint and repair eligible stale outer scrolling synchronously on resume, retaining bounded settling retries for browser events delivered later. Keep the existing guards during typing, touch and zoom as a fallback for engines without clip support.
  • Opt the community scroller into focus visibility adjustment after viewport resize. Only that scroll container moves; the document, draft, focus, selection, and textarea's own scroll stay intact. Defer during active touch and preserve native caret scrolling in editors taller than the viewport.

Validation

  • Original targeted baseline: 25/25 viewport, overlay and route-restoration tests pass.
  • New regressions were observed failing before their fixes: idle pixel sizing, keyboard pan mixed into height, retained background height, delayed resume scroll repair, obscured community reply, lost touch-deferred reveal and oversized-editor scrolling.
  • Final targeted suite: 32/32 pass, including keyboard dismissal/orientation, normal scrolling, draft/focus preservation, cleanup, zoom and overlay behavior. Independent reviewer also ran all 23 viewport tests successfully.
  • Full app tests: 727/730 pass, retaining only the three existing mobile navigation drawer spacing failures. Those same failures were reproduced directly against unchanged main; no drawer sizes were changed.
  • Build and typecheck pass. The build retains existing large-chunk warnings.
  • No local browser was opened under AGENTS.md. jsdom tests exercise event/DOM state and supplied geometry; they cannot reproduce WebKit layout/compositor behavior or verify a physical iPhone.

Device confirmation

Load the new deployment once, open a long community thread, and try both a bottom reply and nested reply with the Chinese keyboard. Scroll with the keyboard open, dismiss it, and reopen it. Confirm the draft and caret survive, the reply remains reachable, and the top bar stays anchored. Repeat after switching apps with and without the keyboard open, and check chat/iframe input, dialogs, pinch zoom, orientation and browser Back. A browser-owned app-switch snapshot or compositor stall before JavaScript resumes cannot be eliminated or verified by these DOM changes.