TL;DR: The safe-area-inset-bottom value doesn't update when the keyboard is visible, creating an unnecessary gap above the keyboard. Here's a CSS-only workaround (with a caveat...)

Ever since the iPhone X (/ai·fown eks/), websites and web apps have been able to render under the system UI (just like native apps!), leveraging the CSS env( safe-area-inset-* )
values to make sure the system UI doesn't clash with our UI.
However, it turns out Cupertino (and now Mountain View) forgot about a small detail:
The safe-area-inset-bottom value doesn't update when the keyboard is visible, creating an unnecessary gap above the keyboard.

The workaround
For anyone who runs into this issue: I've come up with a workaround that covers 90% of the use-case, and only requires a bit of CSS:
.safe-area-bottom {
:root:not(
:has(
input:focus-visible,
textarea:focus-visible,
[contenteditable]:focus-visible
)
) & {
padding-bottom: env( safe-area-inset-bottom, 0 );
}
}
I'm using CSS nesting here, and there's an
&
in there. This roughly compiles down to something like::root:not( ... ) .safe-area-bottom { ... }
The gist is this: Only apply safe-area-inset-bottom
if the document does not contain an input
, textarea
or [contenteditable]
that has :focus-visible
.
The caveat
There is one drawback: input elements can still have :focus-visible
when the keyboard is dismissed, at least on Chrome for Android. This means that the inset will not be applied if the input is focused and the user dismissed the keyboard.
The solution
Both Chromium and WebKit should update the safe-area-inset-*
values when the keyboard pops in or out of view (like how how they update the values when you change orientation of the device).
So please consider subcscribing and/or +1'ing the issues for Chromium and WebKit:
- WebKit: Safe-area-inset-bottom still set when keyboard appears
- Chromium: Safe-area-inset-bottom still set when keyboard appears