Skip to main content

CSS Viewport Units: Mastering Dynamic Viewport Sizes

Created: February 27, 2026 Larry Qu 7 min read

CSS Viewport Units have evolved significantly with new units like svh (small viewport height), dvh (dynamic viewport height), and lvh (large viewport height). These units solve long-standing issues with mobile browser address bars and create more reliable responsive designs.

The Problem: Traditional Viewport Units

Before these new units, designers struggled with 100vh not actually representing the visible viewport:

/* Traditional - often shows unwanted scrollbars on mobile */
.hero {
  height: 100vh;
  overflow: hidden;
}

The issue: Mobile browsers have dynamic UI elements (address bar, navigation) that change the visible viewport area.

How Mobile Browsers Work

┌─────────────────────────┐
│  Browser UI (address)  │  <- Often hidden
├─────────────────────────┤
│                         │
│    Visible Content      │  <- What users see
│                         │
├─────────────────────────┤
│  Browser UI (nav)       │  <- Often hidden
└─────────────────────────┘

When the user scrolls, the browser UI hides, but 100vh doesn’t adjust:

/* Problem: Content gets hidden behind browser UI */
.hero {
  height: 100vh; /* Fixed - doesn't adapt */
  background: linear-gradient(to bottom, blue, purple);
}

The Solution: Dynamic Viewport Units

CSS now provides multiple viewport units to handle different scenarios:

Viewport Units Comparison

/* Small Viewport Height - smallest dynamic viewport */
height: 100svh;  /* Always fits within visible area */

/* Large Viewport Height - largest dynamic viewport */
height: 100lvh;  /* Includes potentially hidden areas */

/* Dynamic Viewport Height - matches current visible area */
height: 100dvh;  /* Adapts as UI shows/hides */

/* Traditional - problematic on mobile */
height: 100vh;   /* Fixed, doesn't adapt */

Small Viewport Height (svh)

The svh unit represents the smallest viewport height when all browser UI is visible:

/* Guaranteed to be visible without scrolling */
.hero {
  height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
}

When to Use svh

  • Hero sections that should always be fully visible
  • Full-screen modals and dialogs
  • Landing pages
  • Any element that shouldn’t scroll when first viewed
/* Full-screen modal - always visible */
.modal-overlay {
  position: fixed;
  inset: 0;
  height: 100svh;
  width: 100svw;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
}

Large Viewport Height (lvh)

The lvh unit represents the largest viewport height when browser UI is hidden:

/* May have parts hidden initially */
.hero {
  height: 100lvh;
}

When to Use lvh

  • Background elements that can extend beyond initial view
  • Scroll-triggered animations
  • Elements designed to scroll into view
/* Background that extends fully */
.page-background {
  height: 100lvh;
  width: 100%;
  position: fixed;
  top: 0;
  left: 0;
  background: linear-gradient(180deg, #1a1a2e, #16213e);
  z-index: -1;
}

Dynamic Viewport Height (dvh)

The dvh unit dynamically matches the current visible viewport as browser UI shows/hides:

/* Automatically adapts to UI state */
.hero {
  height: 100dvh;
  transition: height 0.3s ease;
}

With Scroll

/* Content that fills viewport while scrolling */
.fullscreen-section {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* Works smoothly as address bar shows/hides */
.main-content {
  flex: 1;
  overflow-y: auto;
}

Viewport Width Units

Similar units exist for width:

/* Viewport Width */
width: 100vw;     /* Traditional - may cause horizontal scroll */

/* Small Viewport Width */
width: 100svw;    /* Smallest visible width */

/* Large Viewport Width */
width: 100lvw;    /* Largest visible width */

/* Dynamic Viewport Width */
width: 100dvw;    /* Dynamic width */

Practical Examples

Example 1: Mobile-First Hero Section

/* Always visible hero section */
.hero {
  height: 100svh;
  min-height: -webkit-fill-available;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 2rem;
  box-sizing: border-box;
}

.hero h1 {
  font-size: clamp(2rem, 5vw, 4rem);
  margin: 0;
}

.hero p {
  font-size: clamp(1rem, 2.5vw, 1.5rem);
  max-width: 60ch;
}

/* Fallback for older browsers */
@supports not (height: 100svh) {
  .hero {
    min-height: 100vh;
    min-height: 100dvh;
  }
}

Example 2: Fixed Navigation

/* Header that accounts for dynamic viewport */
.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 60px;
  background: white;
  z-index: 100;
}

.main-content {
  padding-top: 60px;
  /* Ensure content isn't hidden */
  min-height: calc(100svh - 60px);
}

Example 3: Scrollable Content Area

/* Layout with fixed header and scrollable content */
.app-layout {
  display: grid;
  grid-template-rows: auto 1fr;
  height: 100dvh;
  height: 100svh;
  overflow: hidden;
}

.header {
  background: #333;
  color: white;
  padding: 1rem;
}

.content {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.content-inner {
  padding: 2rem;
}

Example 4: Split Screen Layout

/* Full-height split screen */
.split-layout {
  display: grid;
  grid-template-columns: 1fr;
  height: 100dvh;
  height: 100svh;
}

@media (min-width: 768px) {
  .split-layout {
    grid-template-columns: 1fr 1fr;
  }
}

.panel {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
}

Example 5: Card Grid with Viewport Sizing

/* Responsive card grid */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1rem;
  padding: 1rem;
  height: calc(100dvh - 100px);
  overflow-y: auto;
}

.card {
  background: white;
  border-radius: 8px;
  padding: 1.5rem;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

Example 6: Full-Screen Video Background

/* Video that always fills visible area */
.video-container {
  position: relative;
  height: 100svh;
  width: 100vw;
  overflow: hidden;
}

.video-bg {
  position: absolute;
  top: 50%;
  left: 50%;
  min-width: 100%;
  min-height: 100%;
  transform: translate(-50%, -50%);
}

.video-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,0.4);
  display: flex;
  align-items: center;
  justify-content: center;
}

