How to Allow Flex-Items to Grow While Keeping the Same Size

How can I allow flex-items to grow while keeping the same size?

This is something that cannot be expressed via CSS Flexbox right now. Someone asked basically this exact question on the CSS Working Group Mailing List a few days ago, and the response was: "At the moment, no, this is not possible. This is a high-priority item for Flexbox 2, though.

You may be able to hack up something like you want using max-width, though -- that'll prevent flex items (particularly those on the last line) from growing indefinitely. Here's a forked version of your plunk with a nonzero flex-grow, and with max-width: 10em.

(I chose 10em arbitrarily; you could also use e.g. max-width: calc(100%/5), if you want to make sure each flex item takes up no more than 1/5 of the line, for example.)

How to make flexbox items the same size?

Set them so that their flex-basis is 0 (so all elements have the same starting point), and allow them to grow:

flex: 1 1 0px

Your IDE or linter might mention that the unit of measure 'px' is redundant. If you leave it out (like: flex: 1 1 0), IE will not render this correctly. So the px is required to support Internet Explorer, as mentioned in the comments by @fabb;

How to keep wrapped flex-items the same width as the elements on the previous row?

TL;DR

This is not something I'd call a solution per se, but it's a rather elegant workaround that only uses media queries, and more importantly no JavaScript!

Mixin (SCSS):

@mixin flex-wrap-fix($flex-basis, $max-viewport-width: 2000px) {
flex-grow: 1;
flex-basis: $flex-basis;
max-width: 100%;

$multiplier: 1;
$current-width: 0px;

@while $current-width < $max-viewport-width {
$current-width: $current-width + $flex-basis;
$multiplier: $multiplier + 1;

@media(min-width: $flex-basis * $multiplier) {
max-width: percentage(1/$multiplier);
}
}
}

Usage:

Apply the mixin to your flex item:

.flex-item {
@include flex-wrap-fix(100px)
}

Update:

The above mixin should do the trick, as long as you your flex container width matches your viewport size, as is the case in OP's example. Media queries won't help you otherwise, because they're always based on the viewport width. However, you could use the css-element-queries library and its element queries instead of browser media queries. Here's a mixin that you can apply to the flex container:

@mixin flex-container-wrap-items($flex-basis, $max-expected-width: 2000px) {
display: flex;
flex-wrap: wrap;

> * {
max-width: 100%;
flex-grow: 1;
flex-basis: $flex-basis;
}

$multiplier: 1;
$current-width: 0px;

@while $current-width < $max-expected-width {
$current-width: $current-width + $flex-basis;
$multiplier: $multiplier + 1;

&[min-width~="#{$flex-basis * $multiplier}"] > * {
max-width: percentage(1/$multiplier);
}
}
}

Explanation:

Let's say, as per the OP's example, we want each item to have a maximum width of 100px, so we know that for a browser width of 100px we can fit one item per row, and so on:

| Viewport Width | Max Item Count Per Row | Item Width (min-max) |
|----------------|------------------------|----------------------|
| <= 100 | 1 | 0px - 100px |
| <= 200 | 2 | 50px - 100px |
| <= 300 | 3 | 50px - 100px |
| <= 400 | 4 | 50px - 100px |
| <= 500 | 5 | 50px - 100px |
| <= 600 | 6 | 50px - 100px |

We can write media queries to create the following rules:

| Viewport Width | Max Item Count Per Row | Item Max Width | Calculation |
|------------------------------------------------------------------------|
| <= 100px | 1 | 100% | (100/1) |
| <= 200px | 2 | 50% | (100/2) |
| <= 300px | 3 | 33.33333% | (100/3) |
| <= 400px | 4 | 25% | (100/4) |
| <= 500px | 5 | 20% | (100/5) |
| <= 600px | 6 | 16.66666% | (100/6) |

Like this:

li {
flex: 1 0 0
max-width: 100%;
}

@media(min-width: 200px) {
li { max-width: 50%; }
}

@media(min-width: 300px) {
li { max-width: 33.33333%; }
}

@media(min-width: 400px) {
li { max-width: 25%; }
}

@media(min-width: 500px) {
li { max-width: 20%; }
}

@media(min-width: 600px) {
li { max-width: 16.66666%; }
}

Of course, that's repetitive, but most likely you're using some sort of preprocessor, which can take care of the repetitiveness for you. That's precisely what the mixin in the above TL;DR section does.

All we have to do now is to specify 100px as our flex-basis, and optionally the maximum browser window width (defaults to 2000px) to create the media queries for:

@include flex-wrap-fix(100px)

Example

Finally, a forked version of the original CodePen example with the desired output, using the above mixin:

http://codepen.io/anon/pen/aNVzoJ

demo of the solution

CSS Keep all flexbox children elements the same size

You have 2 ways of doing this:

flex-grow: 0 (fiddle)

If you set flex-grow to 0 you are telling the items to keep their width and expand the empty space. Using flex-grow: 1 the items will expand the width to fit the space.

invisible items (fiddle)

Add some invisible items after the normal items with the same flex properties. You will get a left aligned look.

In this solution there will be the same number of items in all visible rows so make sure to add enough invisible items to work properly on larger screens.

How can a flex item keep the same dimensions when it is forced to a new row?

