Technical note

The dead band in your iOS web app is not your CSS

One of our apps runs installed to the iPhone home screen, the add-to-home-screen kind that opens without browser chrome. The bottom navigation bar sat about 62 points above the physical bottom of the screen. Below it: a dead strip painted in the page background color that no element could reach.

position: fixed; bottom: 0 stopped short of the glass. So did 100dvh. So did innerHeight.

We blamed our CSS, twice. First theory: the keyboard dismissing and leaving the visual viewport shrunk. Wrong.

Second theory: a missing viewport-fit=cover. Also wrong. The safe-area inset values even reported normal numbers, which is what makes this trap so convincing. Every signal says your stylesheet did it.

Put numbers on the actual phone

After two dead ends we quit theorizing and built a debug page. It renders screen.height, innerHeight, a measured 100dvh, all four safe-area insets, and a red line drawn at fixed; bottom: 0. Then we took a screenshot from the real phone, not a simulator.

The signature was right there: screen height minus innerHeight equaled the top safe-area inset, exactly. iOS was sizing the page as the screen minus the status bar, anchored at the top. The missing height fell off the bottom.

Cheap trick, and it settled in one screenshot what two confident theories could not. That page is worth keeping around for the next one of these.

The culprit is a status bar meta tag

The setting was apple-mobile-web-app-status-bar-style: black-translucent. It promises a full-bleed app with your content running up behind the status bar, and iOS does draw the page full-screen. But it sizes the layout viewport as if the status bar still took its slice, anchored at the top edge.

So everything bottom-anchored lands about 62 points high, and iOS paints the leftover band with your html background. Meanwhile the safe-area values keep reporting normal numbers, pointing you straight at your own stylesheet. Your CSS is fine. The viewport is lying to it.

The fix, and the catch

Set the status bar style to default. In Next.js that is metadata.appleWebApp with statusBarStyle: 'default'. The app then starts below the status bar, iOS paints that top zone in your theme color so it still reads as one surface, and the bottom edge lands exactly on the hardware.

Here is the trap that will bite you: iOS bakes this setting into the app at install time. Ship the corrected tag and nothing changes for anyone who already installed. The setting is concrete, cured at the pour.

You do not restyle a cured slab, you pour a new one: every existing user has to delete the home screen icon and add it again. Budget for telling them.

The rule we kept

When a fixed element floats above the bottom of an installed iOS web app, read the web app meta tags before you touch a line of CSS. And when the theories start stacking up, stop theorizing and get real measurements off the real device.

The debug page took minutes to write and it earned a permanent spot in our toolkit. Your layout is probably fine. Go find out what the viewport is telling it.