How is the margin percentage value calculated

Issue

I know the percentage value of margin based on the width of its containing block.

when I assigned exact value to the margin of box, and the margin box not overflow the parent element, it works as expected.

But When I assigned percentage value, the margin-right or margin-inline-end will growth, just like margin-right: auto; no matter in Firefox or Chrome

So, is this a specification requirement, or a coincidence in the browser implementation.

var exact = document.querySelector('.exact')
var percent = document.querySelector('.percent')

exact.innerText = 'Computed margin-right: ' + getComputedStyle(exact).getPropertyValue('margin-right')
percent.innerText = 'Computed margin-right: ' + getComputedStyle(percent).getPropertyValue('margin-right')
body {
  margin: 0;
}

.wrap {
  margin: 20px auto;
  width: 200px;
  height: 200px;
  background-color: cadetblue;
  border: 1px solid transparent;
}

.box {
  width: 100px;
  height: 100px;
  background-color: antiquewhite;
}

.exact {
  margin: 20px;
}

.percent {
  margin: 10%
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>margin right</title>
</head>

<body>
  <div class="wrap">
    <div class="box exact"></div>
  </div>
  <div class="wrap">
    <div class="box percent"></div>
  </div>
</body>

</html>

Solution

I don’t think there’s a very satisfactory answer to this. But I’ll take you through the information available.

First of all though, a correction to your assertion that Chrome and Firefox behave the same. On Windows at least, they don’t. For the percentage margin case, Chromium browsers report a margin-right value of 20px while Firefox reports 80px.

Now, on over-constrainment: For in-flow, non-replaced block level elements like the divs in your examples, this equality MUST hold for the used values of the properties.

margin-left + border-left-width + padding-left + width + 
padding-right + border-right-width + margin-right = width of containing block

Plugging in the values you’ve assigned, along with the default values for the left side of the equality, we get for both examples

20px + 0px + 0px + 100px + 0px + 0px + 20px = 140px.

for the right side of the equality we have

200px

So the equality says: 140px = 200px.

That’s clearly false, so at least one of the values needs to be altered to make the equality true. When the layout direction is left-to-right, it’s the margin-right value which is altered. So the value of the margin-right property is altered to be 80px. All browsers make this adjustment correctly, and have done for years.


But the equality is required to hold only for the used values of the properties. It’s not required to hold for the computed values. So if getComputedValue() returned the computed values they should report the unadjusted value. The specification for margin-right tells us that the computed value is "the percentage as specified or the absolute length". So for the length-specified example, it would report "20px" and for the percentage-specified example it would report "10%" for the margin-right value.

However, the CSSOM spec tells us that "getComputedValue" is just a name, and that for backward compatibility purposes it actually returns the resolved value. The resolved value is sometimes the computed value, sometimes the used value and for some special cases not quite either of them.

For margin-right, it says that for your examples, the resolved value is the used value. The used value of the margin-right value should include two adaptations from the computed value. (i) the value is converted to a pixel value if it’s not already and (ii) the over-constrained adjustment should be applied. So getComputedValue() should return 80px for margin-right in both examples.


So while Chrome and Firefox both report the result of the first of these adaptations, always reporting the value in pixels, neither Chrome nor Firefox apply the over-constrained adjustment for the length-specified example, and only Firefox does it for the percentage specified example. So what’s actually happening? And I’m sorry, this is where we get into speculation a bit.

Browsers don’t actually follow specifications in a linear manner when dealing with legacy behaviour. What actually happens is that they try to do the thing that’s most web-compatible. (i.e. the behaviour that causes the fewest problems with existing web pages). They then feed this information back to the spec writers, and the specs can then get updated to match. Sometimes there can be disagreement about what the most web-compatible behaviour is. Sometimes new information can come to light which changes opinions as to the most web-compatible behaviour.

In some cases, this can lead to somewhat perverse resolutions. It’s conceivable, but no more than that, that the Firefox developers have decided that reporting the margin-right used value before over-constrained adjustment for length specified values and after over-constrained adjustment for percentage specified margin-right values gives the best web-compatible behaviour. And that the Chromium developers either disagreed or did not want to replicate such a non-intuitive outcome. Certainly such disagreements on the finer points of browser behaviour are not unusual. Sometimes, a browser maker will try to implement the simple thing and see how much push back they get from the web-dev community.

Either way, it’s most likely that eventually the CSSOM spec will be modified to better reflect what browsers do. What that resolution finally is remains to be seen.

Answered By – Alohci

Answer Checked By – Marilyn (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.