fix: keep iOS UI inside the visual viewport#19
Open
goyamegh wants to merge 1 commit into
Open
Conversation
Fixes a few mobile/iOS layout problems visible on iPhone Safari: - Status bar's right-most buttons (session bucket flag, settings gear) could be pushed off-screen on narrow viewports because the cwd path refused to shrink enough. Hide `.statusPath` on `max-width: 700px`, cap the title width, and add `overflow: hidden` to `.statusBar` as a defensive guard so a stubborn child can't punt the right side of the header off-screen. - Composer footer (and the send button in particular) could be clipped at the right edge on iOS. This happens when iOS Safari programmatically scrolls the document while focusing the textarea, even though body has `overflow: hidden`. Snap any stray window scroll back to (0,0) inside `syncAppHeight`, and constrain the composer / footer to `calc(100vw - 20px)` on mobile so it physically can't overflow. - Add `padding-top: max(10px, env(safe-area-inset-top))` to the mobile `.app` so the status bar is not flush with the iOS notch / dynamic island in standalone (Add to Home Screen) mode. Apply the same safe- area treatment to the expanded composer's `inset`. - Use `100dvh` for `--app-height` where supported, with the existing JS visualViewport sync still acting as the source of truth at runtime. This avoids a brief mis-sized layout on first paint before the JS height sync runs. - Add `overscroll-behavior: none` to html/body so iOS rubber-band scrolling doesn't reveal blank chrome behind the app. No tests added — this is purely CSS / layout glue. `npm run typecheck`, `npm run test:unit`, and `npm run build` all pass. Signed-off-by: Megha Goyal <goyamegh@amazon.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Improve mobile (notably iOS Safari) layout stability by preventing horizontal clipping/overflow and handling dynamic viewport height/safe-area insets.
Changes:
- Add safe-area–aware padding/inset rules and width constraints for the composer on mobile.
- Prevent status bar content from pushing right-side controls off-screen on narrow viewports.
- Improve viewport height handling (dvh + JS sync) and attempt to neutralize iOS “stray scroll” during keyboard focus.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/styles/statusBar.css | Hide overflow in the status bar to keep right-side buttons visible on narrow screens. |
| src/styles/responsive.css | Add iOS safe-area padding, constrain composer widths, and hide long status path on small screens. |
| src/styles/base.css | Prefer 100dvh when supported and add overscroll-behavior to reduce iOS scroll issues. |
| src/app/elements.ts | Reset window scroll to (0,0) during app-height sync to counter iOS keyboard/focus scroll. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+10
to
+12
| /* Prevent right-side buttons (settings, bucket, …) from being pushed | ||
| off-screen on narrow viewports if a child refuses to shrink. */ | ||
| overflow: hidden; |
| /* Defensive: never let the composer (or its footer) overflow the | ||
| viewport horizontally on iOS, where keyboard / focus-scroll quirks | ||
| can otherwise clip the send button at the right edge. */ | ||
| max-width: calc(100vw - 20px); |
Comment on lines
+27
to
+29
| /* iOS Safari sometimes scrolls the html element when an input is focused | ||
| even with body { overflow: hidden }. Lock scroll position explicitly. */ | ||
| overscroll-behavior: none; |
Comment on lines
+128
to
+135
| // iOS Safari can scroll the document body when focusing an input near the | ||
| // bottom of the viewport (keyboard appearance). With our fixed-height, | ||
| // overflow:hidden layout that just leaves part of the UI — e.g. the | ||
| // composer footer or send button — visually clipped at the edges of the | ||
| // visual viewport. Snap any stray scroll back to the top. | ||
| if (window.scrollY !== 0 || window.scrollX !== 0) { | ||
| window.scrollTo(0, 0); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes a few mobile / iOS layout problems visible on iPhone Safari.
Issues fixed
1. Status-bar buttons clipped on the right
On narrow iPhone viewports the cwd path in the status bar refused to shrink enough, which pushed the right-most icon buttons (session bucket flag and settings gear) off-screen.
.statusPathonmax-width: 700px(the working directory is still surfaced via the empty-state chooser and the session list, so no information is lost on mobile)..statusTitleatmin(60vw, 240px)on mobile.overflow: hiddento.statusBaras a defensive guard so a stubborn child can never punt the right side of the header off-screen.2. Composer / send button clipped on the right
The send button could be visually cut off at the right edge on iOS. iOS Safari programmatically scrolls the document while focusing inputs near the bottom of the viewport, even though
body { overflow: hidden }. That left part of the composer footer (and especially the send button) outside the visual viewport.syncAppHeight, snap any straywindow.scrollX/scrollYback to(0, 0)whenever the visual viewport changes (resize / scroll)..composertomax-width: calc(100vw - 20px)and explicitly addmin-width: 0/max-width: 100%to.composerFooterso it physically cannot overflow.3. No top safe-area inset on iOS standalone
In standalone (Add to Home Screen) mode, the status bar was flush against the iOS notch / dynamic island.
padding-top: max(10px, env(safe-area-inset-top))to the mobile.app.env(safe-area-inset-*)values to the expanded composer'sinset.4. Misc viewport hardening
100dvhfor--app-heightwhere supported (the existing JSvisualViewportsync still acts as the source of truth at runtime). Avoids a brief mis-sized layout on first paint before the JS sync runs.overscroll-behavior: nonetohtmlandbodyso iOS rubber-band scrolling doesn't reveal blank chrome behind the app.Files
src/styles/base.css—100dvhfallback,overscroll-behavior: none.src/styles/statusBar.css—overflow: hidden.src/styles/responsive.css— mobile fixes (safe-area, composer max-width, hide path, expanded composer insets).src/app/elements.ts— reset stray window scroll insidesyncAppHeight.Testing
npm run typecheck— passesnpm run test:unit— 50/50 passingnpm run build— succeedsNo new tests added; this PR is purely CSS / layout glue.