A hero section with height: 100vh looks perfect in a desktop browser and is cut off at the bottom on a phone. The cause is the mobile browser's toolbars. This lesson explains the problem and the three families of viewport units that fix it.
Why 100vh breaks on mobile
Mobile browsers show an address bar and often a toolbar. To give content more room, they hide these bars as you scroll down, and show them again when you scroll up. So the visible height of the page changes while you use it.
If vh changed every time the toolbars moved, every element sized in vh would resize during scrolling, and the page would jump. To avoid that, mobile browsers made vh a fixed value based on the largest possible viewport, with the toolbars hidden.
The result: when the page first loads with the toolbars showing, 100vh is taller than the visible area. The bottom of your "full-screen" hero, often where the call-to-action button sits, is hidden under the toolbar.
Three new viewport sizes
The CSS Values and Units Level 4 specification defines three viewport sizes, each with its own units:
| Units | Viewport | Size on mobile |
|---|---|---|
svh, svw | Small viewport | Toolbars fully shown: the smallest visible area |
lvh, lvw | Large viewport | Toolbars fully hidden: the largest visible area |
dvh, dvw | Dynamic viewport | Whatever is visible right now; changes as toolbars move |
On desktop browsers, where toolbars do not slide away, all three are usually the same as vh and vw.
There are also logical versions, svi, svb, lvi, lvb, dvi and dvb, for the inline and block directions, plus *vmin and *vmax variants such as svmin. The original vh behaves like lvh in most mobile browsers.
Building a full-height hero
For a section that should fill the screen on first load, use the small viewport:
.hero {
min-height: 100vh;
min-height: 100svh;
display: grid;
place-content: center;
padding: 2rem 1rem;
text-align: center;
}
<section class="hero">
<h1>Build sites that work on every screen</h1>
<p>A free course in responsive web design.</p>
<a class="button" href="#start">Start learning</a>
</section>
Three details make this robust:
- The fallback comes first. A browser that does not understand
svhignores the second declaration and keeps100vh. A browser that does understand it uses100svh. This is progressive enhancement with no feature query needed. min-height, notheight. If the content is taller than the screen, for example at 200 percent zoom or on a short landscape phone, the section grows instead of cutting text off.svh, notdvh. The hero fits in the smallest visible area, so the button is never hidden. When the toolbars slide away there is a little extra space at the bottom, which is harmless.
Avoid height: 100vh or height: 100svh on sections with text. A fixed height cannot grow, so zoomed or enlarged text overflows and may be cut off. That fails WCAG's Resize Text and Reflow criteria. Use min-height.
Choosing between svh, lvh and dvh
| Use | Unit | Why |
|---|---|---|
| Hero sections and splash screens | svh | Always fits on first load; stable while scrolling |
| Backgrounds that should never show a gap when toolbars hide | lvh | Covers the largest area; parts may be hidden under toolbars |
| App shells, full-screen panels, chat layouts that must match the visible area exactly | dvh | Tracks the real visible height as it changes |
Why not always use dvh?
dvh sounds like the perfect answer, because it always matches what you can see. The catch is that it changes during scrolling. Anything sized in dvh resizes as the toolbars move, which can make content below it shift and cause extra layout work while the user scrolls.
Use dvh for elements that do not scroll with the page, such as a fixed full-screen overlay or the outer shell of a web app where an inner area scrolls. Use svh for content in the normal scrolling page.
/* An app shell: header, scrolling content, footer */
.app {
height: 100vh;
height: 100dvh;
display: grid;
grid-template-rows: auto 1fr auto;
}
.app-content {
overflow-y: auto;
min-height: 0;
}
The app fills the visible area exactly, and only the middle row scrolls. min-height: 0 stops the grid row from growing to fit all its content, as in lesson 7.
This is one of the few places where height is the right choice, because the shell's job is to match the screen. Its inner area scrolls, so text can still grow without being cut off.
The on-screen keyboard
The software keyboard on phones is a separate matter from toolbars. In most current mobile browsers, opening the keyboard does not change the viewport units by default. It overlays the page instead. Do not rely on dvh to keep a form field above the keyboard, and test forms on real devices.
Some browsers support an interactive-widget key in the viewport meta tag that changes how the keyboard affects the layout. Support varies, so check MDN and Baseline before using it.
Browser support
Small, large and dynamic viewport units are supported in the current versions of all major browsers. Check their Baseline status on web.dev for details. The fallback pattern above, a vh declaration followed by the new unit, means older browsers still get a working, if slightly imperfect, layout.
If you need to apply several rules only when the units are supported, use a feature query:
@supports (height: 100dvh) {
.modal {
max-height: 90dvh;
}
}
Testing full-height layouts
DevTools device mode does not simulate toolbars sliding away. Test on real phones:
- Load the page fresh, scrolled to the top. Check that the whole hero, including its button, is visible.
- Scroll down a little so the toolbars hide, then back up. Check nothing jumps unexpectedly.
- Rotate to landscape. A short, wide viewport is where full-height sections most often cover content.
- Zoom to 200 percent on desktop and check the section grows with its text.
Summary
- Mobile browsers base
vhon the largest viewport, so100vhis taller than the visible area when toolbars show. svhis the small viewport,lvhthe large one, anddvhthe dynamic one that changes as toolbars move.- Use
min-height: 100svhfor heroes, withmin-height: 100vhbefore it as a fallback. - Use
dvhfor fixed overlays and app shells, not for content in the scrolling page. - Prefer
min-heighttoheightso content can grow when zoomed. - The on-screen keyboard is not handled by these units by default; test forms on real devices.
You finished the course
You can now build pages that work from a 320 pixel phone to a wide desktop: with the right viewport, mobile-first CSS, media and container queries, responsive images, fluid type, intrinsic layouts, tested breakpoints, accessible navigation and full-height sections that fit. Take the final quiz to check your understanding, then continue with Web Accessibility Essentials to make sure everything you build works for everyone.