fix(web): prevent iOS PWA keyboard from pushing header behind status bar (#454) (#457)

On iOS PWA (black-translucent + viewport-fit=cover), opening the virtual
keyboard causes iOS to scroll the page upward, pushing the session header
behind the system status bar. Fix by resetting window.scrollTo(0, 0) when
the keyboard is detected open, and listening to visualViewport scroll
events in addition to resize to catch any deferred scrolling.

via [HAPI](https://hapi.run)

Co-authored-by: HAPI <noreply@hapi.run>
This commit is contained in:
Haoqing Wang
2026-04-14 17:40:25 +08:00
committed by GitHub
co-authored by HAPI
parent 40e51cb8d4
commit fa8cf53c56
2 changed files with 54 additions and 0 deletions
+43
View File
@@ -60,4 +60,47 @@ describe('useViewportHeight update logic', () => {
expect(root.style.getPropertyValue('--app-viewport-height')).toBe('')
})
it('resets page scroll when keyboard is open', () => {
const scrollToSpy = vi.spyOn(window, 'scrollTo').mockImplementation(() => {})
// Simulate: keyboard open AND page has been scrolled by iOS
Object.defineProperty(window, 'scrollY', { value: 120, configurable: true })
const viewportHeight = 400
const windowHeight = 800
const diff = windowHeight - viewportHeight
if (diff > 1) {
root.style.setProperty('--app-viewport-height', `${viewportHeight}px`)
if (window.scrollY > 0) {
window.scrollTo(0, 0)
}
}
expect(scrollToSpy).toHaveBeenCalledWith(0, 0)
// Cleanup
Object.defineProperty(window, 'scrollY', { value: 0, configurable: true })
scrollToSpy.mockRestore()
})
it('does not reset scroll when page is not scrolled', () => {
const scrollToSpy = vi.spyOn(window, 'scrollTo').mockImplementation(() => {})
Object.defineProperty(window, 'scrollY', { value: 0, configurable: true })
const viewportHeight = 400
const windowHeight = 800
const diff = windowHeight - viewportHeight
if (diff > 1) {
root.style.setProperty('--app-viewport-height', `${viewportHeight}px`)
if (window.scrollY > 0) {
window.scrollTo(0, 0)
}
}
expect(scrollToSpy).not.toHaveBeenCalled()
scrollToSpy.mockRestore()
})
})