If you have ever built a full-screen hero section, opened it on your phone, and watched the bottom get swallowed by the browser’s address bar, you are not doing anything wrong. 100vh is broken on mobile by design, and it has been frustrating developers for years.
The good news is that CSS now ships a proper fix in the form of three new sets of viewport units. This guide explains exactly what they are, why they exist, and how to use them with real, copy-paste examples.
You can see all of these working live in the demo below.
Why 100vh Has Always Been Broken on Mobile
On desktop, 100vh works exactly as you would expect: it gives you 100% of the visible viewport height. On mobile, it lies to you.
Mobile browsers like Safari and Chrome have dynamic UI elements, primarily an address bar at the top and sometimes a toolbar at the bottom. These bars slide away as the user scrolls down to give more reading space, then reappear when the user scrolls back up.
The problem is that 100vh is always calculated as if those bars are already hidden, even when they are sitting right there on screen taking up space.
The result:
Your height: 100vh element is taller than the actual visible area when the page first loads. Your call-to-action button sits just below the fold, invisible behind the address bar. Your "full-screen" hero is not actually full screen.
/* This looks fine on desktop, but clips content on mobile */
.hero
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
The old workaround was a JavaScript hack that read window.innerHeight, set a CSS custom property, and re-ran on every resize event. It worked, but it caused layout shifts, depended on JS being loaded before first paint, and felt like exactly what it was: a band-aid over a browser bug.
The CSS Working Group solved this properly by defining three distinct viewport states and giving each one its own set of units.
The Three New Viewport Unit Families
Think of the mobile viewport as having two extreme states: one where the browser chrome is fully visible and one where it has fully retracted. The new units let you target either extreme or follow along dynamically in between.
Large Viewport Units (lv)
lvh, lvw, lvmin, lvmax are calculated assuming all browser UI is retracted and out of the way. This gives you the largest possible viewport size. It is equivalent to what 100vh was always pretending to be.
.fullscreen-modal
position: fixed;
inset: 0;
width: 100lvw;
height: 100lvh;
background: rgba(0, 0, 0, 0.85);
display: grid;
place-items: center;
This is the right unit for modals and overlays. When a modal opens, the user is focused on it and not actively scrolling, so the browser chrome is usually retracted anyway. Using lvh means the overlay truly covers the entire screen without leaving a gap at the bottom.
Small Viewport Units (sv): When Chrome Is Fully Visible
svh, svw, svmin, svmax are calculated assuming all browser UI is fully expanded. This is the minimum guaranteed space you will ever have, the portion of the screen that is always visible no matter what the browser is doing.
.sticky-cta-bar
position: fixed;
bottom: 0;
left: 0;
width: 100svw;
padding: 1rem;
background: #1a1a2e;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
This is the right unit for anything that absolutely must be visible at all times. Sticky headers, cookie banners, bottom navigation bars, and floating action buttons all benefit from sv units because you know with certainty they will never be clipped by browser UI.
Dynamic Viewport Units (dv): The Real-Time Middle Ground
dvh, dvw, dvmin, dvmax update in real time as the browser Chrome appears and disappears. When the address bar is visible, dvh equals svh. When the user scrolls, the address bar retracts, dvh expands to equal lvh. It is always an exact match for the actual visible area.
.hero
height: 100dvh;
display: grid;
place-items: center;
background: linear-gradient(135deg, #1a1a2e, #16213e);
This is what most developers reach for first as a 100vh replacement, and it works beautifully for hero sections and full-screen app layouts. One thing to be aware of: dvh does not animate at 60fps. Browsers intentionally debounce the updates so the layout does not jitter as the chrome slides. The transition is smooth enough that users never notice, but it means you should not tie scroll animations directly to dvh changes.
Real-World Examples
The Perfect Mobile Hero Section
The most common use case. The goal is a section that fills exactly the visible screen when the user arrives, with no content hidden below the fold.
.hero {
height: 100svh;
/* Sized to the guaranteed visible area on first load */
display: grid;
place-items: center;
padding: 2rem;
background-image: url('/hero-bg.jpg');
background-size: cover;
background-position: center;
tex
Tags:
#0
Want to run a more efficient business?
Mewayz gives you CRM, HR, Accounting, Projects & eCommerce — all in one workspace. 14-day free trial, no credit card needed.
Try Mewayz Free →