Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

html

css

flexbox

I'm trying to achieve a simple thing that we have in standard CSS.

Let's say that I have a grid system with 3 columns and box-sizing: border-box.

That means that I will fit 3 boxes and with a margin that will shrink the size to fit max 3 boxes.

But when I try to do that with FLEXBOX, it's a pain!!

So if I have div's with flex: 1 1 33.33%; margin: 10px; I was expecting to have 3 boxes per row... but if I use flex-wrap: wrap, that will not shrink to fit 3 boxes .

Here is a example.. the idea is that the second row would have 3 boxes in a row and fourth box would be in the last row.

Thanks

https://jsfiddle.net/mariohmol/pbkzj984/14/

.horizontal-layout {
  display: flex;
  flex-direction: row;
  width: 400px;
}

header>span {
  /* flex: 1 1 100%; */
  /* flex: 1 0 100%; */
  flex: 1 1 33.33%;
  margin: 10px;
}

header>.button {
  background-color: grey;
}

header>.app-name {
  background-color: orange;
}

header#with-border-padding {
  flex-wrap: wrap;
}

header#with-border-padding>span {
  flex: 1 1 33.33%;
  box-sizing: border-box;
  /* this is not useful at all */
}

header#with-border-padding>.button {
  border: 1px solid black;
  padding-left: 5px;
}
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>
like image 463
mariomol Avatar asked Jul 12 '17 12:07

mariomol


2 Answers

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.

enter image description here

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>
like image 77
Michael Benjamin Avatar answered Nov 14 '22 01:11

Michael Benjamin


As well pointed out in Michael_B's answer margins are not taken into account when calculating flex item width. But you can subtract margins from flex-basis to get desired behaviour.

For current case:

header > span {
  /* 33.33% - margin-left - margin-right */
  flex: 1 1 calc(33.33% - 20px);
  margin: 10px;
}

Also you should set box-sizing: border-box to include padding (and borders) when calculating width.

* {
  box-sizing: border-box;
}

.horizontal-layout {
  display: flex;
  width: 400px;
}

header > span { 
  flex: 1 0 calc(33.33% - 20px);
  margin: 10px;
}

header#with-border-padding {
  flex-wrap: wrap;
}

header#with-border-padding > span {
  flex: 1 0 calc(33% - 20px);
}

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>
like image 39
Vadim Ovchinnikov Avatar answered Nov 14 '22 03:11

Vadim Ovchinnikov