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

15

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 therefore conclude that the vertical spacing between them should be 14 pixels (7px + 7px).

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

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

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

Can anyone tell me why this happens?

Example

    
asked by anonymous 07.01.2014 / 17:33

2 answers

15

When blocks are stacked, the bottom edge of the top block is combined with the top edge of the bottom block. The effective value is the largest margin, not the sum of the two. This is called "border collapse," and it is described in the CSS specification : / p>

  

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 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 boxes (which may or may not be siblings in the DOM hierarchy) can combine to form a single margin. It is said that margins that combine in this way collapse, and the resulting combined margin is called a collapsed margin.

     

Vertically adjacent margins collapse; exceptions:

     
  • Root box margins never collapse.
  •   
  • If the top and bottom margins of an element with clear are adjacent, their margins collapse with the adjacent margins of the following siblings, but the resulting margin does not collapse with the lower margin of the parent block. / li>   

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

  
  • Margins between a floated box and any other box do not collapse (even between a float ) and its descendants that are rendered   "in-flow" / * in-flow).
  •   
  • Element margins that establish new block contexts (such as floats and elements with any value of 'overflow' except 'visible')   do not collapse with their descendants "in the flow".
  •   
  • Absolutely positioned element margins do not collapse (even with their descendants "in the flow").
  •   
  • Inline-block pit margins do not collapse (even with their descendants "in the flow").
  •   
  • The bottom margin of a block "in the flow" always collapses with the top edge of the next sibling that is also in the stream and is a   block, unless that sibling has 'clear' set to a value that   is not 'none'.
  •   
  • The bottom margin of a block in the 'height' flow with value 'auto' and 'min-height' with zero value collapses with the bottom margin of its   last immediate descendant that is a block and is in the flow, if   this block does not have lower padding or bottom edge, and if the   margin of this element does not collapse with some other upper margin   which has clear 'defined'.
  •   
  • The margins of a box collapse with each other if the 'min-height' property has zero value, and if it has no borders or padding   upper or lower, has a value of 0 or 'auto' for 'height', not   contain a line box, as long as all your direct descendants   in the stream to collapse.
  •   

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

Solution

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

    
07.01.2014 / 17:37
2

This answer aims to add some alternatives for those who need to change the default CSS behavior 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 border 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 the jsfiddle .

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

Additionally, the user has not set a number of elements. You can not assume that one solution is bad because it is not for thousands of items, when that amount is an exception and not the rule in general web development. In fact, any large number of items can 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 workaround would be to increase the bottom margin of the elements. To "correct" the remaining margin at the end you can use 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 still did not like this solution, although there was nothing absurd about it. But for those who want to find the egg, I changed it from last-child to first-child because apparently in old browsers (IE) may perform better .

Alternative 3: inline-block

The last alternative is to use the @bfavaretto response .

The reason I did not post an alternative using inline-block from the beginning is that the browser adds additional spaces between the blocks and I did not really investigate the reason.

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

Additional spacing is caused due to font properties, so a font-size is sufficient 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 jsfiddle .

    
07.01.2014 / 17:45