Flexbox Relative Height Issue in iOS 10
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:
In iOS Safari 10, it looks like this:
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.