← Back to Wiki
CSS / Layout

Padding on the Parent Does Not Inset an Absolutely Positioned Child

A phone with a notch cut the top off our app. The fix was to add padding: env(safe-area-inset-*) to the app shell, which is what every answer online says. It shipped, the changelog said the shell was fixed, and the screen looked exactly the same, because that rule had never applied to a single pixel.

Share on X

The rule underneath

An absolutely positioned element resolves top, right, bottom and left against its containing block's padding box. The edges of the padding box lie on the outside of the padding, not the inside.

So top: 0 puts the child at the parent's border edge, which is where the padding starts. The child then covers the very padding that was supposed to hold it back:

.app   { padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom); }
.pane  { position: absolute; inset: 0; }   /* sits on the border edge, ignores all of it */

If every child of that container is absolutely positioned, the padding affects nothing at all. Ours had four absolutely positioned panes and everything else position: fixed, which resolves against the viewport and is even further out of reach. A complete no-op, twice shipped.

BE WARNED: this failure is invisible in code review. The rule is present, correct in isolation, and uses the right variable. Nothing warns you, no devtools panel marks it as unused, and the only symptom is that the bug you thought you fixed is still there. Which makes it easy to conclude the problem is the safe-area variable, or the viewport meta tag, or the device.

Put the inset on the positioned element

Move the value to the element that is actually positioned:

.pane {
  position: absolute;
  top:    env(safe-area-inset-top);
  bottom: env(safe-area-inset-bottom);
  left: 0; right: 0;
  width: auto; height: auto;      /* important, see below */
}

Reset any width: 100% or height: 100% at the same time. A positioned box with both a top offset and a 100% height is 100% of the container plus the offset, so it overflows by exactly the inset you just added and you are back where you started, with a scrollbar.

Check whether a rule applies before you debug what it does

Two seconds each, and either would have caught this the first time:

The general habit is worth more than the CSS: confirm a rule takes effect before reasoning about why its effect is wrong. Most of the time debugging a wrong result is the harder problem, and this was not that.

Where else the same thing happens

And say what you verified

The changelog claimed the app shell was fixed. It was not, and it was wrong in a way nobody could catch without the device, because the code change was real and the applied result was nothing.

When a fix depends on hardware you do not have, ship it and say it is unconfirmed. That is honest, and it keeps the next person from treating the entry as evidence and looking somewhere else.

When this isn't your problem