htmlcssresponsive

When opening the keyboard on mobile, how can I force my content to shrink-to-fit to the available space instead of shifting up?


Consider the snippet below, where I've created a basic chat window.

The issue: When you focus the input on a mobile device, the entire page is shifted up, pushing the header and messages out of view.

The goal: When you focus the input on a mobile device, the #messages section shrinks to accommodate the keyboard (similar to how the page behaves on desktop when reducing the window height).

(Images included below)

My attempts to resolve it: I've tried using height: 100% instead of 100dvh, I've tried moving the height declaration to the <body> instead of the <html>.

html,
body {
  padding: 0;
  margin: 0;
}

html {
  height: 100dvh;
  width: 100dvw;
}

body {
  height: 100%;
  width: 100%;
  background-color: white;
}

#content {
  height: 100%;
  display: flex;
  flex-direction: column;
}

#header {
  padding: 20px;
  background-color: #ccc;
}

#messages {
  margin: -10px 0;
  flex-grow: 1;
  flex-shrink: 1;
  overflow: auto;
}

.message {
  padding: 20px;
}

#footer {
  display: block;
  background-color: #aaa;
  padding: 20px;
}

input {
  padding: 5px;
  width: 100%;
  display: block;
}

button {
  border: 0;
  margin: 4px 0;
  padding: 4px;
  background-color: orange;
  font-weight: bold;
}
<div id="content">
  <div id="header">Header</div>
  <div id="messages">
    <div class="message"><b>John:</b> Lorem ipsum dolor sit amet.</div>
  </div>
  <form id="footer">
    <input>
    <button type="button">Send Message</button>
  </form>
</div>

Example

EDIT: Firefox on mobile handles this perfectly. Almost seems like Chrome and Safari are not accounting for the on-screen keyboard when calculating dvh.


Solution

  • Turns out the code in the question is fine.

    The solution is to add interactive-widget=resizes-content to your <meta name="viewport"> tag, like so:

    <meta name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content">
    

    Why:

    dvh units are determined using the size of the Layout Viewport. As of Chrome 108 (Nov 2022), the on-screen keyboard affects the Visual Viewport, but not the Layout Viewport.

    The interactive-widget=resizes-content forces Chrome to use the previous behavior, where both the Visual and Layout viewports are resized.

    Sources:

    Chrome for Developers: Prepare for viewport resize behavior changes coming to Chrome on Android

    In November 2022, with the release of Chrome 108, Chrome will make some changes to how the Layout Viewport behaves when the on-screen keyboard (OSK) gets shown [...] and instead resize only the Visual Viewport.

    If you want your website to use the pre-108 resize behavior, fear not. Also shipping in Chrome 108 is an extension to the viewport meta tag.

    Through the interactive-widget key, you can tell Chrome which resize behavior you want.

    Accepted values for interactive-widget are:

    • resizes-visual: Resize only the Visual Viewport but not the Layout

    • resizes-content: Resize both the Visual Viewport and Layout

    • overlays-content: Do not resize any viewport.

    interactive-content