'Strange border-width behavior in Chrome - Floating point border-width?

Certain fluid designs, especially those involving %-width iFrames, seem to cause some strange rounding-type errors in Chrome (I've got version 21).

This Fiddle demonstrates the problem. Set the border to an integer pixel value, and the values you get back when you ask for them are floating point numbers slightly smaller than the expected value.

Trying the exact same code in CodePen doesn't yield the same results, presumably because the iFrame and other styles around it aren't set up the same way.

(I've also seen this behavior for the basic width and height attributes, although I was unable to replicate that part of the problem in jsFiddle.)

This doesn't seem to be a problem in Firefox or in IE8.

Any ideas as to what, specifically, is causing this strange behavior, and how I can work around it to get at the real values?


The plot thickens. In an attempt to shim around the problem, I found that values over 10px do not appear to be subject to the issue.

Also, based on @GionaF's comment, it appears to work properly in Chrome 22.



Solution 1:[1]

I can't reproduce this behavior in Chrome currently, but this behavior most likely has to do with Subpixel Rendering. Essentially, Webkit will do calculations using integer math to avoid floating point imprecision.

It seems that currently, border does not use subpixel rendering which may explain why the issue is not visible in new Chrome versions.

Solution 2:[2]

Does setting box-sizing to border-box for all elements help at all? This will change the way the box model is calculated where the border and padding won't affect the width.

*,
*:before,
*:after {
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box; }

Solution 3:[3]

I was also having the issue with border in inputs and other elements. After an hour of time spent, I found unexpected reason to cause this problem. Bootstrap css uses normalize css and the following code was causing such issue:

hr {
    -webkit-box-sizing: content-box;
    -moz-box-sizing: content-box;
    box-sizing: content-box;
}

To solve this issue, I override the css in my style.css:

hr {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

The hr rule is really not related with inputs but it works. This is really unexpected behavior, try it and will work.

Solution 4:[4]

I just had another example of this with chrome (chromium 76) caused by a transform: translate().

<div class='container'>
    <div class="box-triangle"></div>
</div>

.box-triangle has an ::after element shaping a border triangle, which is absolutely positioned on top of .box-triangle (right after the padding).
If I set a translate with % on .container and the resulting percentage isn't an integer then .box-triangle border will show slightly under its ::after element.

Solutions : Using a px translate instead of %, or making sure the percentage of the container dimension is an integer

Edit : the exact same thing happen if the transform is applied directly on .box-triangle (see last example in snippet)

* {
  box-sizing: border-box;
}

body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-around;
  min-height: 100vh;
  width: 100%;
  background-color: #e55039;
  font-size: 16px;
}

.box-triangle {
  position: relative;
  margin: 0;
  color: #f7f1e3;
  background-color: #227093;
  font-size: 20px;
  margin: 0;
  padding: 1em;
  border: 5px solid white;
}

.box-triangle::after {
  content: "";
  position: absolute;
  height: 0;
  width: 0;
  margin: 0;
  border: solid transparent;
  left: 50%;
  bottom: 100%;
  border-bottom-color: #227093;
  border-width: 1.5em;
  -webkit-transform: translate(-50%, 0);
  transform: translate(-50%, 0);
}

[class^='container'] {
  margin: 20px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.container-1 {
  position: relative;
  height: 10.5rem;
  width: 20rem;
  /* transform: translate(1%, 1%); */
}

.container-2 {
  position: relative;
  height: 10.5rem;
  width: 20rem;
  transform: translate(0, 5%);
}

.container-3 {
  position: relative;
  height: 10rem;
  width: 20rem;
  transform: translate(0, 5%);
}

.container-4 {
  position: relative;
  height: 168px;
  width: 320px;
  transform: translate(0, 5%);
}

.container-5 {
  position: relative;
  height: 168px;
  width: 320px;
  transform: translate(0, 8px);
}

.noContainer {
  position: relative;
  height: 168px;
  width: 320px;
  transform: translate(0, 5%);
}
<div class='container-1'>
  <div class="box-triangle">no translate + container height: 10.5 rem (168px)</div>
</div>

<div class='container-2'>
  <div class="box-triangle">translate(0, 5%) + container height: 10.5 rem (168px)</div>
</div>

<div class='container-3'>
  <div class="box-triangle">translate(0, 5%) + container height: 10rem (160px)</div>
</div>

<div class='container-4'>
  <div class="box-triangle">translate(0, 5%) + container height: 168px</div>
</div>

<div class='container-5'>
  <div class="box-triangle">translate(0, 8px) + container height: 168px</div>
</div>

<div class="box-triangle noContainer">No container + translate(0, 5%) + height: 168px</div>

Solution 5:[5]

Check value of window.devicePixelRatio in the console. If it is smaller than 1, you will get non-precise values as you describe.

window.devicePixelRatio can become smaller than 1 when you're working with an external display and open or close the laptop screen.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Explosion Pills
Solution 2
Solution 3 Bhojendra Rauniyar
Solution 4
Solution 5 Ivan Nikitin