A hero that fits your phone

This hero is sized with the unit you pick below. On a desktop they all look the same. On a phone, scroll and watch the toolbar change what fits.

Hero height: measuring…

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

UnitMeansUse it for
svhSmall viewport, toolbars fully visibleContent that must never be cut off
lvhLarge viewport, toolbars retractedImmersive backgrounds, same as old vh
dvhDynamic viewport, follows the toolbar as it movesMost 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.

Honest note: dvh resizes the element as the toolbar moves, which can cause visible reflow mid-scroll. If the section must never jump, svh is the stable choice: it always fits, even with the toolbar fully visible.

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.

UnitMeasured
100vhmeasuring…
100svhmeasuring…
100lvhmeasuring…
100dvhmeasuring…
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.

measuring viewport…