"margin-top" and "margin-bottom" overlapping, why?

Asked

Viewed 2,406 times

16

I have three elements div:

HTML

<div class="wrapper">
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
</div>

CSS

.box {
    background-color: black;
    box-sizing: border-box;
    height: 48px;
    width: 100%;
    margin: 7px 0;
}

We soon concluded that the vertical spacing between them should be 14 pixels (7px + 7px).

But it only happens when I define display:inline-block;:

.box {
    ... /* definições apresentadas em cima */
    display: inline-block;
}

Otherwise the spacing between each div gets 7px, it’s as if one margin overlaps the other.

Can anyone tell me why this is happening ?

Example

  • 2

    Technically the div are side by side as they are "block" elements, but as they do not have space to appear side by side, they are visually one under the other with collapsed margins. For that final aspect you want to get, you’ll have to use float:left; to float them to the left, thereby ensuring that the margins are fully respected.

  • Precisely this has to do with the display of the element... http://www.maujor.com/tutorial/propriedade-css-display.php

2 answers

16

When there are stacked blocks, the lower edge of the top block is combined with the upper margin of the bottom. The effective value is the highest margin, not the sum of the two. This is called "margin collapse", and is described in the CSS specification:

In CSS, the adjoining margins of two or more boxes (which Might or Might not be siblings) can combine to form a single margin. Margins that combine this way are said to Collapse, and the Resulting Combined margin is called a collapsed margin.

Adjoining vertical margins Collapse, except:

  • Margins of the root element’s box do not Collapse.
  • If the top and bottom margins of an element with clearance are adjoining, its margins Collapse with the adjoining margins of following siblings but that Resulting margin does not Collapse with the bottom margin of the Parent block.

Translation:

In CSS, adjacent margins of two or more pits (who may or may not be brothers in the hierarchy of the GIFT) can combine and form a single margin. Margins that combine in this way are said to collapse, and the resulting combined margin is called collapsed margin.

Vertically adjacent margins collapse; exceptions:

  • Root element box margins never collapse.
  • If the upper and lower margins of an element with clear adjacent, their margins collapse with the adjacent margins of the next siblings, but the resulting margin does not collapse with the lower margin of the parent block.

This basic rule and others described below in the specification have the following consequences (translated from the specification itself):

  • Margins between a floating box and any other box do not collapse (even between a float and their descendants who are rendered "in the flow"/*in-flow).
  • Margins of elements that establish new block contexts (such as floats and Elements with any 'overflow' value except 'Visible') do not collapse with their descendants "in the flow".
  • Margins of elements with absolute position do not collapse (not even with their descendants "in the flow").
  • Pit margins inline-block do not collapse (not even with their descendants "in the flow").
  • The lower margin of a block "in the flow" always collapses with the upper margin of the next brother who is also in the flow and is a block, unless that brother has 'clear' set with a value that don’t be 'None'.
  • The lower margin of a block in the flow with 'height' with 'auto' and 'min-height' value with zero collapses with the lower margin of its immediate last descendant that is a block and is in the flow, if that block does not have padding bottom or bottom edge, and if the the margin of that element does not collapse with any other upper margin that has 'clear' defined.
  • The edges of a box collapse if the min-height property has zero value, and if it has no edges or padding higher or lower, has a value of 0 or 'auto' for 'height', and contains a row box, provided that all of its direct descendants in the collapsing stream.

Yes, it is complex, several concepts interactions between elements need to be taken into account to define whether or not there will be margin collapse.

Solution

Actually there is no universal solution, it depends on the case. The simplest is to use display: inline-block, as you have already pointed out in the question, but this will not always give the desired visual result. A solution from @utluiz is more generic, but has hack face. In certain cases, it is worth simply bending the margins of the elements involved. Another possibility is to use floats, as suggested by @Zuul.

  • 1

    Malta wants a solution here in the :P answer

  • 1

    Excellent reply @bfavaretto, thank you very much! I really think the best solution would be to put the display: inline-block or use only margin-top or margin-bottom with the values folded as you suggested.

  • @Willianribeiro In your case this seems to be the simplest solution. But keep an eye on the answer of utluiz, if he achieves something with :after or :before, would be a good.

3

This answer aims to add some alternatives for those who need to change the default behavior of the CSS that culminates in margin Collapse.

Alternative 1: hidden element (hack!)

One of the palliative solutions to avoid the effect of margin Collapse is to include a hidden element between the two margin elements.

Example:

<div class="wrapper">
  <div class="box"></div>
  <div class="hidden">.</div>
  <div class="box"></div>
  <div class="hidden">.</div>
  <div class="box"></div>
</div>
.hidden {
    overflow: hidden; 
    height: 0px; 
    width: 0px;
}

See on jsfiddle.

As for the criticism about creating additional elements, note that even using ::after or ::before via CSS also there is a overhead, after all the browser needs to internally create that element. The same goes for other tags that generate additional processing.

In addition, the user has not established a number of elements. One cannot assume that a solution is bad because it is not suitable for thousands of items, when that amount is an exception and not the rule in web development in general. Incidentally, any large amount of items might crash certain systems and we would have to throw away several web frameworks that encapsulate complex components.

Alternative 2: doubling the margin

For those who did not like the first "hack", a more specific alternative solution would increase the lower margin of the elements. To "correct" the remaining margin at the end it is possible to use the last-child.

The CSS of this case:

.box {
    background-color: black;
    box-sizing: border-box;
    height: 48px;
    width: 100%;
    margin: 14px 0px 7px 0;
}

.box:first-child {
    margin: 7px 0;
}

Example in jsfiddle.

Well, some of us still did not like this solution, although there is nothing absurd about it. But for those who want to find hair in an egg, I changed it from last-child for first-child because apparently in old browsers (IE) may perform better.

Alternative 3: inline-block

The last alternative is to use the suggestion from reply from @bfavaretto.

The reason I didn’t post an alternative using the inline-block since the beginning is that the browser adds additional spaces between the blocks and ended up not investigating in depth the reason.

But after resurrecting the topic a few months later, I decided to do the research and the solution was, after all, quite simple.

The additional spacing is caused due to the source properties, so a font-size to solve the problem.

Let’s see:

.box {
    background-color: black;
    box-sizing: border-box;
    height: 48px;
    width: 100%;
    margin: 7px 0;
    display: inline-block;
}

.wrapper {
    font-size: 0;
}

Check out on jsfiddle.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.