105

Below are two rows.

  • First row is two items at flex 1 and one at flex 2.

  • Second Row is two items at flex 1.

According to the spec 1A + 1B = 2A

But when padding is included in the calculation the sum is incorrect as you can see in the example below.


QUESTION

How to get flex box to include padding into its calculation so the boxes in the example line up correctly?

.Row{
  display:flex;
}
.Item{
  display:flex;
  flex:1;
  flex-direction:column;
  padding:0 10px 10px 0;
}
.Item > div{
  background:#7ae;
}
.Flx2{
  flex:2;
}
<div class="Row">
  <div class="Item">
    <div>1A</div>
  </div>
  <div class="Item">
    <div>1B</div>
  </div>
  <div class="Item Flx2">
    <div>1C</div>
  </div>
</div>

<div class="Row">
  <div class="Item">
    <div>2A</div>
  </div>
  <div class="Item">
    <div>2B</div>
  </div>
</div>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
DreamTeK
  • 32,537
  • 27
  • 112
  • 171

6 Answers6

118

The solution:

Set margin on the child element instead of padding on your flex item.

.Row{
  display:flex;
}
.Item{
  display:flex;
  flex:1;
  flex-direction:column;
}
.Item > div{
  background:#7ae;
  margin:0 10px 10px 0;
}
.Flx2{
  flex:2;
}
<div class="Row">
  <div class="Item">
    <div>1A</div>
  </div>
  <div class="Item">
    <div>1B</div>
  </div>
  <div class="Item Flx2">
    <div>1C</div>
  </div>
</div>

<div class="Row">
  <div class="Item">
    <div>2A</div>
  </div>
  <div class="Item">
    <div>2B</div>
  </div>
</div>

The problem:

The calculation is done without padding. So; adding padding to the flex element is not giving you your expected width by the spec.

The specific article

For example, the available space to a flex item in a floated auto-sized flex container is:

  • the width of the flex container’s containing block minus the flex container’s margin, border, and padding in the horizontal dimension
  • infinite in the vertical dimension

Why is the padding not calculated? That's what the spec wants.

Determine the available main and cross space for the flex items. For each dimension, if that dimension of the flex container’s content box is a definite size, use that; if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint; otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value. This might result in an infinite value.

If you subtract the padding and margin from the element's size, you get:

1A + 1B = 2A

However, after you did that, the padding was added to the element. The more elements, the more padding. That's not being calculated in the width, causing your statement to be false.

DreamTeK
  • 32,537
  • 27
  • 112
  • 171
Randy
  • 9,419
  • 5
  • 39
  • 56
  • 3
    This not only solved my problem but was very helpful understanding flex also. Thank you. – DreamTeK Jun 14 '16 at 07:34
  • 8
    the problem with using margin instead of padding is that when you hover on the margin section, it won't count as you hovering on that element. – Dinh Tran Jul 10 '17 at 08:55
  • 2
    It also doesn't help when you need the padding for borders. I was hoping setting `box-sizing: border box;` would help but no such luck... – 43Tesseracts Jan 07 '18 at 07:05
  • 3
    It's not really clear: The spec section you highlighted says: **"...subtract the flex container's margin, border and padding from the space available..."** But the padding isn't on the flex container. It's on the flex items. (The nested flex container isn't relevant to the problem.) So I'm not sure those excerpts apply. – Michael Benjamin Mar 08 '18 at 15:51
  • 1
    using margin on children instantly adds scollbars even if there's no content in the child – johntrepreneur Sep 21 '22 at 04:27
  • @johntrepreneur Not in my example, if you can ask a new question with your issue maybe we can find out why. – Randy Sep 21 '22 at 09:57
  • how is it possible that they screwed this up so badly? I think I'm going back to html tables for layout, clearly that algorithm was written by a different generation of programmers. – capr Mar 10 '23 at 11:33
7

How to get flexbox to include padding in calculations?

In your code, padding is included in the calculations.

According to the spec 1A + 1B = 2A

I don't believe this is correct. Maybe provide a link reference for an explanation.


The flex-grow property

When you apply flex: 1 to an element, you are using the flex shorthand property to say this:

  • flex-grow: 1
  • flex-shrink: 1
  • flex-basis: 0

flex-grow tells a flex item to consume the free space in the container.

Here is your code:

.Item {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: 0 10px 10px 0;
}

In the first row, padding-right: 10px is applied to three flex items.

In the second row, padding-right: 10px is applied to two flex items.

Hence, in the first row there is 10px less free space to distribute. This breaks the grid's alignment.

For distributing space (e.g., you want an element to take the remaining height or width of a container), use flex-grow.

For precise sizing of a flex item use flex-basis, width or height.

Here's some more info:

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
1

By default, when using flexbox, the padding property is not included in the calculation of the element's width or height. To include the padding in the calculation, you can set the box-sizing property to border-box.

.Row{
  display:flex;
}
.Item{
  display:flex;
  flex:1;
  flex-direction:column;
  padding:0 10px 10px 0;
  box-sizing: border-box; // Add this line
}
.Item > div{
  background:#7ae;
}
.Flx2{
  flex:2;
}
0

Not sure I follow the accepted answer. The nested flex container doesn't seem to be relevant to the problem. If you run the example without display: flex; on .Item, the problem persists.

The problem here seems to be that flex-grow only calculates the available space that it can take after factoring in the cumulative horizontal padding.

Let's assume the top level flex container is 300px wide.

1st row's available space: 300px - 30px padding = 270px

The flex items in this row have flex-grows of 1, 1, and 2, for a total of 4 units. 270 / 4 = 67.5px. The content boxes of 1A and 1B are thus 67.5px each, the content box of 1C is 135px.

2nd row's available space: 300px - 20px padding = 280px

We have flex-grows of 1 for both 2A and 2B in this row. 280 / 2 = 140px.

So 1A and 1B would have a content box of 67.5px + 10px horizontal padding, making their total width 77.5px each.

2A would have a content box of 140px + 10px horizontal padding, making its total width 150px.

77.5px + 77.5px ≠ 150px. In other words, 1A + 1B ≠ 2A, and that's why they aren't lining up.

The accepted answer does solve the problem, but CSS grid has become well supported since that answer was submitted, and is the more idiomatic approach nowadays for this problem.

.Row{
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}

.Item:nth-child(n + 3) {
  grid-column: span 2;
}

.Item > div{
  background:#7ae;
}
<div class="Row">
  <div class="Item">
    <div>1A</div>
  </div>
  <div class="Item">
    <div>1B</div>
  </div>
  <div class="Item">
    <div>1C</div>
  </div>
  <div class="Item">
    <div>2A</div>
  </div>
  <div class="Item">
    <div>2B</div>
  </div>
</div>
Tom T
  • 314
  • 2
  • 12
0

You can add wrapper element inside your flex child. Giving padding to that element won't affect root flex element's width/height.

<div style='display: flex;'>
  <div>
    <div style='padding: 10px;'>
      <-- your other elements  -->
     </div>
  </div>
</div>
Brang Ja
  • 1
  • 1
  • 1
  • 1
    Can you provide some code to help beginner to reproduce this – Erwan Daniel May 23 '23 at 13:27
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 23 '23 at 13:27
-2

You can use floated pseudo block elements instead of padding, like this: (In this case 30px right padding)

.Item:after {
  content: '';
  display: block;
  float: right;
  width: 30px;
  height: 100%;
}
sorioz
  • 45
  • 4