Flexbox Relative Height Issue in iOS 10

February 9th 2018 HTML 5 Ionic Framework

CSS Flexible Box Layout (or Flexbox for short) greatly simplifies many HTML layout scenarios. Although it is already broadly supported in browsers, sometimes incomplete implementations can still make your life difficult.

For example, in version 10 of iOS Safari, height of flexbox children isn't treated as explicitly set when defined by the parent flex container. The issue has been fixed in iOS Safari 11. In spite of that the problem can't be simply ignored, since a significant number of Apple devices are still using iOS 10. This doesn't only affect web pages, but also hybrid mobile applications written in frameworks such as Ionic.

Here's a simple case to reproduce the issue:

<div class="page">
  <div class="fixed-top"></div>
  <div class="variable-bottom">
    <div class="inner-bottom"></div>
  </div>
</div>

The following CSS sets up the problematic layout:

.page {
  background-color: #DDDDDD;
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 5px;
}

.fixed-top {
  background-color:#BBBBBB;
  height: 200px;
  padding: 5px;
}

.variable-bottom {
  background-color: #999999;
  flex: 1;
  padding: 5px;
}

.inner-bottom {
  background-color: #777777;
  height: 100%;
}

The basic idea is that the page is split into the fixed height top part (fixed-top) and the variable height bottom part (variable-bottom) using up all the remaining height. The bottom part contains another element (inner-bottom) covering all of its parent's height. I used the colors and paddings to see how the layout is rendered. This is the desired result:

Correct layout in iOS Safari 11

In iOS Safari 10, it looks like this:

Incorrect layout in iOS Safari 10

The inner-bottom element has the height of 0. This happens because Safari considers that the variable-bottom height is not defined and therefore treats the height of inner-bottom as if it was set to auto. Since the element has no contents, its height collapses to 0.

To work around the problem, the inner-bottom element must be positioned absolute:

.inner-bottom {
  background-color: #777777;
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: 5px;
  right: 5px;
}

I used 5px for all offsets to keep the 5px padding effect. If I didn't want any padding, I would of course use 0 instead of 5px.

For absolute positioning to function as expected, I also need to use relative positioning for the immediate parent:

.variable-bottom {
  background-color: #999999;
  flex: 1;
  padding: 5px;
  position: relative;
}

With this change, the layout stays the same in newer browsers but also works as expected in iOS Safari 10.

Copyright
Creative Commons License