Flex Items Not Respecting Margins and Box-Sizing: Border-Box

Flex items not respecting margins and box-sizing: border-box

Keep in mind that box-sizing: border-box brings padding and borders into the width / height calculation, but not margins. Margins are always calculated separately.

The box-sizing property takes two values:

  • content-box
  • border-box

It does not offer padding-box or margin-box.

Consider those terms when referring to the CSS Box Model.

Sample Image

source: W3C

3.1. Changing the Box Model: the box-sizing
property

content-box

The specified width and height apply to the width and height respectively
of the content box of the element. The padding and border of the
element are laid out and drawn outside the specified width and height.

border-box

Length and percentages values for width and height on this element
determine the border box of the element. That is, any padding or
border specified on the element is laid out and drawn inside this
specified width and height. The content width and height are
calculated by subtracting the border and padding widths of the
respective sides from the specified width and height properties.


Also, an initial setting of a flex container is flex-shrink: 1. This means that flex items can shrink in order to fit within the container.

Therefore, a specified width, height or flex-basis will not hold, unless flex-shrink is disabled.

You can override the default with flex-shrink: 0.

Here's a more complete explanation: What are the differences between flex-basis and width?


Here's a simple solution:

You have four boxes. You want three on row 1 and the last on row 2.

This is what you have:

flex: 1 1 33.33%;
margin: 10px;

This breaks down to:

  • flex-grow: 1
  • flex-shrink: 1
  • flex-basis: 33.33%

We know that box-sizing: border-box factors padding and borders into the flex-basis. That's not a problem. But what about the margins?

Well, since you have flex-grow: 1 on each item, there is no need for flex-basis to be 33.33%.

Since flex-grow will consume any free space on the row, flex-basis only needs to be large enough to enforce a wrap.

Since margins also consume free space, flex-grow will not intrude into the margin space.

So try this instead:

flex: 1 1 26%;
margin: 10px;

* {  box-sizing: border-box;}
.horizontal-layout { display: flex; width: 400px;}
header > span { flex: 1 0 26%; /* ADJUSTED */ margin: 10px;}
header#with-border-padding { flex-wrap: wrap;}
header#with-border-padding>span { flex: 1 0 26%; /* ADJUSTED */}
header#with-border-padding>.button { border: 1px solid black; padding-left: 5px;}
header>.button { background-color: grey;}
header>.app-name { background-color: orange;}
NO flex-wrap: wrap, so it not respects the flex 33% <br/><header class="horizontal-layout">  <span class="button">A</span>  <span class="app-name">B</span>  <span class="button">C</span>  <span class="button">D</span></header><br/><br/> WITH flex-wrap: wrap : I expect to have 3 boxes in first row and D box in a down<br/><header id="with-border-padding" class="horizontal-layout">  <span class="button">A</span>  <span class="app-name">B</span>  <span class="button">C</span>  <span class="button">D</span></header>

Flex Item only respecting margin or padding when both are set

You also have to write box-sizing for the elem class as well. Due to the dimension of the elements that include padding and maybe borders in future. Because box-sizing property allows us to include the padding and border in an element's total width and height. I hope it helped :)

Flex-grow doesn't respect border-box. Bug or not? And any way to overcome?

This is not a bug. It is the desired behavior.

When you have a bunch of flex-children, and apply flex-basis: 0 and flex-grow: 1 to them, the following steps are being taken by the browser:

  • Render text and other content the flex-children have
  • Apply padding, borders and margins (taking the box-sizing into account)
  • Starting with the smallest flex-child (because of flex-basis: 0) keep adding width to the items until the container is full (flex-grow: 1).

That last step is where your problem lies. The browser doesn't force the elements to first be 0 pixels wide and all grow evenly. The browser simply starts with the smallest flex-child, giving it more width until it has grown as wide as the second smallest. At that point both of these flex-children will both grow evenly. And so forth, adding width until all space is distributed. This is also why box-sizing: border-box seems to not working.

You can fix this by adding a container to each of the flex-items, which has the border-box part applied. See this Fiddle. The contents have been wrapped into another div, and the styles have been applied to it as well (except for the flex-specific properties, of course).

Better way to set distance between flexbox items

  • Flexbox doesn't have collapsing margins.
  • Flexbox doesn't have anything akin to border-spacing for tables (edit: CSS property gap fulfills this role in newer browsers, Can I use)

Therefore achieving what you are asking for is a bit more difficult.

In my experience, the "cleanest" way that doesn't use :first-child/:last-child and works without any modification on flex-wrap:wrap is to set padding:5px on the container and margin:5px on the children. That will produce a 10px gap between each child and between each child and their parent.

Demo

.upper {
margin: 30px;
display: flex;
flex-direction: row;
width: 300px;
height: 80px;
border: 1px red solid;

padding: 5px; /* this */
}

.upper > div {
flex: 1 1 auto;
border: 1px red solid;
text-align: center;

margin: 5px; /* and that, will result in a 10px gap */
}

.upper.mc /* multicol test */ {
flex-direction: column;
flex-wrap: wrap;
width: 200px;
height: 200px;
}
<div class="upper">
<div>aaa<br/>aaa</div>
<div>aaa</div>
<div>aaa<br/>aaa</div>
<div>aaa<br/>aaa<br/>aaa</div>
<div>aaa</div>
<div>aaa</div>
</div>

<div class="upper mc">
<div>aaa<br/>aaa</div>
<div>aaa</div>
<div>aaa<br/>aaa</div>
<div>aaa<br/>aaa<br/>aaa</div>
<div>aaa</div>
<div>aaa</div>
</div>

Is this the expected behaviour of box-sizing: border-box ?

See MDN:

border-box tells the browser to account for any border and padding in the values you specify for an element's width and height. If you set an element's width to 100 pixels, that 100 pixels will include any border or padding you added, and the content box will shrink to absorb that extra width. This typically makes it much easier to size elements.

It doesn't put margins inside the box.

Consider flexbox instead.