Suggest an editImprove this articleRefine the answer for “What are vh, vw, vmin and vmax in CSS”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**CSS viewport units** (`vh`, `vw`, `vmin`, `vmax`) each equal 1% of a viewport dimension: height, width, the smaller side, or the larger side. ```css .hero { height: 100vh; } /* full screen height */ .card { width: 40vmin; } /* 40% of shorter side */ ``` **Key point:** `100vh` on mobile Safari can overshoot the visible area by ~80px. Use `100dvh` in Chrome 108+ and iOS 15.4+.Shown above the full answer for quick recall.Answer (EN)Image**`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 - `100vh` fills the screen top to bottom, `100vw` left to right - `vmin` locks to the shorter viewport side, `vmax` to 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 `rem` there) - Gotcha: `100vh` on iOS Safari overshoots the visible area by ~80px because of the address bar ### Quick example ```css .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:** ```css /* 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:** ```css /* Wrong: includes scrollbar width (~15px), causes horizontal scroll */ nav { width: 100vw; } /* Fix */ nav { width: 100%; } ``` **No fallback for older browsers:** ```css .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-screen` compiles to `height: 100vh` - Bootstrap 5: uses `--bs-full-height: 100vh` in offcanvas and modal components - shadcn/ui: fullscreen loaders and backdrops use `height: 100vh` - Three.js canvas: `width: 100vw; height: 100vh` for 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 ```css .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 ```css .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.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.