The Problem

On iOS, the browser URL bar is dynamic — it slides away as you scroll down, and reappears when you scroll up. This is great for UX, but it creates a trap: 100vh is computed against the large viewport height(URL bar hidden), not the actual visible area when the page first loads.

Result: any element pinned to the bottom of a 100vh container — a sticky footer, a "View Results" button, a chat input — is hidden behind the URL bar on initial page load.

Before (100vh)

Footer hidden behind URL bar

After (100dvh)

Footer fully visible

Root Cause

On iOS browsers, 100vh equals the large viewport height — the full screen height assuming the URL bar is retracted. When a user first loads the page, the URL bar is visible and occupies roughly 60px at the bottom of the visible area. Elements sitting at the bottom of a 100vh container are scrolled out of view behind the URL bar.

The three viewport heights:
svh — small viewport height (URL bar always visible)  | lvh — large viewport height (URL bar hidden) = old vh  | dvh — dynamic viewport height (tracks real visible area in real time)

The Fix

drawer.css
.drawer {
  height: 100vh;   /* fallback for older browsers */
  height: 100dvh;  /* iOS 15.4+: tracks actual visible height dynamically */
}

/* Or use min-height for flexible content: */
.page-shell {
  min-height: 100vh;
  min-height: 100dvh;
}

dvh (dynamic viewport height) recalculates in real time as the URL bar shows and hides. On page load when the URL bar is visible, 100dvh will be approximately 100lvh - 60px, meaning your footer stays inside the visible window.

Browser Support

BrowserMin VersionStatusNotes
Safari iOS15.4+Supporteddvh supported; older falls back to 100vh
Safari iOS (older)< 15.4Partial100vh fallback — footer still clipped, no crash
Chrome Android108+SupportedFull dvh support
Firefox101+SupportedFull dvh support

Further Reading