If you have ever set height: 100vh on a hero section and noticed the bottom gets cut off on mobile, you have hit the most common viewport unit problem in CSS. The classic vh unit does not account for the browser's own UI: the address bar, tab strip, and toolbar that appear at the top or bottom of the screen. Three newer units were introduced to solve exactly this: svh, dvh, and lvh.

This guide explains why vh breaks on mobile, what each new unit measures, and exactly when to use each one.

Why 100vh breaks on mobile

On desktop browsers, 100vh reliably equals the full visible height of the browser window. On mobile, the browser has its own UI: an address bar at the top, and sometimes a tab bar or navigation bar at the bottom. These elements sit inside the screen but on top of your page.

The problem is how browsers measure the viewport when the page first loads. The browser UI is visible, which means the actual visible page area is shorter than the full screen. But 100vh is calculated as if the browser UI were already hidden, matching the full screen height instead of the visible height. The result: your 100vh element is taller than what the user can see, and the bottom of your content is hidden underneath the browser toolbar.

To make things worse, when the user scrolls down, mobile browsers retract their UI to give more reading space. At that point the visible area grows, but your 100vh section has already been sized, so you get a layout jump as the browser updates.

This is not a bug. The original vh behaviour was a deliberate choice, locking the unit to a stable value so it does not constantly resize as the browser UI shows and hides during scrolling. The new units give you explicit control over which measurement you actually want.

The three new units: svh, dvh and lvh

CSS introduced a new family of viewport units built around one key concept: the viewport has a small state (browser UI fully visible) and a large state (browser UI fully hidden). The three new height units each correspond to a specific state.

UnitWhat it measuresUpdates live?
svhSmall viewport height: the viewport with browser UI fully visibleNo
lvhLarge viewport height: the viewport with browser UI fully hiddenNo
dvhDynamic viewport height: the actual current visible heightYes

svh is the smallest the viewport will ever be on that device. lvh is the largest it will ever be. dvh sits somewhere between the two and updates in real time as the browser UI appears and disappears.

svh: small viewport height

svh measures the viewport height when the browser UI is fully expanded and taking up its maximum space. This is what the user sees when they first land on the page, before any scrolling. 100svh will never be taller than the visible area, which means content sized with svh will never overflow off the bottom of the screen.

.hero {
  min-height: 100svh; /* never cut off by mobile browser UI */
}

.full-screen-modal {
  height: 100svh;
  overflow-y: auto;
}

The tradeoff with svh is that on devices where the browser UI retracts when scrolling, a 100svh section will have a small gap at the bottom once the UI hides. The section does not grow to fill the newly available space. For most designs this is acceptable. A small gap is far less disruptive than content being hidden entirely.

svh is the right default for most full-height layouts. Content is always fully visible on load, and the gap that appears when the browser UI retracts is small and rarely noticeable.

lvh: large viewport height

lvh measures the viewport height when the browser UI is fully hidden, the maximum available space. 100lvh equals what 100vh on desktop would give you: the full screen from top to bottom, ignoring any browser chrome.

.canvas {
  height: 100lvh; /* fills screen when browser UI is retracted */
}

On mobile, 100lvh will be taller than the visible area when the browser UI is showing, which is exactly the same problem as 100vh. The main use case for lvh is as a ceiling value: "this element should never exceed the full screen height even when the browser UI is gone." On its own it is rarely what you want for a hero or modal.

dvh: dynamic viewport height

dvh tracks the actual visible viewport height in real time. When the browser UI is visible, 100dvh equals 100svh. When the browser UI hides, 100dvh grows to equal 100lvh. It is always exactly the visible screen height at any given moment.

.hero {
  min-height: 100dvh; /* always exactly the visible screen */
}

This sounds perfect, but the constant resizing comes with a cost. Any time the browser UI shows or hides, every element sized with dvh has to recalculate and repaint. In most designs this causes a visible layout shift: the hero section grows slightly when the user starts scrolling down, and shrinks back when they scroll up. For a simple hero this may be subtle, but for a layout with sticky headers, overlapping sections, or fixed-position elements, the constant reflow is visible and jarring.

Test dvh on a real device before shipping. The constant resize can cause noticeable layout jumps on scroll, especially when other fixed or sticky elements are present. If it causes jank, switch to svh.

The full family: vi, si, di and the width equivalents

The same small/large/dynamic concept applies to all viewport dimensions, not just height. The complete set of new units:

UnitMeasuresEquivalent to
svh / lvh / dvhViewport heightSmall / large / dynamic
svw / lvw / dvwViewport widthSmall / large / dynamic
svmin / lvmin / dvminSmaller of width or heightSmall / large / dynamic
svmax / lvmax / dvmaxLarger of width or heightSmall / large / dynamic
svi / lvi / dviViewport inline size (width in horizontal writing)Small / large / dynamic
svb / lvb / dvbViewport block size (height in horizontal writing)Small / large / dynamic

In practice, svh, dvh, and lvh are what you will encounter and need. Mobile browsers resize the viewport vertically when their UI appears, not horizontally, so the width variants behave the same as the classic vw in most cases.

