fix(mobile): safe-area round 2, zoom-lock, edge-swipe back audit #2

Closed
thatumi wants to merge 0 commits from thatumi/fix/ios-safearea-2 into main AGit
Owner

Safe-area round 2: replaces round 1's blanket #root padding with per-surface insets - the app shell, every full-screen fixed surface and every top-anchored fixed/absolute element (modal X buttons, call screens, banners, menus) now carries its own env(safe-area-inset-) offset, while bottom insets remain only on genuinely bottom-fixed bars (mini call bar, verification toast, compose bar). Zoom-lock: body touch-action:manipulation plus an unlayered 16px input/textarea/select font-size rule that beats all Tailwind text- utilities, so iOS can neither pinch- nor focus-zoom. Edge-swipe back audit: the chat<->members swipe row now uses the same single-writer transform pattern as 8b3cc85, and the thread/threads/search panels gained the same drag-to-close swipe instead of silently exiting the room. Gate: build clean, vitest 452 passed (+1 skip), oxlint 0 errors, cap sync ios clean.

Safe-area round 2: replaces round 1's blanket #root padding with per-surface insets - the app shell, every full-screen fixed surface and every top-anchored fixed/absolute element (modal X buttons, call screens, banners, menus) now carries its own env(safe-area-inset-*) offset, while bottom insets remain only on genuinely bottom-fixed bars (mini call bar, verification toast, compose bar). Zoom-lock: body touch-action:manipulation plus an unlayered 16px input/textarea/select font-size rule that beats all Tailwind text-* utilities, so iOS can neither pinch- nor focus-zoom. Edge-swipe back audit: the chat<->members swipe row now uses the same single-writer transform pattern as 8b3cc85, and the thread/threads/search panels gained the same drag-to-close swipe instead of silently exiting the room. Gate: build clean, vitest 452 passed (+1 skip), oxlint 0 errors, cap sync ios clean.
Safe-area round 2 - replace round 1's blanket #root padding with
per-surface insets. The blanket approach padded the wrong things:
position:fixed overlays lay out against the viewport, not #root, so
modal X buttons and per-screen headers still poked into the Dynamic
Island; and the bottom blanket inset left dead space under scrolling
content.

- #root keeps only height:100dvh + the dark gutter background
- app shell (MainLayout) carries top/left/right env(safe-area-inset-*)
- every full-screen fixed surface insets itself: thread/threads-list/
  search panels, SettingsModalShell dialog (the user-settings modal's
  mobile X lives here), both call screens
- every top-anchored fixed/absolute element offsets itself with
  top: calc(<n>px + env(safe-area-inset-top)): Modal/DialogHost/
  OnboardingGate/KeyboardShortcuts/VerificationOverlay overlays, the
  call screens' minimize button, lightbox actions, the chat
  notification-mode menu, login theme toggle, incoming-call +
  group-call banners; ContextMenu clamps to a probe-measured
  safe-area top so a long-press menu can't open under the notch
- bottom insets only on genuinely bottom-fixed interactive bars:
  mini call bar, verification toast, compose bar - nothing else keeps
  a bottom inset

Zoom-lock (no pinch zoom, no input-focus zoom):
- viewport-fit=cover already present (round 1)
- body { touch-action: manipulation } kills double-tap zoom
- unlayered input/textarea/select { font-size: 16px } beats every
  layered Tailwind text-* utility, so no field can sit under the
  16px threshold that makes iOS zoom on focus

Edge-swipe back audit:
- chat<->members row moved to the same single-writer pattern as the
  list<->chat row (fresh store reads, layout-effect sync, inline
  transform removed from the style prop) - the stale-rest snap class
  of bug fixed in 8b3cc85 can't recur there
- thread/threads-list/search panels gained the same drag-to-close
  swipe; before, a swipe on those screens invisibly drove the covered
  list<->chat row and on release dumped back to the room list,
  skipping the chat

Gate: npm run build clean; vitest 452 passed + 1 skipped; oxlint
0 errors (baseline warnings unchanged); npx cap sync ios clean
(sync outputs are gitignored under apps/web/ios).

Co-Authored-By: GLM <noreply@z.ai>
chore(hub): status card - safe-area round 2, zoom-lock, swipe audit
All checks were successful
CI / build-and-test (pull_request) Successful in 51s
CI / e2e (pull_request) Successful in 2m54s
e3c96a72fc
Round 23 highlights (per-surface insets, self-offsetting top elements,
bottom insets on fixed bars only, zoom-lock, panel swipe-back), the
Node 26 / jsdom localStorage workaround in risks, and next items
refreshed for the post-merge state.

Co-Authored-By: GLM <noreply@z.ai>
claude closed this pull request 2026-10-11 18:14:43 +00:00
Contributor

Closing: this branch's diff against its own merge-base with main is empty - its content is already in main. No merge needed. (Closed ahead of a git-history cleanup before making the repo public.)

Closing: this branch's diff against its own merge-base with main is empty - its content is already in main. No merge needed. (Closed ahead of a git-history cleanup before making the repo public.)
All checks were successful
CI / build-and-test (pull_request) Successful in 51s
CI / e2e (pull_request) Successful in 2m54s

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
thatumi/JorKonvo!2
No description provided.