Reputation: 1687
I have a <ul>
that is a flex-box and a bunch of <li>
s in it which are the flex-items.
I am trying to get the <li>
to have a flexible width, that stays between two sizes using min-width and max-width. It works great as long as they are on the same line. However, when they wrap, the <li>
s on the new line use as much space as they can. Which means they are small on the first line, and big on the second line when there are just a few of them.
Is there a way to tell flexbox to keep the items the width after wrapping, while keeping the flexible width?
wrapping demo:
My code looks like this:
<ul>
<li>
<figure>
<img src="http://placekitten.com/g/250/250">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/100/100">
</figure>
</li>
<!-- ... -->
</ul>
And the CSS:
ul {
display: flex;
flex-wrap: wrap;
}
li {
min-width: 40px;
max-width: 100px;
flex: 1 0 0;
}
Here's a live example on codepen with some extra comments, resize your window to see it wrap.
Upvotes: 96
Views: 91368
Reputation: 569
I have been experimenting with your CodePen and I set
flex: 0 1 10%
Of course you can set the flex-basis whatever you want, I just wanted to prove the point, that if you set flex basis and allow it to shrink, it will behave much nicer in this case.
I think this is what you needed. Here's the preview: http://codepen.io/anon/pen/bwqNEZ
Upvotes: 12
Reputation: 447
This is a limitation of flexbox. The best thing you can do is set a max-width which the wrapping (growing) item will respect. You need CSS Grid for this.
Upvotes: 1
Reputation: 3466
It's not the answer to what you want, but it nice to use:
display: grid;
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
https://codepen.io/omergal/pen/povzJrz
ul {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
}
li {
min-width: 40px;
max-width: 100px;
}
ul,
li {
margin: 0;
padding: 0;
list-style: none;
}
ul {
background-color: tomato;
}
li {
margin: 0.5em;
background-color: darkgreen;
}
img {
width: 100%;
opacity: 0.5;
}
figure,
img {
margin: 0;
padding: 0;
}
<ul>
<li>
<figure>
<img src="http://placekitten.com/g/250/250">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/101/101">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/201/201">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/150/150">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/80/80">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/111/111">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/40/40">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/110/110">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/75/75">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/89/89">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/150/150">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/32/32">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/61/61">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/320/320">
</figure>
</li>
</ul>
Upvotes: 45
Reputation: 475
if the layout is fixed, you could simply add hidden items and toggle their visibility using css breakpoints for the pixels where wrapping happens.
for example, in the following scenario .columned-list has 3 .columned-list-item elements, and for the pixels within which the 3rd .columned-list-item element wraps to the next row, I just make the 4th .columned-list-item element .hidden-item - which is normally invisible - visible :
.columned-list {
display: flex;
flex-wrap: wrap;
margin-left: 35px;
justify-content: flex-start;
.hidden-item {
display: none !important;
@media only screen and (max-width: 1375px) {
flex: 1 0 auto !important;
min-width: 410px !important;
width: 30% !important;
margin: 0 37px 37px 0 !important;
max-width: 100% !important;
display: flex !important;
background-color: #fff !important;
}
@media only screen and (max-width: 928px) {
display: none !important;
}
}
.columned-list-item {
flex: 1 0 auto;
min-width: 410px;
min-height: 1px !important; // <3 IE.
width: 30%;
margin: 0 37px 37px 0;
max-width: 100%;
display: flex;
flex-direction: column;
align-items: center;
color: #676562;
background-color: #f2f3f5;
a:nth-of-type(1) {
position: relative;
min-height: 84px;
width: 100%;
img {
width: 100%;
height: auto;
}
div {
min-height: 88px;
max-width: 203px;
padding: 0 15px;
top: 50%;
transform: translateY(-50%);
position: absolute;
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
opacity: 0.6;
span {
height: 55px;
font-family: 'Trade Gothic Next LT W04 Rg', sans-serif;
font-size: 28px;
font-weight: bold;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: 0.7px;
text-align: left;
color: #676562;
}
}
}
a:nth-of-type(2) {
padding: 10px 35px;
display: flex;
flex-direction: column;
min-height: 161px;
max-width: 390px;
strong {
font-family: 'Trade Gothic Next LT W04 Bold', sans-serif;
font-weight: bold;
text-align: center;
}
span {
font-family: 'Trade Gothic Next LT W04 Light', sans-serif;
font-size: 15px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 1.4;
letter-spacing: normal;
text-align: center;
color: #676562;
}
}
.buttons-block {
display: flex;
justify-content: space-between;
align-items: center;
width: 298px;
max-width: 100%;
margin: 10px 0 23px 0;
.button-a {
width: 114px;
height: 22px;
min-height: 1px;
box-shadow: 0px 1px 0 0 rgba(0, 0, 0, 0.16);
background-color: #f28d4e;
display: flex;
justify-content: center;
align-items: center;
span {
font-family: 'Trade Gothic Next LT W04 Rg', sans-serif;
font-size: 15px;
font-weight: bold;
font-stretch: normal;
font-style: normal;
line-height: 1.4;
letter-spacing: normal;
text-align: center;
color: #ffffff;
}
}
.button-b {
margin-bottom: 0;
padding: 0;
}
}
@media only screen and (max-width: 760px) {
margin: 0 0 20px 0;
}
@media only screen and (max-width: 410px) {
min-width: auto;
width: 100%;
}
}
@media only screen and (max-width: 760px) {
display: flex !important;
flex-direction: column;
align-items: center;
margin-left: 0;
padding-top: 10px;
}
}
Upvotes: 0
Reputation: 1165
I came here looking to solve the same problem for a flex box I have. I've since realised that this is now much easier with display: grid
as detailed here and here.
Upvotes: 1
Reputation: 11
For Stylus you can use this mixin: https://codepen.io/anon/pen/Epjwjq
flex-wrap-fix($basis, $max) {
flex-grow: 1
flex-basis: $basis
max-width: 100%
$multiplier = ceil($max / $basis)
for i in (1..$multiplier) {
@media(min-width: ($basis * i)) {
max-width: percentage(1/i)
}
}
}
Upvotes: 1
Reputation: 7379
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 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);
}
}
}
Apply the mixin to your flex item:
.flex-item {
@include flex-wrap-fix(100px)
}
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);
}
}
}
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)
Finally, a forked version of the original CodePen example with the desired output, using the above mixin:
http://codepen.io/anon/pen/aNVzoJ
Upvotes: 73
Reputation: 629
Its not working very well, but you can achieve something in this direction with columns.
column-gap: 10px;
column-width: 150px;
https://codepen.io/hobbeshunter/pen/OgByNX
Upvotes: 0
Reputation: 1
I had the same issue, so I used a very tiny bit of Jquery to control the width of the boxes and make them all the same width even once they wrap.
My demo is on CodePen Flex wrap, with equal width on new row
function resize(){
var colWidth;
var screen = $(window).width();
if(screen < 576){
colWidth = Math.round(screen / 2) - 31;
$(".item").width(colWidth);
}
if(screen > 575 && screen < 768){
colWidth = Math.round(screen / 3) - 31;
$(".item").width(colWidth);
}
else if (screen > 767 && screen < 992){
colWidth = Math.round(screen / 4) -31;
$(".item").width(colWidth);
}
else if (screen > 991 ){
colWidth = Math.round(screen / 6) -31;
$(".item").width(colWidth);
}
}
window.onresize = function() {
resize();
}
resize();
html,
body{
padding:0;
margin:0;
}
.flex-container {
padding: 0;
margin: 0;
list-style: none;
-ms-box-orient: horizontal;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -moz-flex;
display: -webkit-flex;
display: flex;
justify-content: left;
background-color:#ddd;
}
.wrap {
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
}
.item {
background: tomato;
height: 100px;
margin:0 15px;
line-height: 100px;
color: white;
font-weight: bold;
font-size: 2em;
text-align: center;
margin-top:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="flex-container wrap">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
<li class="item">4</li>
<li class="item">5</li>
<li class="item">6</li>
<li class="item">7</li>
<li class="item">8</li>
<li class="item">9</li>
</ul>
Upvotes: 0
Reputation: 1636
My solution is not ideal, as it leans on JQuery: http://codepen.io/BigWillie/pen/WwyEXX
CSS
ul {
display: flex;
flex-wrap: wrap;
}
li {
min-width: 40px;
max-width: 100px;
flex: 1;
}
JavaScript
var eqRowFlexItem = function(elem) {
var w;
var $elem = $(elem);
// Clear out max-width on elements
$elem.css('max-width', '');
w = $(elem).eq(0).width();
$(elem).css('max-width', w);
}
eqRowFlexItem('li');
$(window).resize(function() {
eqRowFlexItem('li');
});
I'm not entirely sure if it answers the problem. I found this post, as the title matched the problem I was having. I was after an effect whereby I would have a greater number of elements per row on larger screens - and fewer elements per row on smaller screens. In both cases, these elements also needed to scale to fit... across devices.
However, those stray elements on the last row would always expand to fill the remaining space.
The JQuery gets the width of the first element - and applies it as a max-width to every element. The OP wanted the blocks to fall within a min-width / max-width rage. Setting the max-width in CSS seems to work. We clear the max-width off the element, so it defaults to the max-width in the CSS... then gets the actual width.
Upvotes: 0
Reputation: 43870
I think I got it...but it's hacky. The result is the 14 images each scaling from 100px wide down to 40px, even on multiple rows.
<li>
s and added them to the flex <ul>
. Next, the second set was rendered invisible:
li:nth-of-type(n + 15) {
visibility: hidden;
}
In order to maintain uniform flexibility the following changes to each <li>
:
li {
min-width: 40px;
max-width: 10%;
flex: 1 0 100px;
}
Please review the CodePen
and/or the Snippet in Full Page mode.
ul {
display: flex;
flex-wrap: wrap;
max-height: 258px;
}
li {
min-width: 40px;
max-width: 10%;
flex: 1 0 100px;
}
ul,
li {
margin: 0;
padding: 0;
list-style: none;
}
ul {
background-color: tomato;
}
li {
margin: 0.5em;
background-color: darkgreen;
}
li img {
width: 100%;
opacity: 0.5;
}
li figure,
li img {
margin: 0;
padding: 0;
}
li:nth-of-type(n + 15) {
visibility: hidden;
}
<ul>
<li>
<figure>
<img src="http://placekitten.com/g/250/250">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/101/101">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/201/201">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/150/150">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/80/80">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/111/111">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/40/40">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/110/110">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/75/75">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/89/89">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/150/150">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/32/32">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/61/61">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/320/320">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/250/250">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/101/101">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/201/201">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/150/150">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/80/80">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/111/111">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/40/40">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/110/110">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/75/75">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/89/89">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/150/150">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/32/32">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/61/61">
</figure>
</li>
<li>
<figure>
<img src="http://placekitten.com/g/320/320">
</figure>
</li>
</ul>
Upvotes: 5
Reputation: 76
Setting the min-width
and max-width
as percentages has allowed all images to stay the same width when they break onto the next line. It is not ideal, but closer to what you are after?
li {
min-width: 15%;
max-width: 15%;
}
Upvotes: 4
Reputation: 1242
This isn't perfect but worth try. http://codepen.io/anon/pen/MKmXpV
The changes are
li
width: 7.1428%
flex: 0 0 auto
figure
margin: 0 auto
background-color: darkgreen
min-width: 40px
max-width: 100px
Upvotes: 0