Viewport Units in CSS: vw, vh, vmin, and vmax
Introduction
Viewport units are relative CSS lengths based on the dimensions of the browser viewport. The four classic units are vw, vh, vmin, and vmax. They allow dimensions, spacing, and typography to respond continuously as the visible browser area changes, without requiring a breakpoint for every intermediate size.
These units are useful for full-height sections, fluid headings, viewport-aware overlays, proportional visualizations, games, and responsive decorative elements. They are also easy to misuse. Unbounded viewport-based text can become unreadably small or enormous, 100vw can create horizontal overflow, and traditional 100vh has historically conflicted with changing mobile browser toolbars.
Modern CSS extends the viewport-unit family with small, large, and dynamic viewport variants. A production-ready approach therefore begins with the simple calculations, then chooses the viewport definition and constraints that match the interface.
What Is the Viewport?
The viewport is the area through which the browser presents the page. On a desktop browser it is approximately the content area of the window, excluding browser chrome. On mobile, the relationship is more complicated because the address bar, navigation controls, virtual keyboard, safe areas, and zoom can change what users can currently see.
If a viewport is 1200 CSS pixels wide and 800 CSS pixels high, width-based viewport units use 1200 as their reference and height-based units use 800. The values use CSS pixels, not raw hardware pixels.
Viewport width = 1200px
Viewport height = 800px
1vw = 12px
1vh = 8px
1vmin = 8px
1vmax = 12px
The Layout and Visual Viewports
Browsers can expose both a layout viewport and a visual viewport. The layout viewport is the coordinate space used for much page layout, while the visual viewport represents the area currently visible after effects such as pinch zoom or an on-screen keyboard. These concepts are related but not interchangeable.
Viewport CSS units follow definitions established by CSS and the browser, and they do not necessarily shrink for every transient overlay such as a virtual keyboard. If a keyboard-sensitive interface must keep a focused control visible, test the actual browsers and consider the Visual Viewport API or scrolling behavior in addition to CSS units.
Quick Comparison
| Unit | Meaning | At 1200 x 800 | Typical use |
|---|---|---|---|
1vw | 1% of viewport width | 12px | Horizontal fluid sizing |
1vh | 1% of viewport height | 8px | Viewport-height sections |
1vmin | 1% of smaller dimension | 8px | Orientation-safe square sizing |
1vmax | 1% of larger dimension | 12px | Large visual coverage |
Understanding vw
One vw equals one percent of the viewport width. If the viewport is 1000 CSS pixels wide, 1vw is 10 pixels and 50vw is 500 pixels. The browser recalculates the used value as the viewport width changes.
.box {
width: 50vw;
}
In a 1200-pixel-wide viewport, this box is 600 pixels wide regardless of whether its parent is 1000 pixels or 700 pixels. That direct viewport dependency distinguishes vw from percentage width.
vw Versus Percentage Width
A percentage width generally follows the containing block, while vw follows the viewport. Suppose the viewport is 1200 pixels wide and a parent is 600 pixels wide. A child at width: 50% is usually 300 pixels. A child at width: 50vw is 600 pixels and can therefore equal or overflow the entire parent.
.parent {
width: 600px;
}
.container-relative { width: 50%; }
.viewport-relative { width: 50vw; }
Use percentage or flexible layout tracks when an element belongs to a container. Use vw when the viewport itself is the intended reference.
Viewport Width for Fluid Typography
Viewport width can make headings grow continuously with the screen. A rule such as font-size: 5vw, however, has no safety limits. It becomes 16 pixels on a 320-pixel viewport and 128 pixels on a 2560-pixel viewport. Neither result may fit the design.
.hero-title {
font-size: clamp(2rem, 5vw, 4rem);
}
The heading now stops shrinking at 2rem and stops growing at 4rem. The vw value operates only between those boundaries. This bounded pattern is more robust for content, translations, browser zoom, and very wide screens.
Viewport Width for Horizontal Spacing
Horizontal padding can follow viewport width so compact screens receive smaller gutters and wide screens receive more breathing room. Again, a limit prevents extreme output.
.section {
padding-inline: clamp(1rem, 5vw, 4rem);
}
This pattern is useful for page-level spacing. Component internals often should follow the component's own size or typography instead, using container units, rem, or em.
Understanding vh
One vh equals one percent of the viewport height. In an 800-pixel-high viewport, 1vh is 8 pixels and 100vh is 800 pixels. This direct height reference makes vh attractive for hero sections, application shells, slides, and centered empty states.
.hero {
min-height: 100vh;
display: grid;
place-items: center;
}
The section is at least as tall as the viewport and can grow if its content needs more space.
height Versus min-height
A fixed height: 100vh forces a box to one viewport height. If a translated heading, validation message, enlarged text, or additional navigation requires more space, content can overflow or be clipped. min-height: 100vh establishes a full-height minimum while preserving natural growth.
Fixed height can be appropriate for a controlled canvas, presentation slide, or media stage, but content-heavy sections should usually remain flexible. Always test the longest realistic content rather than only an ideal screenshot.
The Mobile 100vh Problem
Mobile browsers use interface controls that expand and collapse while the user scrolls. Historically, 100vh often represented a viewport state that did not match the currently visible area. A full-height element could extend behind the address bar or navigation controls, hiding content or forcing unexpected scrolling.
Browsers made different choices to avoid layouts jumping whenever toolbars moved. The result was stable in some cases but visually inaccurate in others. Modern small, large, and dynamic viewport units give authors a clearer choice.
Small Viewport Units
The small viewport units use the viewport size when dynamic browser interfaces are expanded and consume the most space. svh is the small viewport height, and svw is the small viewport width. A panel sized to 100svh is designed to fit within the conservative viewport state.
.welcome-panel {
min-height: 100svh;
}
This is useful when critical controls must remain visible even while browser chrome is present. When the controls collapse, extra visible space may remain because the small unit does not expand dynamically.
Large Viewport Units
The large viewport units use the viewport size when retractable browser controls are minimized. 100lvh describes the largest available viewport height in that browsing state. It can create immersive media or visual backgrounds that cover the expanded area.
.immersive-background {
min-height: 100lvh;
}
Critical content near the edge can be obscured when controls reappear, so large viewport sizing is better suited to backgrounds and experiences that tolerate temporary occlusion.
Dynamic Viewport Units
Dynamic viewport units track the current viewport as browser controls expand or retract. dvh and dvw can therefore match the visible mobile area more closely than traditional vh. A modern full-screen section often uses min-height: 100dvh.
.app-screen {
min-height: 100dvh;
}
The value can update during scrolling, which may cause layout or visual movement. Use it when accurate current coverage matters, and test whether dynamic resizing feels stable for the specific interface.
How Traditional vh Relates to Modern Variants
Modern specifications define the default viewport units in relation to a viewport size chosen for compatibility, commonly corresponding to the large viewport size. The exact historical behavior that developers remember can differ across older browser versions. For new mobile layouts, an explicit svh, lvh, or dvh communicates intent more clearly.
A progressive pattern can provide a traditional fallback and then override it when dynamic units are supported.
.hero {
min-height: 100vh;
min-height: 100dvh;
}
Understanding vmin
One vmin equals one percent of the smaller viewport dimension. In a 1200-by-800 landscape viewport, it is 8 pixels because height is smaller. In a 400-by-800 portrait viewport, it is 4 pixels because width is smaller.
.responsive-mark {
inline-size: 20vmin;
block-size: 20vmin;
}
The square remains governed by the dimension most likely to constrain it. This makes vmin useful for game boards, circular controls, illustrations, loading graphics, and visualizations that should fit in either orientation.
vmin Across Orientation Changes
On a 400-by-800 portrait screen, 10vmin is 40 pixels. After rotation to 800 by 400, it remains 40 pixels because the smaller dimension is still 400. This behavior can provide more consistent fit than vw, which would double after rotation.
Consistency does not mean no change on every device. The smaller dimension can still change with split-screen windows, desktop resizing, browser controls, or device geometry.
Understanding vmax
One vmax equals one percent of the larger viewport dimension. In a 1200-by-800 viewport it is 12 pixels. In a 400-by-900 viewport it is 9 pixels. Values based on vmax grow with the dimension that offers the greatest span.
.decorative-disc {
inline-size: 60vmax;
block-size: 60vmax;
border-radius: 50%;
}
Vmax is useful for oversized decorative backgrounds that should cover a large region regardless of orientation. It needs limits for functional content because the larger dimension can make an element far too large for the smaller dimension.
vmin Versus vmax
| Viewport | 1vmin | 1vmax | Interpretation |
|---|---|---|---|
| 1200 x 800 | 8px | 12px | Height is smaller; width is larger |
| 400 x 900 | 4px | 9px | Width is smaller; height is larger |
| 700 x 700 | 7px | 7px | Dimensions are equal |
Use vmin when fit inside the screen is the primary concern. Use vmax when broad visual coverage is the goal. Apply minimums and maximums when the element carries text or interaction.
Logical Viewport Units vi and vb
CSS also provides logical viewport units. vi represents one percent of the viewport's inline axis, while vb represents one percent of its block axis. In common horizontal writing, vi behaves like vw and vb behaves like vh. In vertical writing, their physical direction changes.
.reading-panel {
max-inline-size: 80vi;
min-block-size: 60vb;
}
Logical units can make internationalized layouts more adaptable because they describe flow-relative intent rather than assuming horizontal text.
Modern Viewport Unit Families
| Family | Width | Height | Inline/block | Min/max |
|---|---|---|---|---|
| Default | vw | vh | vi, vb | vmin, vmax |
| Small | svw | svh | svi, svb | svmin, svmax |
| Large | lvw | lvh | lvi, lvb | lvmin, lvmax |
| Dynamic | dvw | dvh | dvi, dvb | dvmin, dvmax |
Most everyday applications need only a few of these. The complete family exists so authors can combine writing-mode awareness with explicit mobile viewport behavior.
A Production-Ready Hero Section
A robust hero should fill the current viewport when content is short, grow when content is long, protect text width, and include bounded spacing. It should also leave a hint of following content when that is part of the design rather than forcing every hero to consume exactly one screen.
.hero {
min-height: min(100dvh, 52rem);
display: grid;
align-content: center;
padding: clamp(1rem, 4vw, 4rem);
}
.hero__title {
max-width: 18ch;
font-size: clamp(2rem, 6vw, 4.5rem);
}
The maximum height prevents an unusually tall desktop window from creating excessive empty space, while natural height still allows the content to grow.
Full-Height Application Layout
Dashboards and editors often need a viewport-height shell with independently scrolling content. A grid can reserve natural header and footer rows while the middle track consumes remaining space.
.app {
min-height: 100dvh;
display: grid;
grid-template-rows: auto minmax(0, 1fr) auto;
}
.app__main {
overflow: auto;
}
The minmax(0, 1fr) track permits the middle region to shrink and scroll. Without it, min-content sizing can make the shell exceed the viewport even when its outer height appears correct.
Dialogs and Overlays
A fixed overlay can cover the viewport with inset: 0; it does not need width: 100vw and height: 100vh. Viewport units are still useful for limiting the dialog inside that overlay.
.overlay {
position: fixed;
inset: 0;
display: grid;
place-items: center;
padding: 1rem;
}
.dialog {
width: min(90vw, 42rem);
max-height: min(85dvh, 48rem);
overflow: auto;
}
This allows the dialog to fit smaller viewports while protecting readable dimensions on larger screens.
Sidebars and Viewport Width
A sidebar at 30vw may look balanced on one laptop but become 600 pixels wide on a 2000-pixel viewport. Bounded sizing captures the fluid intention without allowing unreasonable growth.
.sidebar {
width: clamp(15rem, 25vw, 20rem);
}
For a sidebar inside a constrained application shell, container-relative sizing or Grid tracks may be more appropriate than viewport sizing. The reference should match the ownership boundary.
Scrollbar Behavior and 100vw
Depending on browser and scrollbar behavior, 100vw can include width associated with the vertical scrollbar while the normal document content area does not. An element set to 100vw can then be slightly wider than the page and trigger horizontal scrolling.
If the goal is simply to fill a normal parent, use width: 100% or rely on the default auto width of a block element. Reserve 100vw for a deliberate viewport-based effect and test pages both with and without vertical scrolling.
Breaking Out of a Centered Container
Authors sometimes use viewport units to create a full-bleed image inside a centered article. The technique requires careful margin calculations and can interact with scrollbars. Modern page structure often makes a full-width section outside the constrained content wrapper simpler.
.full-bleed {
width: 100vw;
margin-inline: calc(50% - 50vw);
}
If this pattern is used, test scrollbar configurations, writing direction, zoom, and nested transforms. Structural layout is preferable when the markup can express the full-bleed region directly.
Safe Areas and Display Cutouts
Viewport coverage on edge-to-edge mobile devices can overlap notches, rounded corners, or home indicators. Environment variables such as safe-area-inset-top and safe-area-inset-bottom can protect critical content when the page is configured for edge-to-edge display.
.fullscreen-toolbar {
min-height: 100dvh;
padding-top: max(1rem, env(safe-area-inset-top));
padding-bottom: max(1rem, env(safe-area-inset-bottom));
}
Safe-area padding complements viewport sizing; one does not replace the other. Test real devices or reliable emulation because desktop windows do not reproduce every cutout behavior.
Virtual Keyboards and Focused Inputs
An on-screen keyboard may reduce the visual viewport without causing CSS viewport units to behave exactly as expected across every browser. A form locked to a fixed viewport height can leave the focused field behind the keyboard. Natural document flow and browser scrolling are often more resilient than a rigid full-screen form.
For chat composers and specialized application shells, monitor real keyboard behavior, keep the focused field scrollable, and consider the Visual Viewport API only when CSS and native scrolling cannot meet the requirement. JavaScript workarounds should be narrowly scoped and cleaned up when the viewport changes.
Viewport Units and Browser Zoom
Browser zoom changes how many CSS pixels fit in the viewport, so viewport-unit calculations can change even though the physical monitor is unchanged. Fluid typography tied only to vw can therefore shrink or grow in ways that interfere with text enlargement. Rem-based minimums in clamp() protect a readable floor.
Accessibility testing should include zoom, larger default fonts, and narrow windows. Passing at common device presets is not enough because desktop zoom can create an effective narrow viewport.
Viewport Units and Media Queries
Viewport units scale continuously; media queries switch or modify rules when conditions are met. They solve different problems and work well together. A card might use a fluid viewport-aware width until a breakpoint changes the page structure.
.card {
width: min(85vw, 36rem);
}
@media (min-width: 64rem) {
.card-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
Choose breakpoints from content pressure rather than device names. Use viewport units inside each layout mode only where continuous scaling improves the result.
Viewport Units Versus Container Units
Viewport units respond to the browser window. Container query units respond to a designated container. A reusable card in a narrow sidebar should usually scale from the sidebar, not from a wide desktop viewport. Otherwise, a vw-based heading can become oversized inside a small component.
Use viewport units for page-level and immersive relationships. Use percentages, Grid, Flexbox, or container units when the component's local space is the meaningful reference.
Performance and Layout Stability
Viewport changes cause viewport-based values to be recalculated. Ordinary resizing is expected, but dynamic mobile units can update as browser controls move. Large regions whose size changes continuously may create visible shifts, repaint work, or movement of controls under the user's finger.
This does not make dvh inherently slow. Keep the affected layout simple, avoid unnecessary transitions on viewport-driven properties, and test scrolling on representative mobile hardware. Prefer transforms for purely visual animation rather than animating layout dimensions.
Accessibility Considerations
Never rely on an unbounded viewport unit for essential text. Users with low vision may need zoom or enlarged defaults, and a viewport-only formula can work against those preferences. Use rem-based minimums, allow wrapping, and avoid fixed-height text containers.
Full-screen interfaces must preserve keyboard navigation, visible focus, scroll access, and escape mechanisms. A modal constrained by dvh still needs focus management and an accessible name. A hero that fills the screen should not hide the next content or trap users in a scrolling region.
Common Mistake: Using 100vw for Normal Containers
A block element already fills the available inline space by default. Adding width: 100vw disconnects it from its parent and can create overflow around scrollbars or page padding. Use width: 100% only when it is necessary, or let normal auto sizing do the work.
Common Mistake: Fixing Content to 100vh
A fixed viewport height assumes content will always fit. Dynamic text, localization, validation messages, and accessibility scaling break that assumption. Prefer min-height: 100dvh for content sections and allow the document to become taller than one screen.
Common Mistake: Unbounded Viewport Typography
Rules such as font-size: 10vw are attractive in a single mockup but unstable across phones, tablets, desktops, split-screen windows, and ultrawide monitors. Wrap the fluid preference in clamp(), and choose lower and upper limits from readability and content testing.
Common Mistake: Confusing % and vw
Percentages are property- and context-dependent; vw always uses viewport width. A 100-percent image follows its containing block, while a 100vw image follows the browser viewport. The two are equal only when the container and viewport happen to have the same relevant width.
Viewport Units in Iframes and Embedded Documents
A document rendered inside an iframe has its own viewport. Viewport units in that embedded document are based on the iframe's viewport rather than the top-level browser window. If an iframe is 600 pixels wide inside a 1400-pixel desktop page, 50vw inside the iframe is approximately 300 pixels, not 700 pixels. This local behavior is usually desirable because embedded content should adapt to the area assigned to it.
The host page still controls the iframe element's external size. A child document cannot make its own 100vh escape the frame and cover the host window. If the frame needs to grow with content, the host and embedded application need an explicit resizing strategy, often based on secure cross-document messaging. Viewport units alone cannot communicate intrinsic document height across that boundary.
Similar reasoning applies to browser side panels, web views, preview panes, and documents shown inside development tools. The effective viewport may be much narrower than the physical screen. Test embedded experiences in their real containers instead of assuming desktop screen dimensions. When a component is reused inside one document rather than a separate browsing context, container queries are usually more appropriate because vw still follows that document's full viewport.
Accounting for Headers, Footers, and Persistent Controls
A page may need a content region that fills the viewport minus a persistent header. A direct calculation can express that relationship when the header height is stable and represented by a shared token.
:root {
--header-height: 4rem;
}
.main-stage {
min-height: calc(100dvh - var(--header-height));
}
This approach requires the token to match the real header. If the header wraps to two lines under zoom or localization, a fixed subtraction becomes wrong. A full-height Grid shell with an auto header row and a flexible content row is generally safer because layout measures the header's natural height instead of duplicating it in a variable.
.page-shell {
min-height: 100dvh;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
}
Sticky headers create a different concern: anchored headings and focused controls can scroll behind them. Use scroll-padding-top on the scrolling container or scroll-margin-top on targets. These values often belong to the header's measured design token rather than vh because the obstruction is a component, not a percentage of the viewport.
Bottom navigation, cookie notices, media controls, and safe-area insets can also reduce usable space. Avoid layering several independently fixed elements over a 100dvh section without reserving room. Model the entire shell, allow the primary area to scroll, and verify that the first and last interactive controls remain reachable with keyboard, touch, and zoom.
Debugging Viewport-Based Layouts
Use DevTools responsive mode to resize continuously, rotate orientation, and simulate browser controls where supported. Inspect computed dimensions and determine whether an overflowing element uses viewport or container sizing. Temporarily outline all elements to find the source of horizontal scrolling.
* {
outline: 1px solid rgb(255 0 0 / 20%);
}
console.log(window.innerWidth, window.innerHeight);
console.log(window.visualViewport?.width,
window.visualViewport?.height);
Remove diagnostic styles after testing. Compare JavaScript viewport readings only as debugging evidence; do not assume one API maps perfectly to every CSS viewport concept.
Testing Viewport Units
Test portrait and landscape phones, tablets, desktop windows, split-screen modes, and very tall or wide displays. Scroll to expand and collapse mobile browser bars. Open the virtual keyboard on every input in full-height views. Test pages with enough content to create a vertical scrollbar.
Increase zoom and default font size, then check clipping, horizontal scrolling, focus visibility, sticky controls, safe-area padding, and modal access. Automated visual tests can cover stable viewport sizes, but dynamic mobile UI still benefits from real-device testing.
A Practical Unit-Selection Workflow
First identify the intended reference. If an element should follow its parent, use percentage or layout tracks. If it should follow the window width, consider vw. If it should fill the current mobile-visible height, consider dvh. If it must fit regardless of orientation, vmin may work. If a decorative element must cover broadly, vmax may work.
Next add limits, preserve natural content growth, account for browser UI and safe areas, and test orientation and zoom. Viewport units are strongest when they express a real viewport relationship rather than serving as a shortcut for every responsive measurement.
What controls the size?
Parent/container -> %, Grid, Flexbox, container units
Viewport width -> vw
Visible mobile height-> dvh
Conservative height -> svh
Smaller screen side -> vmin
Larger screen side -> vmax
Interview Questions and Answers
What is 1vw? It is one percent of the viewport width.
What is 1vh? It is one percent of the viewport height defined for the traditional viewport unit.
What is vmin? One vmin is one percent of the smaller viewport dimension.
What is vmax? One vmax is one percent of the larger viewport dimension.
What is the difference between percent and vw? Percentage resolution depends on the property and context, while vw always references viewport width.
What is the difference between vh and dvh? Dvh dynamically tracks changes in the viewport as browser interfaces expand or retract; traditional vh uses the default viewport definition.
Why can 100vw create horizontal scrolling? It can include viewport width associated with scrollbar space and ignores the width constraints of a normal parent.
Why combine clamp with viewport units? Clamp supplies readable lower and upper limits around a fluid viewport-based preferred value.
When should svh be used? Use it when content should fit within the conservative viewport state where browser controls occupy the most space.
Practical Review Checklist
- Use vw only for dimensions genuinely related to viewport width.
- Prefer container sizing when a component belongs to a parent.
- Use min-height instead of fixed height for content-rich sections.
- Choose svh, lvh, or dvh according to mobile browser behavior.
- Bound viewport typography and spacing with clamp, min, or max.
- Use vmin for fit based on the smaller viewport dimension.
- Reserve vmax mainly for broad visual or decorative sizing.
- Avoid 100vw when normal container width is sufficient.
- Account for safe-area insets on edge-to-edge mobile layouts.
- Test the virtual keyboard in full-height forms and applications.
- Verify browser zoom, text enlargement, and horizontal overflow.
- Test portrait, landscape, scrolling, and dynamic browser controls.
Key Takeaway
Viewport units connect CSS dimensions directly to the browser viewport. Vw follows width, vh follows height, vmin follows the smaller dimension, and vmax follows the larger dimension. Modern small, large, and dynamic families make mobile viewport intent more explicit.
Use viewport units when the viewport is truly the design reference, preserve natural content growth, and constrain fluid values so they remain readable. Prefer percentages or container-based layout for local components, and test mobile browser controls, orientation, scrollbars, zoom, keyboards, and safe areas before treating a full-screen design as complete.