fix(maestro): keep mobile key bar above the soft keyboard
ci / rust (push) Successful in 8m46s

On Android the soft keyboard overlaid the layout viewport and the browser
auto-scrolled the document on focus, dragging the shell (and its special-key
bar) to the top of the screen and forcing a manual scroll.

- index.html: interactive-widget=resizes-content so Chrome resizes the layout
  viewport instead of overlaying the keyboard, killing the auto-scroll.
- viewport.ts: mirror visualViewport.offsetTop into --app-top for iOS / older
  browsers that ignore interactive-widget and still scroll under the keyboard.
- styles.css: translateY(--app-top) re-pins #app to the visible region.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 20:49:46 +00:00
co-authored by Claude Opus 4.8
parent 7f3ac21402
commit d989a40772
3 changed files with 17 additions and 3 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content"
/>
<meta name="color-scheme" content="dark light" />
<meta name="theme-color" content="#1a1d24" media="(prefers-color-scheme: dark)" />
+4 -1
View File
@@ -211,8 +211,11 @@ body {
#app {
/* `--app-vh` tracks the *visual* viewport (set by installViewportSizing) so
the shell stays above the mobile soft keyboard; falls back to the layout
viewport where `visualViewport` is unavailable. */
viewport where `visualViewport` is unavailable. `--app-top` follows the
layout viewport's scroll under the keyboard so the shell stays pinned to
the visible region instead of drifting to the top. */
height: var(--app-vh, 100%);
transform: translateY(var(--app-top, 0px));
display: flex;
flex-direction: column;
overflow: hidden;
+12 -1
View File
@@ -1,5 +1,5 @@
/**
* Bind the app shell's height to the *visual* viewport.
* Bind the app shell's height *and position* to the *visual* viewport.
*
* On mobile the layout viewport does not shrink when the on-screen keyboard
* opens — only the visual viewport does. A shell sized at `height: 100%` (or
@@ -8,6 +8,16 @@
* `visualViewport.height` onto a CSS custom property keeps the shell exactly as
* tall as the visible area, so the key bar lands just above the keyboard.
*
* Height alone isn't enough: when the keyboard opens the browser also scrolls
* the layout viewport to reveal the focused element, which drags the shell
* (anchored at layout-top) up and out of view — the key bar ends up at the top
* of the screen and the user has to scroll back down. `visualViewport.offsetTop`
* is how far the layout viewport has scrolled under the keyboard; translating
* the shell down by that amount re-pins it to the visible region. (Chrome's
* `interactive-widget=resizes-content`, set in the viewport meta, prevents the
* scroll entirely on Android; this covers iOS and older browsers that ignore
* it, where offsetTop stays non-zero.)
*
* No-ops where `visualViewport` is unavailable; the CSS falls back to `100%`.
*/
export function installViewportSizing(): void {
@@ -20,6 +30,7 @@ export function installViewportSizing(): void {
const sync = () => {
raf = 0;
root.style.setProperty("--app-vh", `${Math.round(vv.height)}px`);
root.style.setProperty("--app-top", `${Math.round(vv.offsetTop)}px`);
};
const schedule = () => {
if (raf) return;