The Problem
On iOS, when the software keyboard appears, elements styled with position: fixed— toolbars, send buttons, nav bars — float up into the middle of the screen rather than staying anchored to the bottom of the visible area.
Footer displaced above keyboard
Root Cause
iOS separates the layout viewport (what CSS sees and position: fixed is relative to) from the visual viewport (the actual visible area of the screen). When the software keyboard appears, the visual viewport shrinks, but the layout viewport does not change.
position: fixed elements are positioned relative to the layout viewport. So a footer withbottom: 0 stays at the bottom of the full-screen layout viewport — which is now mostly hidden behind the keyboard.
Fix 1 — position: sticky for in-flow footers
The simplest fix when the footer is part of a flex/grid column layout: use position: sticky instead of fixed. Sticky elements stay attached to their scroll container without the layout-viewport problem.
.app-shell {
display: flex;
flex-direction: column;
height: 100dvh; /* tracks actual visible height */
}
.messages-list {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.chat-footer {
position: sticky;
bottom: 0;
flex-shrink: 0;
background: #1a1a1a;
padding: 0.75rem;
}Fix 2 — Visual Viewport API (Full control)
For complex cases — full-screen apps, iframes, or elements that genuinely need position: fixed— use the Visual Viewport API to track the actual visible window and adjust manually.
const footer = document.querySelector('.app-footer');
function repositionFooter() {
const vv = window.visualViewport;
if (!vv) return;
// Offset by the amount the keyboard has consumed
const keyboardHeight = window.innerHeight - vv.height;
footer.style.transform = `translateY(-${keyboardHeight}px)`;
}
window.visualViewport?.addEventListener('resize', repositionFooter);
window.visualViewport?.addEventListener('scroll', repositionFooter);window.visualViewport before using it.Browser Support
| Browser | Min Version | Status | Notes |
|---|---|---|---|
| Safari iOS (all) | All | Not supported | position:fixed breaks when keyboard opens; layout viewport not resized |
| Safari iOS 15.4+ | 15.4+ | Partial | dvh partially improves this; Visual Viewport API is the full fix |
| Chrome iOS | All | Not supported | Same WebKit engine, same behaviour |
| Chrome Android | All | Supported | Keyboard resize handled correctly |
| Firefox | All | Supported | Keyboard resize handled correctly |
Further Reading
- MDN: Visual Viewport APIDocumentation for window.visualViewport — the API to track the actual visible area.
- WebKit Blog: Viewport Changes in Safari 15Explains the layout vs visual viewport split and the dvh units introduced in iOS 15.4.
- web.dev: "Virtual keyboard API"Chrome's VirtualKeyboard API (Chrome Android) — different approach for Chromium.