Search code examples
htmlcsssafari

Safari CSS issue vertically misaligning elements with for some `height` value


The following code displays fine on Chrome and Firefox, but on Safari (17, Mac+IpadOS) the bar baselines are changing.

Even weirder (and which makes me think it could be a rendering issue), it can be fixed by changing the height component by a few percent point (like 79% instead of 80%, or 58% instead of 60%).

Any idea on a proper fix?

signal {
  display: inline-block;
  height: 1em;
  width: 1.5em;
  overflow: visible;
  white-space: nowrap;
}

signal>i {
  width: 20%;
  margin-left: 1px;
  display: inline-block;
  height: 40%;
  background-color: #7daa49;
}

signal>i:nth-child(2) {
  height: 60%;
}

signal>i:nth-child(3) {
  height: 80%;
}

signal>i:nth-child(4) {
  height: 100%;
}
<signal class="q4"><i></i><i></i><i></i><i></i></signal>

Display on Chrome/Firefox (correct):

correct bars

Display on Safari (incorrect):

incorrect bars

Note that on Safari it's not always the same bars who are misaligned, in the screenshot above it's the first two ones, but in the original issue it is the ones in the middle.


Solution

  • Yes you have a problem when for example 40% does not turn out to be a whole number of pixels.

    It is therefore going to be difficult to use em for this if you need a fully general solution.

    This snippet sets the height to 20px as a demo. 20px gives whole pixel heights. And the result is no misalignment.

    signal {
      display: inline-block;
      height: 20px;
      width: 1.5em;
      overflow: visible;
      white-space: nowrap;
    }
    
    signal>i {
      width: 20%;
      margin-left: 1px;
      display: inline-block;
      height: 40%;
      background-color: #7daa49;
    }
    
    signal>i:nth-child(2) {
      height: 60%;
    }
    
    signal>i:nth-child(3) {
      height: 80%;
    }
    
    signal>i:nth-child(4) {
      height: 100%;
    }
    <signal class="q4"><i></i><i></i><i></i><i></i></signal>

    Here is a different approach, making the signal element a grid.

    <style>
      signal {
        display: grid;
        grid-template-columns: repeat(4, 1fr);
        gap: 1px;
        height: 1em;
        width: 1.5em;
        overflow: visible;
        white-space: nowrap;
        position: relative;
      }
      
      signal>i {
        width: 100%;
        height: 100%;
        --h: 40%;
        background-image: linear-gradient(to top, #7daa49 0 var(--h), transparent var(--h) 100%);
      }
      
      signal>i:nth-child(2) {
        --h: 60%;
      }
      
      signal>i:nth-child(3) {
        --h: 80%;
      }
      
      signal>i:nth-child(4) {
        --h: 100%;
      }
    </style>
    <signal class="q4"><i></i><i></i><i></i><i></i></signal>

    Safari seems happier with that, at least on the version I have (IOS 16) and the height can go back to being based on em.