Why Is Padding Expanding a Flex Item

Using Flexbox with padding in the inner items

The flex-grow property is not designed for precision sizing of flex items. It's job is to distribute space in the container among flex items. So flex-grow is not the best tool for this job.

Instead, use flex-basis, which is the equivalent of width or height (depending on flex-direction) in a flex container.

Then add box-sizing: border-box to integrate the padding into the flex item's width.

revised fiddle demo

<div style="display: flex;">  <div style="flex-basis: 60%; padding: 4rem ; background-color: red; box-sizing: border-box;">  </div>  <div style="flex-basis: 40%; background-color: green;">    </div></div>

How to make flex-grow ignore padding?

How to make flex-grow ignore padding?

flex-grow cannot ignore padding:

  • flex-grow consumes free space.

  • padding occupies space.

  • So flex-grow must factor in padding in order to work properly.

Here's a workaround that may be useful to you:

.flexbox {
display: flex;
max-width: 1241px;
height: 30px;
}

label {
flex: 5 1 0;
background-color: cyan;
}

#input {
flex: 13 1 0;
height: 100%;
}

input {
height: 100%;
width: 100%;
padding-left: 25px;
background-color: yellow;
border: none;
}

div:last-child {
flex: 6 1 0;
background-color: red;
}

* {
box-sizing: border-box;
}

body {
margin: 0;
font-size: 14px;
}
<div class="flexbox">
<label>width must = 258.54px</label>
<div id="input"><input type="text" value="width must = 672.20px"></div>
<div>width must = 310.25px</div>
</div>

Padding of one flex item influence the rest of flex items

i am added align-items:center;. because, default align-items: is stretch

.footer__form {    flex-basis: 49%;    display: flex;    justify-content: space-between;    flex-direction: row;    align-items:center;}     .footer__form--email {      flex: 0 0 65%;      padding: 1.5rem 1.5rem;      }
.footer__form--email--submit { flex: 0 0 30%; font-weight: 700;
}
<div class="footer__form">        <input type="email" class="footer__form--email" placeholder="enter your email for updates">        <input type="submit" class="footer__form--submit u-uppercase-text" value="sign up"></div>

How to get flexbox to include padding in calculations?

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>

Why does child padding expand parent width?

A complex case of shrink-to-fit behavior. All your rows are flex items so their width depend on their content. Now the complex part is that you are making the child having width:100% which means 100% of the parent width so we have a cycle here.

In such case the browser will do the following:

  1. ignore the width:100% (consider it as auto)
  2. find the width of the child using other properties
  3. set the width of the parent based on (2)
  4. get back to the resolve width:100% based on the width found in (3)
  5. the width of child may change but not the one of the parent.

If one child is 100% then logically both cannot fit into one line and each one will be on its own line.

Here is a 2 steps illustration to understand what is happening:

body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
color: #C0C0C0;
background-color: #202020;
display: flex;
flex-direction: column;
align-items: center;
}

* {
box-sizing: border-box;
}
.col {
display: flex;
flex-direction: column;
}
.row {
display: flex;
flex-direction: row;
background-color: gray
}
.wrap {
flex-wrap: wrap;
}
.align-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.gap {
gap: 20px;
}
input {
background-color:#202020 ;
border: 1px solid #C0C0C0 ;
border-radius: 5px;
padding: 12px 20px;
margin: 8px 0;
box-sizing: border-box;
}
.inputContainer {
max-width: 300px;
width: 100%;
}
.inputContainer > * {
width: 100%;
}
<form class="col gap align-center" style="width: 90vw" action="">
<div class="row wrap gap justify-center">
<div class="col inputContainer" style="width:auto">
<label>expands:</label>
<input />
</div>
<div class="col inputContainer" style="width:auto">
<label>parent:</label>
<input />
</div>
</div>
</form>
<hr>
<form class="col gap align-center" style="width: 90vw" action="">
<div class="row wrap gap justify-center">
<div class="col inputContainer" >
<label>expands:</label>
<input />
</div>
<div class="col inputContainer">
<label>parent:</label>
<input />
</div>
</div>
</form>

Flexbox item widths are affected by padding

1 option you have is to set flex-basis: 33.33% or a number relative to the amount of children you have. I'm not sure if there is a way to do it dynamically.

Flex-Grow leading to different widths depending on the markup

Simply keep the same structure and wrap all the input in div element to get the same result:

* {
box-sizing: border-box;
}

div {
width: 400px;
}

.form-input {
padding: 4px;
border: 1px solid #565656;
}

.form-input-list {
list-style-type: none;
width: 100%;
padding-left: 0;
}

.form-input-item {
display: flex;
align-items: center;
}

