Fix CSS dvh Mobile Keyboard Issue

You're coding a sleek mobile chat app. Keyboard pops up. Your fixed bottom bar? Buried. CSS dvh was supposed to save you—until it didn't.

Mobile chat app with fixed input bar floating above on-screen keyboard, CSS vars adjusting layout

Key Takeaways

  • CSS dvh ignores mobile keyboards by spec design, treating them as overlays.
  • use-dynamic-viewport hook injects perfect CSS vars for true dynamic height and keyboard offset.
  • Cross-platform quirks demand heuristics; this tiny lib (0.8KB) nails iOS Safari and Android Chrome.

Keyboard slams open on your iPhone. That fixed input bar at the bottom? Gone, swallowed whole. You’ve swapped 100vh for dvh, chased the dynamic viewport height dream. But here you are, cursing Safari again.

CSS dvh burst onto the scene to slay the infamous 100vh mobile bug—URL bars that yo-yo, layouts that jump. It promised sanity: height units that flex with the browser chrome. Except.

It pretends the on-screen keyboard doesn’t exist.

Why Does CSS dvh Completely Ignore the Mobile Keyboard?

Blame the spec. CSS viewport units treat virtual keyboards as overlays, not layout shrinkers. dvh, svh, lvh—they all hold steady when keys deploy. No resize. Your position: fixed elements? Covered. Brutally.

The CSS viewport units spec treats the virtual keyboard as an overlay — it doesn’t resize the layout viewport. So dvh, svh, and 100vh all stay the same value when the keyboard opens.

That’s straight from the trenches. And it’s why millions of PWAs, chats, forms—anything with bottom-fixed inputs—still break on mobile.

But wait. Platforms feud here. iOS Safari? Scrolls the visual viewport up—visualViewport.offsetTop jumps, resize and scroll fire on visualViewport, but not window.resize. Android Chrome? Shrinks window.innerHeight outright, fires window.resize, offsetTop stays zero.

Handle one? The other crumbles. Classic cross-platform hell.

How Do You Actually Calculate Keyboard Height?

window.innerHeight minus window.visualViewport.height. Boom—keyboardHeight. Simple math, right?

Except iOS’s scroll trick means you need heuristics. Track layout refs with width guards: orientation flips innerWidth, keyboards don’t. On Android, width stays put too—but offsetTop flags iOS.

Here’s the code whisperer:

const handleWindowResize = () => {
  const vv = window.visualViewport
  const currWidth = window.innerWidth
  const widthChanged = currWidth !== layoutWidthRef.current
  // iOS: vv.offsetTop > 0 when keyboard is open → skip
  // Android: width unchanged when keyboard opens → skip
  // True orientation/resize: width changes → update reference
  if (!vv || (vv.offsetTop === 0 && widthChanged)) {
    layoutHeightRef.current = window.innerHeight
    layoutWidthRef.current = currWidth
  }
}

Tested. Reliable. No width wobbles from keyboards.

This isn’t just theory—it’s battle-tested across iOS Safari and Android Chrome. Next.js App Router? SSR safe. Zero deps, 0.8KB gzipped. React 17+, TS-typed, 19 Vitest specs.

Enter use-dynamic-viewport: The Tiny Fix That Just Works

npm install use-dynamic-viewport. One hook. Two CSS vars on :root: –dvh (true dynamic height), –keyboard-height.

import { useDynamicViewport } from 'use-dynamic-viewport'

export function Layout() {
  useDynamicViewport() // magic
  return <div className="app">...</div>
}

.app { height: var(–dvh, 100svh); }

.input-bar { position: fixed; bottom: var(–keyboard-height, 0px); left: 0; right: 0; }

Keyboard up? Bar floats perfectly above. Down? Full height. No JS polling, no ResizeObserver hacks.

Or snag JS values:

const { viewportHeight, keyboardHeight, isKeyboardOpen } = useDynamicViewport()

Chats, modals, fullscreens—fixed.

Look, browser vendors hyped dvh as the 100vh killer. Fair. But ignoring keyboards? That’s like fixing the door lock while leaving the window wide open. We’ve seen this movie: vh units born from desktop assumptions, mangled on mobile. Now viewport 2.0 repeats the sin.

My bet? CSS@keyboard-height units by 2026. Specs lag; hooks like this bridge the gap. Open source beats waiting—rl0425 dropped this on GitHub (https://github.com/rl0425/use-dynamic-viewport), npm live. Fork it, ship it.

But here’s the deep cut: this exposes viewport architecture’s core fracture. Layout viewport (for CSS) vs. visual viewport (what users see). Browsers patch inconsistently—iOS overlays and scrolls, Android resizes. Until the spec mandates keyboard-aware units, we’re gluing heuristics. Genius, fragile.

Why does it matter? Mobile’s 60% of web traffic. Forms convert or bounce on keyboard UX. PWAs die here. Devs waste weeks on ‘resize’ listeners that flake.

Is use-dynamic-viewport Safe for Production?

19 tests say yes. Handles edge: landscape keyboards, split-view iPads, Android notch drama. No globals fouled. SSR? Hydrates clean—runs client-only.

Prod apps already? Stack Overflow chats, Telegram web—imagine if they’d had this years ago.

Skeptical? Clone the repo. Vitest + RTL. Fire up emulators. Keyboard spam it.

This hook isn’t hype—it’s the pragmatic stab at sanity in a spec that’s half-baked.

Unique angle: remember IE’s hasLayout hackarounds? This is 2024’s viewport equivalent. Browsers evolve slow; open source sprints ahead. Grab it before dvh2 drops.

Why Hasn’t the CSS WG Fixed This Yet?

Politics. Interop demands consensus—Apple, Google, Mozilla align? Glacial. Meanwhile, 100vh zombies roam; dvh joins the undead.

Prediction: visualViewport API stabilizes, then native vars. But don’t hold breath.


🧬 Related Insights

Frequently Asked Questions

What is CSS dvh and why does it fail on keyboards?

CSS dvh tracks browser chrome like URL bars but treats keyboards as overlays, keeping height fixed while your UI gets covered.

How do I fix CSS dvh keyboard issues in React?

Install use-dynamic-viewport npm package, call the hook—it injects –dvh and –keyboard-height vars automatically.

Does use-dynamic-viewport work on both iOS and Android?

Yes, handles platform diffs with offsetTop and width heuristics; tested across Safari and Chrome.

Is there a vanilla JS version?

Core logic’s in the repo—extract the resize handler; hook’s React wrapper.

Marcus Rivera
Written by

Tech journalist covering AI business and enterprise adoption. 10 years in B2B media.

Frequently asked questions

What is CSS dvh and why does it fail on keyboards?
CSS dvh tracks browser chrome like URL bars but treats keyboards as overlays, keeping height fixed while your UI gets covered.
How do I fix CSS dvh keyboard issues in React?
Install use-dynamic-viewport npm package, call the hook—it injects --dvh and --keyboard-height vars automatically.
Does use-dynamic-viewport work on both iOS and Android?
Yes, handles platform diffs with offsetTop and width heuristics; tested across Safari and Chrome.
Is there a vanilla JS version?
Core logic's in the repo—extract the resize handler; hook's React wrapper.

Worth sharing?

Get the best AI stories of the week in your inbox — no noise, no spam.

Originally reported by Dev.to

Stay in the loop

The week's most important stories from theAIcatchup, delivered once a week.