Viewport Units in CSS Functions

With calc()

/* Calculate heights with viewport units */
.header {
  height: calc(60px + 2svh);
}

.content {
  min-height: calc(100dvh - 60px - 40px);
}

With clamp()

/* Responsive sizing */
.hero-title {
  font-size: clamp(2rem, 5dvh, 5rem);
}

.section {
  min-height: clamp(300px, 50dvh, 600px);
}

With container queries

/* Works with container queries too */
.card-container {
  container-type: inline-size;
}

.card {
  height: 100cqh;  /* Container query height */
}

Legacy Browser Support

Use @supports for progressive enhancement:

/* Modern browsers */
.hero {
  height: 100svh;
}

/* Fallback for older browsers */
@supports not (height: 100svh) {
  .hero {
    height: 100vh;
    min-height: -webkit-fill-available;
    min-height: -moz-available;
  }
}

JavaScript Fallback

// Set CSS custom property for older browsers
function setViewportHeight() {
  const vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
}

// Use in CSS: height: calc(100 * var(--vh));
setViewportHeight();
window.addEventListener('resize', setViewportHeight);

Browser Support

Unit Chrome Firefox Safari Edge
svh 108+ 101+ 15.4+ 108+
dvh 108+ 101+ 15.4+ 108+
lvh 108+ 101+ 15.4+ 108+
/* Feature detection */
@supports (height: 100svh) {
  .hero { height: 100svh; }
}

Common Patterns

Pattern 1: Mobile Full Screen

.fullscreen-mobile {
  height: 100svh;
  height: 100dvh;
  min-height: -webkit-fill-available;
}

Pattern 2: Scroll Container

.scroll-container {
  height: 100dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

Pattern 3: Fixed Background

.fixed-background {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  width: 100svw;
  height: 100vh;
  height: 100dvh;
  z-index: -1;
}
.page-wrapper {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  min-height: 100svh;
}

.content {
  flex: 1;
}

.footer {
  margin-top: auto;
}

Best Practices

1. Use svh for Initial View

/* For elements that should be visible immediately */
.hero, .modal, .overlay {
  height: 100svh;
}

2. Use dvh for Scrollable Content

/* For scrollable containers */
.main-content {
  height: 100dvh;
  overflow: hidden;
}

.inner-scroll {
  height: 100%;
  overflow-y: auto;
}

3. Combine with Modern CSS

/* Works great with container queries */
.card-container {
  container-type: inline-size;
}

.card {
  height: 100cqh;
}

@container (max-width: 400px) {
  .card {
    height: 50svh;
  }
}

4. Test on Real Devices

/* Always test on actual mobile devices */
.hero {
  height: 100svh;
  /* Test with and without this */
  min-height: -webkit-fill-available;
}

Debugging Viewport Issues

Chrome DevTools

  1. Open DevTools (F12)
  2. Toggle device toolbar (Ctrl+Shift+M)
  3. Test with address bar shown/hidden
  4. Check computed styles for actual values

Common Issues

/* Issue: Horizontal scroll */
/* Solution: Use 100svw instead of 100vw */
body {
  width: 100svw;
}

/* Issue: Content hidden behind fixed elements */
/* Solution: Account for header height */
.main-content {
  min-height: calc(100svh - 60px);
}

/* Issue: Animation jank */
/* Solution: Use dvh for smooth transitions */
.hero {
  height: 100dvh;
  transition: height 0.3s ease;
}

External Resources

Summary

Modern CSS viewport units solve long-standing mobile layout issues:

  • svh (Small Viewport Height): Use for elements that should always be visible
  • lvh (Large Viewport Height): Use for backgrounds and elements that can extend
  • dvh (Dynamic Viewport Height): Use for scrollable content areas
  • svw/dvw/lvw: Same concepts applied to width
  • Browser Support: Chrome 108+, Firefox 101+, Safari 15.4+, Edge 108+

Start using these units today to create more reliable, mobile-friendly layouts!

Resources

Comments

Share this article

Scan to read on mobile

👍 Was this article helpful?