.form-label {
flex-basis: 0;
flex-grow: 1;
background-color: yellow;
}

.form-input,
.form-input-group {
flex-basis: 0;
flex-grow: 1.5;
}

.form-input-group {
display: flex;
}

.form-input-group-item {
flex-basis: 0;
flex-grow: 1;
}
<div>
<form>
<ul class="form-input-list">
<li class="form-input-item">
<label class="form-label">First Name</label>
<div class="form-input-group">
<input type="text" class="form-input" />
</div>
</li>
<li class="form-input-item">
<label class="form-label">Last Name</label>
<div class="form-input-group">
<input type="text" class="form-input" />
</div>
</li>
<li class="form-input-item">
<label class="form-label">Phone</label>
<div class="form-input-group">
<select class="form-input-group-item form-input">
<option>+49</option>
</select>
<input class="form-input-group-item form-input" type="text" />
</div>
</li>
</ul>
</form>
</div>

How does flex-shrink factor in padding and border-box?

Flexbox defines this as

For every unfrozen item on the line, multiply its flex shrink factor
by its inner flex base size, and note this as its scaled flex
shrink factor
. Find the ratio of the item’s scaled flex shrink
factor to the sum of the scaled flex shrink factors of all
unfrozen items on the line. Set the item’s target main size to
its flex base size minus a fraction of the absolute value of the
remaining free space proportional to the ratio.

Simplifying, frozen flex items are those which can't or don't have to be flexed anymore. I will assume no min-width restrictions and non-zero flex shrink factors. This way all flex items are initially unfrozen and they all become frozen after only one iteration of the flex loop.

The inner flex base size depends on the value of box-sizing, defined by CSS2UI as

  • content-box: The specified width and height (and respective min/max properties) 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 (and respective min/max properties) 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. [...] Used values, as
    exposed for instance through getComputedStyle(), also refer to the
    border box.

Basically, that means that sizes (widths, flex bases) have an inner an an outer variant. The inner size includes only the content, the outer one also includes paddings and border widths. The length specified in the stylesheet will be used as the inner size in case of box-sizing: content-box, or as the outer one in case of box-sizing: border-box. The other can be calculated by adding or subtracting border and padding widths.

Neglecting lots of details, the algorithm would be something like

let sumScaledShrinkFactors = 0,
remainingFreeSpace = flexContainer.innerMainSize;
for (let item of flexItems) {
remainingFreeSpace -= item.outerFlexBasis;
item.scaledShrinkFactor = item.innerFlexBasis * item.flexShrinkFactor;
sumScaledShrinkFactors += item.scaledShrinkFactor;
}
for (let item of flexItems) {
let ratio = item.scaledShrinkFactor / sumScaledShrinkFactors;
item.innerWidth = item.innerFlexBasis + ratio * remainingFreeSpace;
}

With no paddings, it's as you explain

                                   (width)
innerW │ padd │ outerW
───────┼──────┼───────
300px * (1 + 2 / 1000px * -200px) = 180px │ 0px │ 180px
200px * (1 + 1 / 1000px * -200px) = 160px │ 0px │ 160px
100px * (1 + 2 / 1000px * -200px) = 60px │ 0px │ 60px
───────┼──────┼───────
400px │ 0px │ 400px

With 10px horizontal paddings, the available space is reduced by 3 * 2 * 10px = 60px, so now it's -260px.

                                   (width)
innerW │ padd │ outerW
───────┼──────┼───────
300px * (1 + 2 / 1000px * -260px) = 144px │ 20px │ 164px
200px * (1 + 1 / 1000px * -260px) = 148px │ 20px │ 168px
100px * (1 + 2 / 1000px * -260px) = 48px │ 20px │ 68px
───────┼──────┼───────
340px │ 60px │ 400px

When you use box-sizing: border-box, the specified flex bases are the outer ones, so the paddings are subtracted from them to calculate the inner ones, which are 280px, 180px, 80px. Then the sum of the scaled flex shrink factors becomes 2*280px + 180px + 2*80px = 900px. The available space is like in the case with no padding, because the outer flex bases are the same. Note the width retrieved by getComputedStyle will now be the outer one, so paddings are added back at the end.

                                                    (width)
innerW │ padd │ outerW
────────┼──────┼────────
280px * (1 + 2 / 900px * -200px) ≈ 155.6px │ 20px │ 175.6px
180px * (1 + 1 / 900px * -200px) = 140.0px │ 20px │ 160.0px
80px * (1 + 2 / 900px * -200px) ≈ 44.4px │ 20px │ 64.4px
────────┼──────┼────────
340.0px │ 60px │ 400.0px


Related Topics



Leave a reply



Submit