What are vh, vw, vmin and vmax in CSS
vh, vw, vmin, and vmax are CSS viewport units where 1 unit equals 1% of a viewport dimension: vh is height, vw is width, vmin is the shorter side, and vmax is the longer side.
Theory
TL;DR
100vhfills the screen top to bottom,100vwleft to rightvminlocks to the shorter viewport side,vmaxto the longer one, both flip when the device rotates- Unlike
px, all four units recalculate on every resize and orientation change - Use viewport units for layout containers and hero sections, not for text (use
remthere) - Gotcha:
100vhon iOS Safari overshoots the visible area by ~80px because of the address bar
Quick example
.hero {
height: 100vh; /* fills the full viewport height */
width: 100vw; /* fills the full viewport width */
}
.card {
width: 40vmin; /* 40% of the shorter side, stays proportional on rotate */
height: 40vmin;
background: #4a90e2;
}On a 1200x800 desktop: 1vh = 8px, 1vw = 12px, 1vmin = 8px. Rotate a phone from portrait to landscape and vmin sticks to the shorter side automatically. No JavaScript needed.
Key difference from other units
Viewport units always calculate against the browser window, not a parent element. 50vw on a deeply nested element still means half the screen. Compare that to 50%, which means half of whatever contains the element. That distinction matters: use viewport units for full-bleed sections, not for components that need to scale relative to their container.
When to use
- Full-screen hero sections:
height: 100vh - Square elements that survive orientation changes:
width: 40vmin; height: 40vmin - Modals that fit under the browser chrome:
max-height: 90vh - Wide banners that stretch to use maximum horizontal space:
vmax - Body text: use
rem, not viewport units
Comparison table
| Unit | Based on | Portrait 390x844 | Landscape 844x390 | Use for |
|---|---|---|---|---|
vh | Viewport height | 1vh = 8.44px | 1vh = 3.9px | Full-height sections |
vw | Viewport width | 1vw = 3.9px | 1vw = 8.44px | Horizontal spans |
vmin | Smaller side | 1vmin = 3.9px | 1vmin = 3.9px | Square elements, no overflow |
vmax | Larger side | 1vmax = 8.44px | 1vmax = 8.44px | Max-stretch banners |
px | Fixed | Always 1px | Always 1px | Precise, non-responsive |
How the browser calculates these
The browser measures its initial containing block (the viewport rectangle, scrollbars excluded), divides by 100, and multiplies by your number. This recalculates on every resize event and orientation change. On mobile, iOS Safari historically included the address bar area in its vh calculation, so 100vh was taller than the visible screen by roughly 80px. Chrome 108 and iOS 15.4 introduced dvh (dynamic viewport height) to fix this.
Common mistakes
Using height: 100vh for a full-screen mobile layout:
/* Wrong: iOS Safari clips ~80px of content under the address bar */
.app { height: 100vh; }
/* Fix */
.app {
height: 100vh; /* fallback for older browsers */
height: 100dvh; /* Chrome 108+, iOS 15.4+ - overrides vh if supported */
}I have seen this break the bottom navigation on more than one production Next.js app. Two lines fix it completely.
Using width: 100vw for a full-width nav:
/* Wrong: includes scrollbar width (~15px), causes horizontal scroll */
nav { width: 100vw; }
/* Fix */
nav { width: 100%; }No fallback for older browsers:
.box { width: 30%; } /* fallback */
.box { width: 30vmin; } /* modern browsers override */IE 9-11 had partial support for vh and vw but none for vmin and vmax. A percentage fallback covers it.
Real-world usage
- Tailwind CSS:
h-screencompiles toheight: 100vh - Bootstrap 5: uses
--bs-full-height: 100vhin offcanvas and modal components - shadcn/ui: fullscreen loaders and backdrops use
height: 100vh - Three.js canvas:
width: 100vw; height: 100vhfor scene containers - Next.js landing pages: hero sections and full-screen modal backdrops
Follow-up questions
Q: What is the difference between 100vh and height: 100%?
A: 100vh always equals the viewport height. height: 100% requires every parent element up to <html> to have an explicit height set, otherwise the element collapses to zero.
Q: Why does 100vh fail on mobile Safari?
A: Safari included the address bar area in its vh calculation, making 100vh taller than the visible screen. Use 100dvh on iOS 15.4+ and Chrome 108+. For older mobile browsers, the JS workaround with a CSS custom property still works.
Q: When would you pick vmin over vw?
A: When the element needs to stay visible in both portrait and landscape. vmin always references the shorter side, so a 40vmin element stays fully on screen after rotation without overflow.
Q: What are dvh, svh, and lvh?
A: Newer units from CSS Viewport Units Level 4. dvh is dynamic (tracks the actual visible height as the address bar appears or hides). svh is the smallest viewport (bar fully visible). lvh is the largest (bar fully hidden). All major browsers support them from 2023 onward.
Examples
Full-screen hero section
.hero {
width: 100vw;
height: 100vh; /* baseline for all browsers */
height: 100dvh; /* dynamic height for modern mobile */
display: flex;
align-items: center;
justify-content: center;
background: #1a1a2e;
}The standard pattern for landing page heroes. The second height declaration overrides the first in browsers that support dvh, so no separate media query is needed.
Responsive modal
.modal {
width: clamp(300px, 80vw, 800px);
height: clamp(200px, 70vh, 600px);
max-width: 90vmin;
margin: auto;
background: white;
border-radius: 8px;
overflow: auto;
}On a 1920x1080 desktop this lands at 800x600px. On a portrait phone it scales down to about 310x480px. The 90vmin cap prevents overflow in either orientation, so the modal works the same in portrait and landscape without extra media queries.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.