Why 100vh Doesn’t Always Fit a Mobile Screen
100vh can be taller than the area visible between a phone’s browser controls. CSS now provides explicit alternatives: svh for the small viewport, lvh for the large viewport, and dvh for a changing viewport.
For a full-height hero, min-height: 100svh is a useful starting point. An application panel may need 100dvh instead. The choice depends on whether the design should stay stable or follow the browser controls.
Let a hero grow beyond one screen
A hero can fill at least the small viewport while keeping its content in normal flow:
.hero {
box-sizing: border-box;
min-height: 100vh;
min-height: 100svh;
display: grid;
align-content: center;
padding: 2rem;
}
The first height is a fallback. A browser that accepts the second declaration uses the small viewport reference instead.
min-height leaves room for a longer heading, translated copy, or larger text. A fixed height would impose a different constraint, so it needs a deliberate overflow policy.
The border-box rule also makes the padding part of the declared minimum size. Otherwise, a nominal screen-height box can gain extra height from its own padding.
Why 100vh can extend below the visible area
A mobile browser can show or retract its address and navigation controls. That leaves several possible reference heights for a page that asks to fill the screen.
The CSS length reference distinguishes the viewport variants. The default vh corresponds to the large viewport unit, so it can include space that is unavailable while the controls are visible.
The available choices describe different behaviour:
| Unit | Reference height | Practical trade-off |
|---|---|---|
svh | Small viewport, with relevant browser controls expanded | Stable, but may leave extra space when controls retract |
lvh | Large viewport, with those controls retracted | Can extend below the currently visible area |
dvh | Dynamic viewport as controls change | Tracks the available area, but can resize during scrolling |
None of those units knows whether the page’s content actually fits. A viewport calculation is one input to the layout, not permission to clip a long paragraph.
Use dynamic height for a panel that must follow controls
A panel with a fixed header and a scrollable content area can make that contract explicit:
.panel {
height: 100vh;
height: 100dvh;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
}
.panel__content {
overflow: auto;
}
The second grid track can shrink, and its content can scroll rather than expanding the panel indefinitely. This pattern suits an interface whose viewport-height boundary is intentional.
Dynamic viewport units can change during a scroll. For ordinary article content, a stable minimum and document scrolling are often less distracting than a panel that repeatedly changes height.
The browser controls also do not have to animate every intermediate unit value smoothly. The viewport unit specification leaves room for implementation choices during those transitions.
Do not treat dvh as a keyboard guarantee
An on-screen keyboard introduces another distinction: the layout viewport and the portion currently visible to the user can differ. Switching a height to dvh does not guarantee identical keyboard behaviour in every browser configuration.
The Visual Viewport API explains that distinction. A focused field, a fixed footer, and a software keyboard deserve an actual device check if the interface depends on their relationship.
Begin with a page that can scroll to the active control. Add viewport observation only when a specific remaining problem requires it, rather than replacing every CSS height with JavaScript.
Keep older Safari workarounds in their historical context
The WebKit viewport-height discussion records why older mobile layouts could extend behind browser controls. Before the explicit viewport variants, one workaround was:
body {
min-height: 100vh;
min-height: -webkit-fill-available;
}
That prefixed value was a compatibility workaround, rather than the same contract as 100svh or 100dvh. Its result depends on the containing layout and implementation.
Other approaches used fixed device media queries, JavaScript measurements, or Viewport Units Buggyfill. Those explain old code a maintainer may encounter. They are poor default additions to a new page that can express its requirement with current CSS.
Hard-coded device dimensions also age badly: the page’s actual available area matters more than a list of phone models. A script that measures window.innerHeight adds resize handling and still needs a clear definition of the area it is trying to follow.
Check the intended layout with browser controls expanded and retracted, then with longer content and increased text size. The useful result is a reachable interface, not a section that remains exactly one screen high at the expense of its content.