CSS Module 5 – Values, units and math functions
Viewport units: vw, dvh, svh and lvh
How vw, vh, svh, lvh and dvh measure the viewport, why 100vh hides content behind phone toolbars and 100vw overflows, and what to use instead.
What you will learn
- Size full-screen sections with svh, lvh and dvh instead of 100vh on mobile
- Use vi and vb logical viewport units and know when vw causes horizontal overflow
- Avoid viewport-only font sizes that stop text growing when the user zooms
Before you start
What you’ll learn · Before you start
What you will learn
- Size full-screen sections with svh, lvh and dvh instead of 100vh on mobile
- Use vi and vb logical viewport units and know when vw causes horizontal overflow
- Avoid viewport-only font sizes that stop text growing when the user zooms
Before you start
On this page
Viewport units size things as a share of the browser’s viewport: A viewing or layout reference area used by the browser; a viewport-based length names which size and direction supply its reference.: vw is 1 % of its width and vh 1 % of
its height, so 100vh is one full screen. Phones complicate the height, because their browsers show toolbars when a
page opens and hide them while the reader scrolls. CSS therefore has three viewport heights. svh measures the
small viewport: The viewport-size reference associated with the browser's retractable interface being expanded., with the toolbars shown. lvh measures the large viewport: The viewport-size reference associated with the browser's retractable interface being retracted., with them hidden. dvh measures
the dynamic viewport: A viewport-size reference that follows the browser's current retractable-interface state., whichever of the two is on screen right now. Plain vh is the large one, so a 100vh section
can sit partly behind the toolbars. Use svh for full-screen sections, dvh for app shells that must match the visible
area, and 100% rather than 100vw for full widths, because vw includes a desktop scrollbar. The supplied comparison cases below use Chromium 153, Firefox 155 and WebKit 26.6.
Viewport heights follow one of three toolbar states
The viewport is the part of the window that shows the page. On a desktop its height changes only when the window does. On a phone the browser’s address bar and buttons slide away as the reader scrolls down, so the space for the page grows, and they come back on scrolling up. CSS Values 4 names the two extremes and the moving value between them:
svh(small) assumes every retractable toolbar is shown: content this tall always fits, with a gap below once the toolbars hide.lvh(large) assumes they are all hidden: content this tall fills the screen once they hide, but the toolbars cover part of it while they show.dvh(dynamic) follows the toolbars, so it always matches the visible area, and changes while the reader scrolls.
vh was defined before phones did this, and browsers settled on the large viewport for it, so vh means lvh. Each
height unit has width and logical twins (svw, dvi, lvmin and so on); on most phones the toolbars change only the
height, so the width variants agree. The small, large and dynamic units are Baseline widely available. The numbers of
a phone differ from model to model and browser to browser, so the program takes one example, a viewport 640 px tall
with 56 px of toolbars:
// The small, large and dynamic viewport heights of CSS Values 4 on a phone whose browser shows its toolbars when the
// page opens and hides them while the reader scrolls. The heights are an example: every phone and browser has its own.
const viewport = { largeHeight: 640, toolbars: 56 }; // CSS px: the viewport with the toolbars hidden, their height
const smallHeight = viewport.largeHeight - viewport.toolbars;
const units = {
'100vh': () => viewport.largeHeight, // vh is the large viewport
'100lvh': () => viewport.largeHeight,
'100svh': () => smallHeight,
'100dvh': (shown) => (shown ? smallHeight : viewport.largeHeight),
};
console.log('unit toolbars shown toolbars hidden');
for (const [unit, height] of Object.entries(units)) {
const row = [true, false].map((shown) => {
const visible = shown ? smallHeight : viewport.largeHeight;
const h = height(shown);
const note = h > visible ? `${h - visible}px hidden` : h < visible ? `${visible - h}px gap below` : 'fits exactly';
return `${h}px, ${note}`.padEnd(24);
});
console.log(`${unit.padEnd(8)} ${row.join('')}`.trimEnd());
}
// A full-screen hero with its 48px button at the bottom: is the button visible when the page opens?
for (const unit of Object.keys(units)) {
const heroHeight = units[unit](true);
const buttonTop = heroHeight - 48;
console.log(`hero at ${unit}: button from ${buttonTop}px to ${heroHeight}px, ${heroHeight <= smallHeight ? 'visible' : 'behind the toolbars'} on opening`);
} Output
unit toolbars shown toolbars hidden 100vh 640px, 56px hidden 640px, fits exactly 100lvh 640px, 56px hidden 640px, fits exactly 100svh 584px, fits exactly 584px, 56px gap below 100dvh 584px, fits exactly 640px, fits exactly hero at 100vh: button from 592px to 640px, behind the toolbars on opening hero at 100lvh: button from 592px to 640px, behind the toolbars on opening hero at 100svh: button from 536px to 584px, visible on opening hero at 100dvh: button from 536px to 584px, visible on opening
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node toolbars.mjs
Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
The small, large and dynamic viewport of one example phone
Text description of the diagram
The diagram follows the lesson's example phone, whose viewport is 640 px tall with the browser's toolbars hidden and 584 px tall while they show. Real phones and browsers have their own numbers.
- When the page opens, the toolbars are shown and take 56 px. The visible page is 584 px tall: the small viewport. 100svh and 100dvh are both 584 px and fit exactly. 100vh and 100lvh are 640 px, the large viewport, so 56 px of a box that tall sits behind the toolbars.
- Scrolling hides the toolbars, and the visible page becomes 640 px tall: the large viewport. Now 100vh, 100lvh and 100dvh are 640 px and fit. 100svh stays 584 px, leaving a 56 px gap below the box.
- So svh never hides content, lvh and vh never leave a gap, and dvh changes with the toolbars, which makes boxes sized with it resize while the reader scrolls.
A 100vh hero opens with its bottom 56 px behind the toolbars, which is often where its main button goes.
100svh fits on opening and leaves a 56 px gap once the toolbars hide. 100dvh always fits, at a cost the
specification spells out: a box sized in dynamic units resizes while the reader scrolls, which can be distracting and
costs layout work on every change. Browsers may also skip the in-between values while the toolbars animate. The
on-screen keyboard usually doesn’t count as a toolbar: the specification gives it as the typical example of an
interface that overlays the page without changing any viewport unit. A chat page that needs its input above the
keyboard can ask for interactive-widget=resizes-content in its viewport <meta> tag, which resizes the viewport, and
every viewport unit with it, in the browsers that support that key.
Follow the same 640px phone through the two toolbar states. The steps use the programme’s 56px toolbar value rather than assuming every phone has that much browser interface:
The drawing shows the whole scene; its 4 steps are listed below.
Every step as text
- Toolbars show. The 56px toolbars leave 584px visible. vh and lvh still use 640px, while svh and dvh use 584px in this opening state.
- Place the 48px button. A button at the bottom of a 640px hero runs from 592 to 640px, behind the toolbar boundary. A 584px hero puts it from 536 to 584px.
- Toolbars hide. The visible height becomes 640px. The dynamic reference follows it, while the small reference stays at 584px and leaves a 56px gap.
- Choose the change you need. The references describe different behaviours. A stable hero can use the small height; a shell that must follow the current height can use the dynamic one.
The small and large references stay at 584 and 640px. Only the dynamic reference changes with the toolbar state.
Checkpoint
Check: Small large and dynamic heights
Choose a stable hero or a shell that follows toolbars
Start with the required behaviour rather than the newest unit. A hero should keep its main action in view when browser toolbars are expanded, then stay steady during a scroll. A shell can instead require its navigation to follow the current available height. These goals select different references even on the same phone.
The fallback uses declaration order. Put the large-reference vh declaration first and the small-reference svh declaration second. An engine that understands both selects the later one; an older engine discards the unfamiliar value and retains the earlier declaration. Reversing those lines would override the small-reference choice.
| Criterion | vh, lvh | svh | dvh |
|---|---|---|---|
| Measures | The large viewport | The small viewport | What is visible now |
| Toolbars shown | Bottom hidden behind them | Fits | Fits |
| Toolbars hidden | Fits | Gap below | Fits |
| Resizes while scrolling | No | No | Yes |
| When to choose | Backgrounds that may run under the toolbars | Heroes and full-screen sections | App shells, chats, maps |
For widths, prefer 100% or a grid that spans the page to 100vw; use vi and vb when a component may meet
vertical text; and never size text in viewport units alone.
Checkpoint
Check: Choosing a stable or changing height
Fit complete shells and full-width layouts Pro: opens with a Pro, Premium or Ultimate pass
The height reference is only one part of a working layout. These cases follow content growth, the scrolling row and the width left beside a classic scrollbar.
In this part:
- A shell gives its scrolling row room to shrink
- Full-width grids use the available page area
1 diagram · 4 runnable examples (100vw banner, CSS, Grid banner, HTML) · about 6 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; nothing renews unless you choose to. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Follow logical axes and test zoomed text Pro: opens with a Pro, Premium or Ultimate pass
A viewport length also has an axis and a pixel scale. These cases keep both references explicit while working through vertical text and larger on-screen type.
In this part:
- Logical axes change which viewport side a unit uses
- Zoom changes CSS pixels and the viewport together
- Practise the complete phone model
- Work out viewport units on a phone with toolbars
- Results of the sample tests
- Keep the full layout cases together
2 diagrams · 2 runnable examples (HTML) · 1 exercise with sample tests · about 9 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; nothing renews unless you choose to. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Practise the viewport core
Practice that adapts to you
Practise this lesson
4 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Fresher · What is the difference between 100vh, 100svh and 100dvh on a phone?
All three are a full viewport height, measured against different viewports. Phone browsers show toolbars when a page
opens and hide them while the reader scrolls. 100svh is the small viewport, with the toolbars shown, so content that
tall always fits, and leaves a gap once they hide. 100vh is the same as 100lvh, the large viewport with the
toolbars hidden, so while they show, the bottom of the content sits behind them: 56 px of a 640 px screen in the
lesson’s example, often where the main button is. 100dvh follows the toolbars and always matches the visible area,
but it resizes while the reader scrolls. Use svh for heroes, dvh for app shells, and put 100vh first as a fallback.
-
Mid to senior
A full-width banner uses width: 100vw with margin-inline: calc(50% - 50vw) inside a centred container. Users on Windows report a horizontal scrollbar, and nobody in the team, all on Macs, can see it. Explain, fix it without vw, and stop it coming back.
-
Mid to senior
A dashboard uses 3vw text in an 800px window. Readers say zoom barely enlarges it, even after a developer adds clamp(). How do you investigate its references, choose bounds and test that the page remains usable?
Key takeaways
- vh and lvh use the large viewport; svh uses the small one, and dvh follows its dynamic size.
- Choose a stable small-viewport reference for a hero and a dynamic reference for a shell that must resize with toolbars.
- Widths and font sizes need their own reference decisions; a viewport unit does not fit every layout job.
Terms in this lesson
- dynamic viewport
- A viewport-size reference that follows the browser's current retractable-interface state.
- large viewport
- The viewport-size reference associated with the browser's retractable interface being retracted.
- small viewport
- The viewport-size reference associated with the browser's retractable interface being expanded.
- viewport
- A viewing or layout reference area used by the browser; a viewport-based length names which size and direction supply its reference.
References
- CSS Values and Units Module Level 4: viewport-percentage lengths (W3C CSS Working Group)
- CSS Values and Units Module Level 4: the large, small and dynamic viewport sizes (W3C CSS Working Group)
- CSSOM View Module: zooming (W3C CSS Working Group)
- Web Content Accessibility Guidelines (WCAG) 2.2: Resize Text (W3C)
- F94: Failure of Success Criterion 1.4.4 due to incorrect use of viewport units to resize text (W3C)
- Web Content Accessibility Guidelines (WCAG) 2.2: Reflow (W3C)
- Web features explorer: small, large and dynamic viewport units (W3C WebDX Community Group)
- <length> (MDN Web Docs) (Mozilla)
- <meta name="viewport"> (MDN Web Docs) (Mozilla)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress