Search code examples
htmlcsslayoutcss-position

Alignment of absolutely positioned divs off by 1px at some zoom levels


I have two absolutely positioned divs that I have lined up to look like an "L" or a kinked pipe. On Chrome at 100% zoom on a MacBook Pro (16-inch, 2019), it looks like this:

enter image description here

The issue is that as I adjust the browser zoom level, the two divs go in and out of alignment by one pixel. For example, this is what it looks like at 90% zoom:

enter image description here

And the same thing happens at 110% zoom. If I adjust the position by 1px, it goes back into alignment. What causes this, and is there a way to prevent it all zoom levels?

Here is a working example on codepen: https://codepen.io/elethan/pen/gOLQXrB

CSS:

#outer-container {
  width: 200px;
  height: 40px;
  background: gray;
  padding: 10px;
  position: absolute;
  top: 50%;
  right: 50%;
}

#progress-bar {
  background: purple;
  height: 40px;
  width: 80%;
  border: 4px solid gray
}

#down {
  background: purple;
  height: 40px;
  width: 40px;
  border: 10px solid gray;
  border-top: 0 solid transparent;
  
  position: absolute;
  left: 124px;
  top: 54px;
}

HTML:

<div id="outer-container">
  <div id="progress-bar"></div>
  <div id="down"></div>
</div>


Solution

  • Answer

    It happens due to the poor accuracy when dealing with floating point numbers in browsers.

    The worst part is that they round differently the rendered element (what we actually see) from the element's size or positioning (the size they occupy or are placed at).

    That's why some elements seems to be +/- 1 pixel from the position they should be. It can vary depending on the rendering engine and even the size of your window, as you can try, it seems to fix/break each pixel you resize.

    Example

    In Firefox at 90% zoom:

    example

    • The padding of outer-container from 10px renders 9 (it's fine).
    • The border of progress-bar from 4px renders 3 (rounds down from 3.6)
    • The content of progress-bar from 40px renders 36 (it's fine).
    • At the moment: 9 + 3 + 36 = 48.
    • The top position of down from 54px, renders at 49 (rounds up from 48.6).

    In this case, the problem seems to be the rounding up for the down's position top, and rounding down the border's width of progress-bar. That's the empty pixel between progress-bar and down boxes.

    That's just an example of a couple of elements in a single browser, as you can imagine, it will happen all the time with each element in the screen for each browser.