Writing the fallback for older browsers

Browser support for the new units is above 90% globally as of 2026, but some users are still on older versions of Chrome, Firefox, or Samsung Internet that do not support them. The correct fallback pattern is to write the vh value first and the new unit second. Browsers that do not recognise svh will ignore it and use the vh declaration above.

.hero {
  min-height: 100vh;   /* fallback for older browsers */
  min-height: 100svh;  /* used by browsers that support it */
}

This pattern works because CSS declarations in the same rule overwrite each other in source order. A browser that understands svh reads both lines and uses the second. A browser that does not understand svh ignores the second line and keeps the first.

Browser support

BrowserSupport from version
Chrome108
Safari15.4
Firefox101
Edge108
Samsung Internet21
iOS Safari15.4

Safari 15.4 shipped in March 2022 and iOS 15.4 followed the same month, so iOS Safari has had support for over three years. The fallback pattern above covers any remaining users on older versions.

Common patterns

Full-height hero section

.hero {
  min-height: 100vh;          /* fallback */
  min-height: 100svh;         /* safe on mobile */
  display: flex;
  align-items: center;
  justify-content: center;
}

Full-screen modal or drawer

.modal {
  position: fixed;
  inset: 0;
  height: 100vh;             /* fallback */
  height: 100dvh;            /* fills visible area exactly */
  overflow-y: auto;
}

For a fixed-position modal, dvh is appropriate because the modal is already taking over the full screen and there is nothing behind it to shift around. The constant resize is not noticeable inside a fixed overlay.

Sticky sidebar that fills the screen

.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;             /* fallback */
  height: 100svh;            /* stable height on mobile */
  overflow-y: auto;
}

Minimum height with content that may grow

.section {
  /* min-height lets content push the section taller if needed */
  min-height: 100vh;
  min-height: 100svh;
  padding: 4rem 2rem;
}

Using all three together for a precise range

.panel {
  /* Never shorter than the small state */
  min-height: 100svh;
  /* Never taller than the large state */
  max-height: 100lvh;
  /* Tries to match the actual visible height */
  height: 100dvh;
}

svh vs dvh: how to choose

SituationUseWhy
Hero section, landing pagesvhContent always visible on load, no layout jump
Full-screen fixed modal / overlaydvhShould fill exactly what the user sees at all times
Sticky sidebarsvhStable height avoids scroll-triggered reflow
App shell (navigation + content area)dvhApp-style layout should track the visible area
Max-height caplvhUseful as a ceiling value combined with min-height: svh

The relationship with the old vh, vw and vmin

The classic vh unit behaves like lvh in practice on most mobile browsers. It was defined to match the initial viewport before any scrolling, which on modern mobile browsers means the browser UI is included in the measurement. You can think of vh as an approximation of lvh on mobile, and the reason the problem exists at all.

On desktop browsers there is no difference between vh, svh, dvh, and lvh because the viewport height does not change as you interact with the page. All four units resolve to the same value on desktop. The new units only behave differently on devices where browser UI can appear and disappear.

Desktop vs mobile summary: On desktop, svh = dvh = lvh = vh. On mobile, svh is the visible height with browser UI showing, lvh is the full screen with browser UI hidden, and dvh fluctuates between the two. You only need to care about which unit to use when targeting mobile layouts.

Frequently asked questions

What is the difference between dvh, svh and lvh?

svh (small viewport height) is the viewport height with the browser UI fully visible, the smallest it will ever be. lvh (large viewport height) is the viewport height with the browser UI fully hidden, the largest it will ever be. dvh (dynamic viewport height) updates in real time as the browser UI shows or hides. For most full-height layouts, svh is the safest choice because it never causes overflow.

Why does 100vh not work on mobile?

Mobile browsers include their address bar and tab bar in the viewport height measurement. So 100vh equals the full screen including that browser UI, which means content gets cut off at the bottom on load. When the user scrolls, the browser UI retracts and 100vh becomes taller than the visible area, causing a layout jump. The svh, dvh and lvh units were introduced to solve this.

Should I use dvh or svh?

Use svh when content must not overflow and a small gap at the bottom is acceptable. This is the right choice for most hero sections. Use dvh when you need the layout to always fill the exact visible screen and have confirmed on a real device that the constant resizing does not cause visual jank.

What browsers support dvh, svh and lvh?

Chrome 108, Safari 15.4, Firefox 101, and Edge 108. Global support is above 90% as of 2026. Write a vh fallback before the svh or dvh declaration to handle older browsers.

Does the same problem exist for vw with dvw, svw and lvw?

Yes, the same family exists for width: svw, lvw, and dvw. However the problem is much less common in practice because mobile browser UI does not resize the viewport horizontally, only vertically. The height variants are what you will encounter and need in real projects.

Convert px to rem and rem to px instantly

Stop doing unit conversion manually. Our free converter handles both directions with custom base font size and bulk conversion support.

Open PX to REM Converter →