CSS Viewport Units, In Depth

📅 July 11, 2025 | 👨‍💻 Web Development | 📐 CSS Units

The complete guide to vw, vh, vmin, and vmax

Viewport units are CSS units that scale in proportion to the size of the screen, which makes them a cornerstone of responsive web design. In this article we'll go beyond the basics and look at advanced techniques and real-world examples for building truly fluid, responsive layouts with viewport units.

1. The Viewport Unit Family

1.1 The Core Viewport Units

/* Basic viewport unit examples */ .viewport-example { width: 50vw; /* 50% of the viewport width */ height: 30vh; /* 30% of the viewport height */ font-size: 2vmin; /* 2% of the smaller of vw and vh */ margin: 1vmax; /* 1% of the larger of vw and vh */ }

1.2 How Viewport Units Compute in Practice

/* Screen size: 1920x1080 */ .example-1920x1080 { width: 50vw; /* 960px (1920 × 0.5) */ height: 30vh; /* 324px (1080 × 0.3) */ font-size: 2vmin; /* 21.6px (1080 × 0.02) - vh is smaller */ margin: 1vmax; /* 19.2px (1920 × 0.01) - vw is larger */ } /* Screen size: 375x667 (iPhone) */ .example-375x667 { width: 50vw; /* 187.5px (375 × 0.5) */ height: 30vh; /* 200.1px (667 × 0.3) */ font-size: 2vmin; /* 7.5px (375 × 0.02) - vw is smaller */ margin: 1vmax; /* 6.67px (667 × 0.01) - vh is larger */ }

2. Getting More Out of vw (Viewport Width)

2.1 Responsive Containers

/* Fully responsive container */ .responsive-container { width: 90vw; /* 90% of the viewport width */ max-width: 1200px; /* cap the maximum width */ margin: 0 auto; padding: 2vw; /* responsive padding */ } /* Flexible grid system */ .flexible-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2vw; /* responsive gap */ padding: 3vw; /* responsive padding */ } /* Responsive card */ .responsive-card { width: 100%; padding: 4vw; /* responsive padding */ border-radius: 1vw; /* responsive rounded corners */ box-shadow: 0 0.5vw 1vw rgba(0, 0, 0, 0.1); }

2.2 Fluid Typography

/* Basic fluid font size */ .responsive-text { font-size: clamp(1rem, 2.5vw, 2rem); } /* Fluid font for headings */ .responsive-heading { font-size: clamp(1.5rem, 4vw, 3rem); line-height: 1.2; } /* Fluid font for subheadings */ .responsive-subheading { font-size: clamp(1.125rem, 3vw, 1.5rem); line-height: 1.3; } /* Fluid font for body copy */ .responsive-body { font-size: clamp(0.875rem, 2vw, 1rem); line-height: 1.6; }

💡 Put clamp() to Work

The clamp(minimum, preferred, maximum) function prevents viewport units from producing extreme sizes. By defining a minimum and a maximum, your values only fluctuate within a sensible range.

3. Getting More Out of vh (Viewport Height)

3.1 Full-Screen Layouts

/* Full-screen section */ .fullscreen-section { width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center; background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); } /* Hero section */ .hero-section { width: 100vw; height: 100vh; position: relative; overflow: hidden; } .hero-content { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; color: white; } /* Sticky footer */ .sticky-footer { position: fixed; bottom: 0; left: 0; width: 100vw; height: 10vh; background: #333; color: white; display: flex; align-items: center; justify-content: center; }

3.2 Fixing the Mobile Address Bar Problem

⚠️ The Mobile vh Problem

On mobile browsers, the vh value changes as the address bar appears and disappears, which can break your layout. Let's look at the ways to fix this.

/* Fix using a CSS custom property */ :root { --vh: 1vh; } /* Compute the real viewport height with JavaScript */ function setVH() { const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); } setVH(); window.addEventListener('resize', setVH); /* Use it in your CSS */ .mobile-friendly-height { height: calc(var(--vh, 1vh) * 100); } /* Or fix it with CSS alone */ .mobile-height-fix { height: 100vh; height: 100dvh; /* dynamic viewport height */ }

Modern browsers also ship two siblings of dvh: svh (small viewport height, measured with the browser UI expanded) and lvh (large viewport height, measured with it collapsed). In short: svh is the guaranteed-visible minimum, lvh is the maximum, and dvh tracks the current value in real time as the address bar shows or hides — which is why 100dvh is usually the right replacement for 100vh on mobile.

4. Getting More Out of vmin and vmax

4.1 Working with vmin

Because vmin is based on the smaller of vw and vh, it keeps elements at a consistent size regardless of screen orientation.

/* Square element */ .square-element { width: 50vmin; height: 50vmin; background: #2196f3; border-radius: 2vmin; } /* Responsive icon */ .responsive-icon { width: 8vmin; height: 8vmin; font-size: 4vmin; } /* Responsive button */ .responsive-button { padding: 2vmin 4vmin; font-size: 3vmin; border-radius: 1vmin; min-width: 20vmin; min-height: 8vmin; } /* Responsive avatar */ .responsive-avatar { width: 15vmin; height: 15vmin; border-radius: 50%; border: 0.5vmin solid #fff; }

4.2 Working with vmax

Because vmax is based on the larger of vw and vh, elements can grow bigger as the screen gets bigger.

/* Larger elements on larger screens */ .large-screen-element { width: 30vmax; height: 30vmax; background: #ff9800; border-radius: 3vmax; } /* Responsive background image */ .responsive-bg { background-size: 100vmax auto; background-position: center; background-repeat: no-repeat; } /* Responsive logo */ .responsive-logo { width: 25vmax; height: auto; max-width: 300px; }

5. Advanced Techniques

5.1 Combining Viewport Units

/* Mixing viewport units with other units */ .complex-layout { width: calc(100vw - 4rem); height: calc(100vh - 2rem); margin: 1rem; padding: 2vw; } /* Responsive grid */ .responsive-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(20vw, 1fr)); gap: 2vw; padding: 3vw; } /* Responsive flexbox */ .responsive-flex { display: flex; flex-wrap: wrap; gap: 2vw; padding: 2vw; } .responsive-flex > * { flex: 1 1 calc(33.333% - 2vw); min-width: 250px; }

