Richard Hunter
Richard Hunter

Reputation: 2200

Do browsers correctly implement the Visual Formatting Model from the CSS spec?

Reading the CSS Spec https://drafts.csswg.org/css2/visudet.html#blockwidth I am somewhat scratching my head at section 10.3.3. which supposedly explains how block level non-replaced elements are laid out within their containing block. It reads:

The following constraints must hold among the used values of the other properties:

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

..If all of the above have a computed value other than 'auto', the values are said to be "over-constrained" and one of the used values will have to be different from its computed value. If the 'direction' property of the containing block has the value 'ltr', the specified value of 'margin-right' is ignored and the value is calculated so as to make the equality true. If the value of 'direction' is 'rtl', this happens to 'margin-left' instead.

This suggests that an element will always be stretched or somehow shrunk to fit into its containing block, but that's clearly not the case; Setting a width to an explicit value will make an element either narrower or wider than it's containing block, overflowing if necessary. Also I see no sign of the margin-right or margin-left properties changing in accordance with the equation.

So have I misunderstood the spec (admittedly likely!) or are browsers not implementing it properly?

Upvotes: 4

Views: 94

Answers (1)

Oriol
Oriol

Reputation: 288650

For example, assume the containing block is 400px wide, and the block has:

  • margin-left: 15px
  • border-left-width: 15px
  • padding-right: 15px
  • width: 300px
  • padding-right: 15px
  • border-right-width: 15px
  • margin-right: 15px

Then, the sum will be 390px, so the remaining free space is 10px.

Since the direction is ltr by default, the used value of the right margin will be 25px instead of 15px. That is, the remaining free space is added to the right margin in order to keep the block aligned to the left of the containing block.

Otherwise, the left margin would be modified to keep the block aligned to the right.

.container {
  margin-bottom: 10px;
  width: 400px;
  border: 1px solid red;
}
.block {
  margin: 0 15px;
  border: 15px solid;
  padding: 0 15px;
  width: 300px;
  direction: ltr;
}
.ltr { direction: ltr; }
.rtl { direction: rtl; }
Left-to-right container:
<div class="ltr container">
  <div class="block">
    Right margin becomes 25px instead of 15px
  </div>
</div>
Right-to-left container:
<div class="rtl container">
  <div class="block">
    Left margin becomes 25px instead of 15px
  </div>
</div>

In case the sum was greater than the containing block, the remaining free space would be negative. This is not problematic because margins can be negative.

Therefore, visually it's implemented correctly. However, note that under the hood, browsers might just just be distributing the remaining free space to the left or right of the block instead of modifying its margins.

This might explain why getComputedStyle seems to return incorrect reults. According to CSSOM, it should return the resolved value, which in case of margins is

If the property applies to the element or pseudo-element and the resolved value of the 'display' property is not 'none', the resolved value is the used value. Otherwise the resolved value is the computed value.

Then, in this case it should be 25px but seems to be 15px on most browsers.

Upvotes: 4

Related Questions