fix(demo-wallet): mobile layout, safe-area, viewport height, bottom-sheet keyboard/scroll#487
fix(demo-wallet): mobile layout, safe-area, viewport height, bottom-sheet keyboard/scroll#487lesyuk wants to merge 2 commits into
Conversation
…heet keyboard/scroll A set of linked mobile-web (iOS Safari + Android Chrome) layout and bottom-sheet fixes for the wallet demo. Desktop and the extension popup are unchanged. Safe-area & viewport: - Add viewport-fit=cover so env(safe-area-inset-*) resolves to real values on mobile (0 on desktop, no visual change). - Reserve bottom clearance for the pinned actions in the onboarding/auth layout (CenteredScreen): max(3rem, 1.5rem + env(safe-area-inset-bottom)). The inset alone is 0 under Android 3-button navigation while its ~48dp bar still overlaps the viewport, which clipped the Continue / Reset Wallet / terms line; the base clears that bar while gesture/iOS/desktop keep a normal margin. - Use min-h-dvh (was 100vh) for the dashboard layout so it fills the viewport on iOS Safari, whose dynamic toolbars are excluded from 100vh. dvh degrades to vh where unsupported, so desktop/Android are unchanged. Pinned top bar (new PinnedHeader): - Pin the top nav so its controls (back button on sub-screens; Connect-to-dApp, wallet switcher, settings on the dashboard) stay reachable while the page scrolls or the keyboard is open. Also pin the onboarding back button. - On mobile the header is position:fixed (not sticky) with a measured flow spacer: when a vaul bottom sheet opens on iOS Safari it locks the body with position:fixed; top:-scrollY, which detaches a sticky header (it drops out for the sheet's lifetime and snaps back late). A viewport-anchored fixed header is immune and stays painted, dimmed under the overlay. Desktop keeps sticky (Radix Dialog locks scroll via overflow:hidden, so sticky is fine and avoids a reflow). - Top padding is a small fixed value, not env(safe-area-inset-top): in a browser tab that inset tracks the address bar showing/hiding, producing a large fluctuating gap above the bar on both platforms. On-screen Return key (setup-password / unlock): - Wrap the password inputs in a <form> whose onSubmit mirrors the Continue/Unlock button (guarded by the same validity condition, so invalid input is a no-op) and set enterKeyHint for the right on-screen key label. A visually-hidden submit button is what makes iOS Safari's Return key fire submission; the visible buttons keep their existing handlers, no submit logic duplicated. Connect-to-dApp sheet keyboard handling (the only sheet with a text input): - vaul's repositionInputs mutates the sheet height/offset from visualViewport resize events and misfired: fly-off on the first iOS focus, shove-up on Android. Disable it for this sheet on both platforms and drive the keyboard lift manually via a visualViewport-based bottom offset (useKeyboardInset), so the compact, content-height sheet -- input and Connect button -- rests directly above the keyboard. bottom is orthogonal to vaul's transform, so there is no fight. - Standard (input-less) sheets set noBodyStyles so vaul's iOS body scroll-lock doesn't break the now-fixed header; the iOS input sheet keeps the lock (so focusing the textarea can't scroll the document and drag the fixed sheet away) and restores the page scroll offset on close. Scoped via a keyboardSafe opt-in on the input sheet; other sheets and the desktop Dialog are unaffected.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 49 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (10)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
prettier/prettier fixes flagged by CI lint (formatting only, no behavior change): modal.tsx keyboard-inset ternary and connect-dapp-modal Modal.Container props.
Summary
Mobile-web layout, safe-area, viewport-height and bottom-sheet keyboard/scroll fixes for the wallet demo. Every change is behind a mobile breakpoint or an opt-in prop; desktop and the extension popup are unchanged.
Motivation / Bug
On-device testing (iOS Safari + Android Chrome) surfaced several mobile-only issues:
CenteredScreenfooter used a fixedpb-6; Android 3-button mode reportsenv(safe-area-inset-bottom)=0while its ~48dp bar still overlaps the viewport, andenv()didn't resolve at all withoutviewport-fit=cover.100vh, which excludes iOS Safari's dynamic toolbars, leaving a gap.body { position: fixed; top: -scrollY }; aposition: stickyheader is anchored to its scroll container, and a fixed<body>is no longer that container, so the header detaches.env(safe-area-inset-top), which in a browser tab (not a standalone PWA) tracks the address bar showing/hiding — a large, moving gap on both platforms.<form>, so iOS Safari's Return never submitted).repositionInputsmutates the sheet height/offset fromvisualViewportresize events: the sheet flew off-screen on the first iOS focus and got shoved up on Android; the dashboard could also bleed through above it.What changed
viewport-fit=coverinindex.html; theCenteredScreenfooter reservesmax(3rem, 1.5rem + env(safe-area-inset-bottom)); the dashboard layout switched tomin-h-dvh.apps/demo-wallet/src/core/components/shared/pinned-header/) — new top bar:position: fixedon mobile with a ResizeObserver-measured flow spacer,md:stickyon desktop; a small fixed top pad instead of the top inset.NewLayoutand the onboarding back button use it.setup-password-screen,unlock-screen) — inputs wrapped in a<form>whoseonSubmitmirrors the primary button (same validity guard),enterKeyHintset, plus a visually-hidden submit button (what actually makes iOS Safari's Return submit).modal.tsx,connect-dapp-modal.tsx,is-ios.ts) — akeyboardSafeopt-in: disable vaulrepositionInputson both platforms and lift the compact sheet with a manualvisualViewport-basedbottomoffset (useKeyboardInset) so the input and Connect button rest directly above the keyboard. Input-less sheets setnoBodyStylesso the scroll-lock can't break the now-fixed header; the iOS input sheet keeps the lock and restores scroll on close.Why this approach
fixed) header is the only thing immune to vaul'sposition:fixedbody lock; sticky provably detaches. Desktop keeps sticky because Radix Dialog locks scroll viaoverflow:hidden(not a fixed body), so the problem doesn't occur there and we avoid a fixed-vs-flow reflow on the wide layout.repositionInputs:repositionInputsis what caused the fly-off/shove; abottomoffset is orthogonal to vaul's transform-based open/close/drag, so the two don't fight, and the resize stream supplies smooth intermediate heights.env(safe-area-inset-top): that inset is address-bar-coupled in a browser tab, which is exactly the fluctuation observed.How it was verified
pnpm --filter demo-wallet typecheck(tsc, clean).Scope / safety
Every change is mobile-only (breakpoint- or
visualViewport/env()-guarded) or opt-in (keyboardSafe). The desktopDialogpath and the extension popup entry are untouched. No store or business logic changed.Follow-ups
Related: #485 (demo-wallet e2e). It should be rebased on top of these fixes before it merges.