How Dynamic Viewport Units Fixed Mobile Safari's Address Bar
For years, web developers struggled with mobile Safari's expanding
and collapsing address bar, which caused elements sized with the
traditional 100vh CSS unit to overflow the screen and hide
critical content. The introduction of new CSS viewport
units—specifically svh, lvh, and
dvh—finally resolved this issue by giving developers
precise control over how elements respond to dynamic browser interfaces.
This article explains the mechanics of the original mobile Safari
viewport problem and how these three modern CSS units provide a native,
JavaScript-free solution.
The Problem with
100vh in Mobile Safari
Historically, the vh (viewport height) unit was designed
to represent 1% of the visible screen height. Setting an element to
100vh meant it should take up the entire height of the
screen. However, mobile Safari features a dynamic user interface: when a
user first loads a webpage, the address bar and bottom navigation
toolbar are fully visible. When the user scrolls down, these toolbars
shrink or disappear to provide more reading space.
Originally, if Safari recalculated 100vh as the toolbars
shrank, every element using vh units would resize during a
scroll. This caused a jarring, jumpy user experience and terrible
scrolling performance.
To prevent this layout shift, Apple made a controversial decision:
mobile Safari permanently calculated 100vh as if the
browser toolbars were always hidden. Consequently, when a user
first landed on a page with the toolbars visible, any element set to
100vh was actually taller than the visible screen. This
caused the bottom of the element—often containing important buttons or
navigation—to be cut off or hidden behind the browser UI, forcing an
unwanted scrollbar on what was meant to be a single-screen layout.
Before the new units were introduced, developers had to rely on
clunky JavaScript event listeners measuring
window.innerHeight to manually set heights, or inconsistent
CSS hacks like -webkit-fill-available.
The Solution: svh,
lvh, and dvh
To solve this permanently, the CSS Working Group introduced new viewport units that explicitly account for dynamic browser toolbars. Instead of forcing one static definition of the viewport, developers can now choose between three distinct states.
1. Small Viewport Height
(svh)
The svh unit represents the "Small Viewport"—the visible
screen area when all browser toolbars (like Safari's address bar) are
fully expanded.
- How it solves the problem: Setting a hero section
or modal to
100svhguarantees that the element will fit entirely on the screen the moment the page loads. Nothing will be hidden behind the Safari toolbar, and no unwanted scrolling will occur. When the user scrolls down and the address bar shrinks, the100svhelement remains the exact same size, preventing any jumpy layout shifts.
2. Large Viewport Height
(lvh)
The lvh unit represents the "Large Viewport"—the visible
screen area when the browser toolbars are completely retracted.
- How it solves the problem:
100lvheffectively standardizes the behavior that mobile Safari previously forced upon100vh. It is useful for background elements or layouts where you intentionally want the element to fill the maximum possible screen space once the user begins scrolling, and you do not mind if the bottom is slightly obscured on the initial load.
3. Dynamic Viewport Height
(dvh)
The dvh unit represents the "Dynamic Viewport." It is a
fluid unit that automatically transitions between the svh
and lvh values in real-time as the browser interface
expands and collapses.
- How it solves the problem: When a page loads in
mobile Safari,
100dvhequals100svh, fitting perfectly between the visible toolbars. As the user scrolls and the address bar shrinks,100dvhdynamically recalculates and grows until it matches100lvh. This ensures an element is always flush with the edges of the visible screen, regardless of the toolbar's state.
Choosing the Right Unit
While dvh sounds like the ultimate fix, it comes with a
performance caveat. Because 100dvh forces the browser to
recalculate the layout as the user scrolls, it can still cause visual
jumping and frame drops on complex pages.
Therefore, the problem is best solved by using these units strategically:
- Use
100dvhfor fixed, non-scrolling application layouts (like a chat interface or a full-screen mobile menu) where the UI must constantly hug the bottom of the screen. - Use
100svhfor standard scrolling web pages (like a website's top hero section). It guarantees the content fits perfectly on initial load without causing layout shifts when the user scrolls down the page.