The problem with 100vh
On a phone, the browser toolbar takes space, then retracts as you scroll. The old vh unit measures the largest possible viewport, toolbars retracted. So a 100vh hero is taller than the visible screen on first load. The bottom of it, usually the button you most want tapped, sits under the toolbar.
The old fix was JavaScript: measure window.innerHeight on resize and set a custom property. It worked, but it was a workaround for something CSS should have known.
Three units for three toolbar states
| Unit | Means | Use it for |
|---|---|---|
| svh | Small viewport, toolbars fully visible | Content that must never be cut off |
| lvh | Large viewport, toolbars retracted | Immersive backgrounds, same as old vh |
| dvh | Dynamic viewport, follows the toolbar as it moves | Most full-height sections |
The one-line fix
.hero {
height: 100vh; /* fallback for older browsers */
height: 100dvh; /* modern browsers use this */
}
Keep the vh line first. Browsers that do not know dvh ignore the second line and keep the fallback. Browsers that do know it override with the dynamic value.
Support
Dynamic viewport units shipped in Chrome 108, Firefox 101, and Safari 15.4, so they work on iOS Safari, not just Chromium. Older browsers fall back to the vh line above.
One iPhone caveat: only Safari shows the difference. Firefox, Chrome, and Edge on iPhone all run in a webview whose toolbar sits outside the page, so the viewport never splits into small and large there, and all four units measure the same.
Measured on this device
These four numbers are measured live from hidden elements, one per unit. No toggling or scrolling needed. If they differ, the gap is the toolbar.
| Unit | Measured |
|---|---|
| 100vh | measuring… |
| 100svh | measuring… |
| 100lvh | measuring… |
| 100dvh | measuring… |
Try it
Scroll this page on your phone. Watch the bar pinned to the bottom: visualViewport.height grows as the toolbar retracts. Switch the hero to 100vh and notice the hero is taller than that number on first load. Switch to 100dvh and the hero tracks it.
Keep scrolling. There is nothing more below except this paragraph and the footer, which is the point: the whole demo is the viewport itself.