Maybe an invisible flex item in the last slot will work for you.

Add this code to your CSS:

.container::after {
content: "";
flex-grow: 1;
max-width: 450px;
min-width: 350px;
}

DEMO


UPDATE

I forgot to add one rule, which caused a slight overflow to the right of the pseudo-element.

.container::after {
content: "";
flex-grow: 1;
max-width: 450px;
min-width: 350px;
margin-right: 10px; /* to match the other flex items */
}

REVISED DEMO


More details in this related post: Properly sizing and aligning the flex item(s) on the last row

Can I shrink flexbox items into same size when flex items are bigger than the flex container?

I would use display: grid instead of display: flex to achieve this. Here is an example:

.flex-container {  font-size: 12px;  display: grid;  grid-template-columns: 1fr;  grid-template-rows: repeat(2, minmax(minmax(0, auto), 50%));  width: 200px;  height: 500px;  border: 1px solid #dadfe3;}
.flex-item { min-height: 0; overflow: auto; margin: 0 20px;}
.flex-item div { margin: 4px 0; background-color: #f0f9ff; color: #2f88ff;}
<body>  <div class="flex-container">    <div class="flex-item">      <input type="text" placeholder="filter 1">      <div>item content 1</div>      <div>item content 2</div>      <div>item content 3</div>      <div>item content 4</div>      <div>item content 5</div>      <div>item content 6</div>    </div>    <div class="flex-item">      <input type="text" placeholder="filter 2">      <div>item 1</div>      <div>item 2</div>      <div>item 3</div>      <div>item 4</div>      <div>item 5</div>      <div>item 6</div>      <div>item 7</div>      <div>item 8</div>      <div>item 9</div>      <div>item 10</div>      <div>item 11</div>      <div>item 12</div>      <div>item 13</div>      <div>item 14</div>      <div>item 15</div>    </div>  </div></body>

Prevent a flex items height from expanding to match other flex items

This is an old solution.
My answer is superseded by this new answer by Aaron using align-self. That is a better solution that does not rely on a CSS quirk.

As long as the flex container has no height itself, you can set height: 0% on the first flex item. Because it has no height to inherit from its parent, any percentage height will cause it to collapse. It will then grow with its contents.

Example

In this example I have removed the -webkit prefix. It's only really required for Safari and the prefix can be added above the non-prefixed version. I also removed flex-direction: row as it is the default value.

.container {
display: flex;
}
.flexbox-1 {
flex: 1;
height: 0%;
border: solid 3px red;
}
.flexbox-2 {
flex: 2;
border: solid 3px blue;
height: 200px;
margin-left: 10px;
}
<div class="container">
<div class="flexbox-1">.flexbox-1</div>
<div class="flexbox-2">.flexbox-2</div>
</div>

Flexbox not giving equal width to elements

There is an important bit that is not mentioned in the article to which you linked and that is flex-basis. By default flex-basis is auto.

From the spec:

If the specified flex-basis is auto, the used flex basis is the value of the flex item’s main size property. (This can itself be the keyword auto, which sizes the flex item based on its contents.)

Each flex item has a flex-basis which is sort of like its initial size. Then from there, any remaining free space is distributed proportionally (based on flex-grow) among the items. With auto, that basis is the contents size (or defined size with width, etc.). As a result, items with bigger text within are being given more space overall in your example.

If you want your elements to be completely even, you can set flex-basis: 0. This will set the flex basis to 0 and then any remaining space (which will be all space since all basises are 0) will be proportionally distributed based on flex-grow.

li {
flex-grow: 1;
flex-basis: 0;
/* ... */
}

This diagram from the spec does a pretty good job of illustrating the point.

And here is a working example with your fiddle.

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>

How to prevent content from re-sizing a flex item?

When you tell a flex item to flex: 3 (Item 6) or flex: 1 (Items 5 & 7), here's how that shorthand property breaks down:

flex: ?  =  <flex-grow>, <flex-shrink>, <flex-basis>

flex: 3 = flex-grow: 3, flex-shrink: 1, flex-basis: 0

flex: 1 = flex-grow: 1, flex-shrink: 1, flex-basis: 0

In both cases, the flex-shrink factor is 1, meaning the flex item is allowed to shrink proportionally.

To prevent the flex items from shrinking, which may be causing the misalignment, I would change the flex-shrink to 0.

Instead of flex: 3 and flex: 1 try:

flex: 3 0 0;
flex: 1 0 0;

More details in the flexbox spec: 7.1.1. Basic Values of flex


But you may have another problem. You wrote:

Note that items 5 and 7 are setup the same way in css, with flex values of 1. I.e. item 6 is 3 times the height of items 5 and 7. As such, when item 6 resizes, the 3:1 ratio is maintained.

This is not how the flex-grow property works.

By applying flex-grow: 3 to a flex item you are telling it to consume three times more available space than flex items with flex-grow: 1. This does not necessarily mean Item 6 will be three times the size of Items 5 and 7.

Learn more about how flex-grow works here: flex-grow not sizing flex items as expected

As an alternative sizing method, you may want to use the flex-basis property. Try flex: 0 0 60% on Item 6, and flex: 0 0 20% on Items 5 and 7.



Related Topics



Leave a reply



Submit