5.2 CSS Custom Properties and Viewport Units

/* Manage viewport units through CSS custom properties */ :root { --spacing-xs: 1vw; --spacing-sm: 2vw; --spacing-md: 3vw; --spacing-lg: 4vw; --spacing-xl: 5vw; --font-size-xs: 2vmin; --font-size-sm: 3vmin; --font-size-md: 4vmin; --font-size-lg: 5vmin; --font-size-xl: 6vmin; --border-radius: 1vmin; --shadow: 0 1vmin 2vmin rgba(0, 0, 0, 0.1); } /* Use the variables */ .consistent-spacing { padding: var(--spacing-md); margin: var(--spacing-sm); border-radius: var(--border-radius); box-shadow: var(--shadow); font-size: var(--font-size-md); }

6. Real-World Examples

6.1 A Responsive Card Component

/* Responsive card */ .responsive-card { width: 100%; max-width: 400px; padding: 4vw; border-radius: 2vmin; box-shadow: 0 2vmin 4vmin rgba(0, 0, 0, 0.1); background: white; transition: transform 0.3s ease; } .responsive-card:hover { transform: translateY(-1vmin); } .card-title { font-size: clamp(1.25rem, 4vmin, 1.5rem); margin-bottom: 2vmin; color: #333; } .card-content { font-size: clamp(0.875rem, 3vmin, 1rem); line-height: 1.6; color: #666; margin-bottom: 3vmin; } .card-button { padding: 2vmin 4vmin; font-size: clamp(0.875rem, 3vmin, 1rem); border: none; border-radius: 1vmin; background: #2196f3; color: white; cursor: pointer; transition: background 0.3s; } .card-button:hover { background: #1976d2; }

6.2 A Responsive Navigation Bar

/* Responsive navigation */ .responsive-nav { width: 100vw; height: 10vh; background: #333; display: flex; align-items: center; justify-content: space-between; padding: 0 4vw; position: fixed; top: 0; left: 0; z-index: 1000; } .nav-logo { font-size: clamp(1.25rem, 4vmin, 1.5rem); color: white; font-weight: bold; } .nav-menu { display: flex; gap: 4vw; list-style: none; } .nav-item { font-size: clamp(0.875rem, 3vmin, 1rem); } .nav-link { color: white; text-decoration: none; padding: 1vmin 2vmin; border-radius: 1vmin; transition: background 0.3s; } .nav-link:hover { background: rgba(255, 255, 255, 0.1); } /* Mobile menu */ .mobile-menu-toggle { display: none; font-size: 4vmin; color: white; background: none; border: none; cursor: pointer; } @media (max-width: 768px) { .nav-menu { display: none; } .mobile-menu-toggle { display: block; } }

7. Performance Optimization

7.1 Using Viewport Units Efficiently

7.2 Browser Compatibility

/* Fallbacks for browser compatibility */ .fallback-example { width: 90%; /* fallback */ width: 90vw; /* modern browsers */ font-size: 16px; /* fallback */ font-size: clamp(16px, 2.5vw, 24px); /* modern browsers */ } /* Feature detection with @supports */ @supports (width: 1vw) { .viewport-supported { width: 50vw; height: 50vh; } } @supports not (width: 1vw) { .viewport-fallback { width: 50%; height: 300px; } }

8. Pitfalls and Troubleshooting

8.1 Common Problems

⚠️ Viewport Unit Pitfalls

  • Extreme size swings: sizes become inappropriate on very small or very large screens
  • Mobile address bar issues: the well-known vh problem in mobile browsers
  • Performance issues: overuse can degrade rendering performance
  • Accessibility concerns: viewport-based sizing can override user preferences like browser zoom

8.2 Solutions

/* 1. Prevent extreme sizes */ .safe-viewport { font-size: clamp(12px, 2.5vw, 24px); width: clamp(300px, 50vw, 800px); } /* 2. Fix the mobile address bar problem */ .mobile-safe-height { height: 100vh; height: 100dvh; /* dynamic viewport height */ } /* 3. Optimize performance */ .optimized-viewport { will-change: auto; transform: translateZ(0); /* GPU acceleration */ } /* 4. Respect accessibility preferences */ @media (prefers-reduced-motion: reduce) { .viewport-animation { transition: none; } }

✅ Viewport Unit Best Practices

  • Use clamp() to keep values within a safe range
  • Keep values consistent with CSS custom properties
  • Account for browser compatibility
  • Apply performance optimizations
  • Factor in accessibility considerations
  • Test on real devices

9. The PX to VW Converter and Viewport Units

Working effectively with viewport units requires accurate conversion. With the PX to VW Converter, you can turn the pixel values from your design mockups into precise viewport units.

🔧 Practical Tip

Convert the px values from your design mockups with the PX to VW Converter, then combine the results with clamp() to build layouts that are both safe and responsive. That way you can deliver an optimal user experience at every screen size.

10. Conclusion

Viewport units are an essential part of modern web design. Used well, vw, vh, vmin, and vmax let you build perfectly responsive websites. Just be sure to keep the pitfalls in mind and apply the optimization techniques above to keep the user experience solid. And with the PX to VW Converter, working with viewport units becomes that much more efficient.