Flex-box ignores wrapped children's margin (margin collapse)

Given the following HTML:

<div class="outer">
  <div>
    <div class="inner">A</div>
  </div>
</div>
<div class="outer">
  <div class="inner">B</div>
</div>

and the following CSS (prefix free):

.outer {
  display: box;
  box-orient: vertical;

  height: 100px;
  width: 100px;

  background: red;
  margin: 10px;
}

.inner {
  height:     50px;
  margin-top: 10px;
  background: green;
}

A and B

Here is a CodePen.

A is wrapped in a <div> so it's margin gets ignored.

Q: How can I achieve B's behavior for A (margin) with the flex box model?

Note: The div wrappers can go multiple levels deep

Targeting: latest Chrome/Safari/iOS

Thank you very much for your help!

Edit: Thanks to @JoséCabo I came up with this:

.outer {
  display: flex;
  flex-direction: column;

  height: 100px;
  width: 100px;

  background: red;
  margin: 10px;
}

.inner {
  height:     50px;
  margin-top: 10px;
  background: green;
}

CodePen

Chrome: Chrome

Safari: Safari

Unfortunately it doesn't work in Safari as mentioned by @cimmanon, so I still need some help.

Upvotes: 7

Views: 14802

Answers (2)

cimmanon
cimmanon

Reputation: 68349

What you're looking at actually has nothing to do with Flexbox, but what's called margin collapse

.outer div {
  border: 1px solid;
}

The addition of the border has prevented the margin from collapsing. Rather than relying on margins, I would recommend placing a padding on the parent container:

.outer {
  padding-top: 10px;
}

Example:

.wrapper {
  background: #eef;
  border: 1px solid darkgray;
}

.container {
  display: flex;
  flex-wrap: wrap;
  margin: -1em;
}
  
.item {
  flex-grow: 1;
  margin: 1em;
  border: 1px solid black;
  padding: 1em;
  min-width: 6em;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
  <div class="wrapper">
  <div class='container'>
    <div class='item'>item</div>
    <div class='item'>item</div>
    <div class='item'>item</div>   
    <div class='item'>item</div>
    <div class='item'>item</div>
    <div class='item'>item</div>    
  </div>
  </div>
</body>
</html>

Now, to cover all of your prefixes, you need something like this:

.outer {
  display: -moz-box;
  display: -webkit-flexbox;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
  -moz-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  height: 100px;
  width: 100px;
  background: red;
  margin: 10px;
}

Upvotes: 6

Finally I came up with the right solution (for my specific problem).

.outer {
  display: flex;
  flex-direction: column;

  height: 100px;
  width: 100px;

  background: red;
  margin: 10px;
}

.inner {
  height: 50px;
  margin-top: 10px;
  background: green;
  display: inline-block;
  width: 100%;
}

CodePen

I'm using display: inline-block on .inner to disable margin collapsing and then compensate for the lost width with width: 100%.

All the credit goes to cimmanon for pointing me in the right "margin collapse" direction

Upvotes: 3

Related Questions