The Trick To Viewport Units On Mobile

Master CSS viewport handling for mobile devices with both classic solutions and modern CSS alternatives

Why Viewport Units Behave Strangely on Mobile

Viewport units have always been controversial, and some of that is because of how mobile browsers have made things more complicated by having their own opinions about how to implement them. When building responsive mobile layouts, developers often encounter unexpected behavior where 100vh doesn't actually mean 100% of the visible viewport height.

The issue stems from how browsers interpret the "viewport" definition. On desktop browsers, the viewport is relatively stable, but on mobile devices, the visible area changes as users interact with the page. When you set an element to height: 100vh, you're not necessarily getting an element that fills 100% of what the user can currently see.

The CSS specification is remarkably vague about how viewport units should be calculated on mobile devices. Mobile browsers like Safari and Chrome have address bars and navigation controls that appear and disappear as the user scrolls, which creates complications for fixed-height elements using viewport units.

For developers building responsive web applications, understanding these nuances is essential for creating consistent user experiences across all devices. This is particularly important when implementing mobile-first design principles that prioritize smaller screens while maintaining functionality on desktop.

The Problem with Traditional Viewport Units

When you're working with viewport height (vh) on a mobile browser, you're dealing with an environment where the visible viewport changes dynamically. Different mobile browsers handle viewport units in distinct ways:

  • Safari for iOS defines a fixed value for vh based on the maximum height of the screen, preventing content jumping but potentially cropping elements when the address bar is visible
  • Chrome's mobile browser adopted a similar approach, though implementations aren't identical across all platforms
  • Firefox for Android has been working on addressing this behavior, with implementation details continuing to evolve

The result is that 100vh may refer to the maximum possible viewport rather than the current visible area, causing elements to be cropped or extend beyond the visible screen.

According to CSS-Tricks' analysis of mobile browser viewport behavior, this inconsistency has been a persistent challenge for developers building responsive mobile layouts.

The Classic Solution: CSS Custom Properties with JavaScript

The solution that became widely known as "the trick to viewport units on mobile" involves using CSS custom properties (CSS variables) in combination with JavaScript to dynamically calculate and apply the correct viewport height.

The technique works by capturing the current viewport height using JavaScript's window.innerHeight property, which accurately reflects the currently visible area of the browser, including accounting for any visible browser chrome.

The CSS

.element {
 height: 100vh; /* Fallback for browsers that don't support custom properties */
 height: calc(var(--vh, 1vh) * 100);
}

The JavaScript

// First we get the viewport height and multiply it by 1% to get a value for a vh unit
let vh = window.innerHeight * 0.01;
// Then we set the value in the --vh custom property to the root of the document
document.documentElement.style.setProperty('--vh', `${vh}px`);

The --vh custom property now represents 1% of the current visible viewport height, and multiplying it by 100 gives you exactly 100% of the visible area.

This approach is particularly valuable for cross-platform mobile applications that need consistent behavior across iOS and Android browsers. This technique, as documented by CSS-Tricks, provides precise control over viewport calculations regardless of browser implementation.

CSS Custom Property Usage
1.my-element {2 height: 100vh; /* Fallback */3 height: calc(var(--vh, 1vh) * 100);4}5 6/* Alternative: Using directly */7.fullscreen-section {8 height: calc(var(--vh, 1vh) * 100);9 min-height: calc(var(--vh, 1vh) * 100);10}
JavaScript Viewport Calculation
1// Calculate and set viewport height custom property2function setViewportHeight() {3 let vh = window.innerHeight * 0.01;4 document.documentElement.style.setProperty('--vh', `${vh}px`);5}6 7// Set on initial load8setViewportHeight();9 10// Update on window resize11window.addEventListener('resize', setViewportHeight);

Handling Window Resize and Orientation Changes

Mobile devices can be rotated, changing the viewport dimensions significantly. To handle these scenarios, the solution should include a resize event listener:

window.addEventListener('resize', () => {
 let vh = window.innerHeight * 0.01;
 document.documentElement.style.setProperty('--vh', `${vh}px`);
});

Important consideration: Continuously updating the --vh value during scroll-triggered address bar changes can cause visible layout shifts and performance issues. For most use cases, it's better to calculate the viewport height once on page load and only update it when the window is actually resized or rotated.

As noted by CSS-Tricks, you should carefully consider performance implications and implement appropriate throttling or debouncing when handling resize events frequently.

Comparison of Viewport Height Units
UnitDefinitionUse CasePotential Issue
vhTraditional viewport heightDesktop layoutsInconsistent on mobile
dvhDynamic viewport heightAlways fill visible areaLayout shifts on scroll
svhSmall viewport heightElements must be fully visibleMay appear too small
lvhLarge viewport heightMaximum screen coverageMay extend under chrome
CSS + JSCustom property with JavaScriptCross-browser compatibilityRequires JavaScript
Best Practices for Mobile Viewport Handling

Key recommendations for reliable cross-device viewport unit implementation

Test on Real Devices

Browser DevTools responsive mode doesn't perfectly replicate mobile browser behavior. Always test on actual iOS and Android devices.

Provide Fallbacks

When using modern viewport units, provide fallbacks for browsers that don't support them.

Consider Accessibility

Layout shifts can affect users with motion sensitivity. Consider using prefers-reduced-motion.

Use Units Appropriately

Viewport units aren't always right for all layouts. Consider flexbox/grid for more dynamic sizing.

Progressive Enhancement Example
1/* Progressive enhancement with fallbacks */2.hero-section {3 /* Fallback for older browsers */4 height: 100vh;5 6 /* Modern browsers: small viewport height */7 height: 100svh;8 9 /* Even more modern: dynamic viewport height */10 height: 100dvh;11}

Frequently Asked Questions

Need Help with Mobile Development?

Our team specializes in building responsive, mobile-first web applications that work consistently across all devices and browsers.