Search code examples
htmlcssios-simulatormobile-safari

top/bottom:[%] based on parent element's borders instead of height


I found a weird behavior when positioning a child element using top:[%]. On mobile-safari, given top/bottom borders of sufficient size, the behavior seems to switch: Instead of the percentage being applied to the parent's height, its applied to the parent's borders (plus a little extra).

The following code reproduces the bug:

.parent {
  position: relative;
  border-bottom: 200px;
  border-style: solid;
  height: 100px;
  
  width: 100%;
}

.normal {
  position: absolute;
  top: 10px;
  
  width: 50%;
  background-color: blue;
}

.defective {
  position: relative;
  top: 10%;
  
  width: 50%;
  left: 50%;
  background-color: red;
}

* {
  /*style reset*/
  margin: 0;
  border: 0;
}
<body>
  <div class="parent">
    <div class="normal">
      <p>The top is 10px</p>
    </div>
    <div class="defective">
      <p>I should be at the same height</p>
    </div>
  </div>

In this example, the top should be 10px, but when I inspect the element, it's actually 20.3px. Here is what it looks like:

Here is what I see

Other details:

  • The bug seems to kick in when the sum of the vertical borders of the parent are 98% or more of its height.
  • The child element doesn't have to be a div, it can be an image.
  • The bug does NOT appear if the child has position:absolute.

Am I the only one who sees this? Is is documented somewhere?

Reproduced on the following:

  • iPhone XS Max 12 (real device on browserstack.com)
  • iPad mini iOS 9
  • iPhone8, with Safari 13.6 (simulated on lamdatest.com)

Solution

  • The relatively placed element is displaced vertically depending on the width of the border on some versions of IOS. This did not show on an iPad running Safari IOS14 but did on a mini iPad running IOS 9.

    This appears to be a bug in at least some IOS versions.

    A workaround might be to put the border into a pseudo element on parent. This snippet then worked on IOS 9 on the mini iPad:

    <style>
    .parent {
      position: relative;
      height: 100px;
      
      width: 100%;
    }
    .parent::before {
     content: '';
     position: absolute;
     width: 100%;
     height: 100%;
     border-bottom: 200px;
     border-style: solid;
    }
    .normal {
      position: absolute;
      top: 10px;
      
      width: 50%;
      background-color: blue;
      
      rdisplay: none;
    }
    
    .defective {
      position: relative;
      top: 10%;
      
      width: 50%;
      left: 50%;
      background-color: red;
    }
    
    * {
      /*style reset*/
      margin: 0;
      border: 0;
    }
    </style>
    <body>
      <div class="parent">
        
        <div class="defective">
          <p>I should be at the same height</p>
        </div>
        <div class="normal">
          <p>The top is 10px</p>
        </div>
      </div>