How Does Flex-Shrink Factor in Padding and Border-Box

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

In what circumstances is flex-shrink applied to flex elements and how does it work?

In order to see flex-shrink in action, you need to be able to make its container smaller.

HTML:

<div class="container">
<div class="child one">
Child One
</div>

<div class="child two">
Child Two
</div>
</div>

CSS:

div {
border: 1px solid;
}

.container {
display: flex;
}

.child.one {
flex: 1 1 10em;
color: green;
}

.child.two {
flex: 2 2 10em;
color: purple;
}
  • http://jsfiddle.net/GyXxT/ (unprefixed -- Opera or Firefox nightly build)
  • http://jsfiddle.net/GyXxT/1/ (webkit)

In this example, both child elements ideally want to be 10em wide. If the parent element is greater than 20em wide, the 2nd child will take twice as much leftover space as the 1st child, making it appear bigger. If the parent element is less than 20em wide, then the 2nd child will have twice as much shaved off of it as the 1st child, making it look smaller.

Current flexbox support: Opera (unprefixed), Chrome (prefixed), IE10 (prefixed, but uses slightly different property names/values). Firefox currently uses the old spec from 2009 (prefixed), but the new spec is supposed to be available in experimental builds right now (unprefixed).

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.

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>

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>


Related Topics



Leave a reply



Submit