Choose Viewport Height Units Without Hiding Mobile Content
A full-height shell sized to a larger viewport can extend 56 pixels below the currently visible area when browser controls occupy part of the screen.
block-size: 100vh- Visible area
- --
- Shell height
- --
- Clipped height
- --
min-block-size: 100dvh- Visible area
- --
- Shell height
- --
- Clipped height
- --
Three-browser evidence verified. Live values come from your current browser. The formal matrix records the controlled browser runs and fixture revision used for the published conclusion.
Observed failure
The shell uses the larger height while less is visible
The deterministic harness represents a 232-pixel large viewport and a 176-pixel current visible area. The failing shell uses the large value, so its last 56 pixels are clipped by the simulated browser UI boundary.
Minimal code diff
Match the unit to the intended viewport state
Use `dvh` when the shell should track the current dynamic viewport, `svh` when it must always fit the smallest state, and `lvh` only when covering the largest state is intentional.
Before
.app-shell {
block-size: 100vh;
}After
.app-shell {
min-block-size: 100dvh;
block-size: auto;
}Root cause
Mobile browsers can expose large, small, and dynamic viewport sizes as their controls retract or expand. CSS Values Level 4 defines svh, lvh, and dvh for those distinct states. Legacy vh maps to the large viewport size, which can be taller than the currently visible region.
The fixture deliberately uses explicit 232- and 176-pixel harness values because desktop browser automation cannot reproduce every mobile browser chrome transition. It verifies the geometry of the failure and fix without claiming that the harness is a device emulator.
Define the state the interface must survive
Choose the unit from the product requirement, not from a replacement chart. A reading surface may use a minimum height and continue in normal document flow. A task shell with a final action must keep that action reachable in the smallest visible state, during browser-control changes, and when content grows. Record the shell rectangle, the modeled visible boundary, and the clearance around the final control rather than judging only whether the background appears full-screen.
Repeat the check in portrait and landscape with longer translated text and increased text size. Then test a real supported mobile browser manually, because the deterministic harness does not model an address-bar animation, safe-area inset, or on-screen keyboard. Those device observations supplement the reproducible geometry result instead of being silently generalized from it.
When this fix is not enough
Changing one unit does not decide how content should scroll. A shell with controls, safe-area insets, an on-screen keyboard, or nested scroll regions needs additional design decisions. dvh can also resize while browser UI changes, so critical content should remain reachable without depending on a perfectly stable full-screen height.
Regression condition
Across all recorded browsers and viewports, the failing modeled shell must exceed the visible boundary by 56 pixels. The fixed shell must fit the 176-pixel visible area with zero clipped height. Both states must retain zero page-level horizontal overflow.
Primary references
Sources checked for this case
- CSS Values and Units Module Level 4 - viewport-relative lengths - W3C, checked 2026-08-04
- CSS Working Group - new viewport units in Values and Units Level 4 - W3C CSS Working Group, checked 2026-08-04
Formal evidence
Browser and viewport matrix
Cells show failing / fixed visible-height clipping in CSS pixels.
| Browser | 320 | 375 | 768 | 1024 | 1440 |
|---|---|---|---|---|---|
| Chromium | +56 / +0 | +56 / +0 | +56 / +0 | +56 / +0 | +56 / +0 |
| Firefox | +56 / +0 | +56 / +0 | +56 / +0 | +56 / +0 | +56 / +0 |
| WebKit | +56 / +0 | +56 / +0 | +56 / +0 | +56 / +0 | +56 / +0 |
18/18 recorded assertions passed
chromium-failing-clipping-32056pxchromium-fixed-clipping-3200pxchromium-horizontal-reflow-3200px / 0pxchromium-failing-clipping-37556pxchromium-fixed-clipping-3750pxchromium-horizontal-reflow-3750px / 0pxfirefox-failing-clipping-32056pxfirefox-fixed-clipping-3200pxfirefox-horizontal-reflow-3200px / 0pxfirefox-failing-clipping-37556pxfirefox-fixed-clipping-3750pxfirefox-horizontal-reflow-3750px / 0pxwebkit-failing-clipping-32056pxwebkit-fixed-clipping-3200pxwebkit-horizontal-reflow-3200px / 0pxwebkit-failing-clipping-37556pxwebkit-fixed-clipping-3750pxwebkit-horizontal-reflow-3750px / 0px
Run 2026-08-04T11-36-57-234Z. chromium 149.0.7827.201, firefox 153.0, webkit 26.5. Open raw manifest