Skip to content

fix: keep iOS UI inside the visual viewport#19

Open
goyamegh wants to merge 1 commit into
ashwin-pc:mainfrom
goyamegh:fix/ios-ui-overflow
Open

fix: keep iOS UI inside the visual viewport#19
goyamegh wants to merge 1 commit into
ashwin-pc:mainfrom
goyamegh:fix/ios-ui-overflow

Conversation

@goyamegh
Copy link
Copy Markdown

@goyamegh goyamegh commented Jun 7, 2026

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.

  • Hide .statusPath on max-width: 700px (the working directory is still surfaced via the empty-state chooser and the session list, so no information is lost on mobile).
  • Cap .statusTitle at min(60vw, 240px) on mobile.
  • Add overflow: hidden to .statusBar as 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.

  • Inside syncAppHeight, snap any stray window.scrollX/scrollY back to (0, 0) whenever the visual viewport changes (resize / scroll).
  • On mobile, constrain .composer to max-width: calc(100vw - 20px) and explicitly add min-width: 0 / max-width: 100% to .composerFooter so 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.

  • Add padding-top: max(10px, env(safe-area-inset-top)) to the mobile .app.
  • Apply matching env(safe-area-inset-*) values to the expanded composer's inset.

4. Misc viewport hardening

  • Use 100dvh for --app-height where supported (the existing JS visualViewport sync still acts as the source of truth at runtime). Avoids a brief mis-sized layout on first paint before the JS sync runs.
  • Add overscroll-behavior: none to html and body so iOS rubber-band scrolling doesn't reveal blank chrome behind the app.

Files

  • src/styles/base.css100dvh fallback, overscroll-behavior: none.
  • src/styles/statusBar.cssoverflow: 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 inside syncAppHeight.

Testing

  • npm run typecheck — passes
  • npm run test:unit — 50/50 passing
  • npm run build — succeeds

No new tests added; this PR is purely CSS / layout glue.

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>
Copilot AI review requested due to automatic review settings June 7, 2026 07:11
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 thread src/styles/statusBar.css
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;
Comment thread src/styles/responsive.css
/* 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 thread src/styles/base.css
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 thread src/app/elements.ts
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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants