I have a fixed-width container into which several variable-width elements must appear in a row, which can spill over into additional rows as necessary.

However, the beginning of each element must be aligned with the one on top of it, so in ASCII art it would look like so (say, padding of 1):

/----------------------------------\ | | | # One # Two # Three # Four | | # Five # Six | | | \----------------------------------/

In other words:

The first element of every row must be left-aligned The last element of every row (except for the final row) must be right-aligned Every element must be left-aligned to the element above it

I'm trying to use flexbox for this without success.

This is the best I've come so far, using flex-wrap: wrap for the container and flex-grow: 1 for the elements.

Problem is that the last row fills out to the edge.

justify-content: flex-start; // this does nothing

If I take away flow-grow: 1 then the elements aren't distributed equally. I also tried fiddling around with last-of-type on the elements but it's also not enough.

Is this even possible with flexbox, or am I going about it the wrong way?

TylerH's user avatar

TylerH

21.3k86 gold badges84 silver badges122 bronze badges

asked Dec 17, 2015 at 9:34

Guy Benron's user avatar

2

After trying the suggestions here (thanks!) and searching the web long and wide, I've reached the conclusion that this is simply not possible with flexbox. And by that I mean that others have reached this conclusion while I stubbornly tried to make it work anyway, until finally giving up and accepting the wisdom of wiser people.

There are a couple of links I came across that might explain it better, or different aspects of the requirements, so I'm posting them here for... posterity.

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

http://fourkitchens.com/blog/article/responsive-multi-column-lists-flexbox

Luke's user avatar

Luke

5,7582 gold badges36 silver badges39 bronze badges

answered Dec 17, 2015 at 13:16

Guy Benron's user avatar

1 Comment

The posted solution by Jonas Lundman seems to do the trick.

2021-11-18T15:15:58.067Z+00:00

There is no easy way to do this with flexbox. But if you are willing to sacrifice IE then you can do it with css grid, add this to the container:

display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));

And if you want to have some space between the elements then add

grid-gap: 10px;

answered Aug 29, 2018 at 11:48

Kristjan Liiva's user avatar

I know I am kind of late, but I have been looking for solution for this in past hour or so, and I think I sort of figured it out. Put empty div on the end of your container, and set flex-grow: 1 to it, and nothing else. Also set justify-content: space-between on container, and don't use flex-grow on other items. This will always make last line align left because this div will stretch through all remaining space.

However the problem of this is that it ALWAYS makes last line align left - even if it is the only line, which makes this solution unusable for me, but it might be usable for someone who can expect more than one line of items.

answered Feb 7, 2017 at 10:28

Mintenker's user avatar

1 Comment

This is a good idea but unfortunately the spacing with this method is not equel between lines :'(

2020-04-21T13:15:54.757Z+00:00

If the width of your items is fixed, you can add several empty divs to the end of your list of items:

<div class="item">meaningful content</div> <div class="item">meaningful content</div> <div class="item">meaningful content</div> <div class="empty-div"></div> <div class="empty-div"></div> <div class="empty-div"></div>

and then:

.item, .empty-div{ width: 150px; } // make them the same width

Works perfectly well.

answered Mar 15, 2017 at 6:03

Evgenia Karunus's user avatar

1 Comment

Doesn't quite answer the question, but it answers mine :)

2021-08-18T07:59:54.417Z+00:00

I was able to achieve the desired result with a combination of positive and negative margins.

If each element in the container defines a margin to create a space between them:

.container .element { flex: 1; margin: 0px 5px; }

recover the pixels from the edges of each row in the container with a negative margin of the same amount:

.container { display: flex; flex-direction: row; flex-wrap: wrap; margin: 0px -5px; }

This should result in 10px between each element in the row with the first and last of each row at the edge of the container.

answered Dec 6, 2018 at 22:07

MikeyP's user avatar

2 Comments

This is a good solution for making the space between work when wrapping. However, this answer ultimately does not answer the question. The poster is looking for the last row to be left aligned with boxes of the same size as other rows.

2019-11-21T21:44:30.047Z+00:00

2021-09-07T15:40:07.063Z+00:00

One solution that will work in many cases is simply applying padding to the items. Then you can use flex-start, and get spacing in between the cards.

For example

.container { display: flex; justify-content: center; } .parent { width: 420px; display: flex; flex-wrap: wrap; flex-direction: row; justify-content: flex-start; } .child { flex: 0 30%; min-width: 100px; padding-left: 10px; margin-bottom: 10px; } .child-content { background-color: yellow; border: 1px solid black; height: 100px; } <div class="container"> <div class="parent"> <div class="child"> <div class="child-content"> Box </div> </div> <div class="child"> <div class="child-content"> Box </div> </div> <div class="child"> <div class="child-content"> Box </div> </div> <div class="child"> <div class="child-content"> Box </div> </div> <div class="child"> <div class="child-content"> Box </div> </div> </div> </div>

answered Nov 29, 2020 at 6:12

CodyBugstein's user avatar

CSS Flex 4 columns always starting at left. I cant see wy this is impossible? If the columns should be equal in with, this working for us using calc() and relative units:

/* parent */ .ua-flex { display: flex; flex-wrap: wrap; justify-content: start; } /* children */ .ua-flex > * { flex: 0 0 calc(25% - 1em); margin: 1em 0 0 0; } .ua-flex > :not(:nth-child(4n+4)) { margin-right: 1.333333em; }

Im I missing something here? Its all abouth math, the subtracted calc() in this case 1em, gives 3 space of gap 1.333em with margin-right on 3 of 4 columns, and 0.5em subtracted calc() should give 0.666em gap with margin-right on 3 of 4 columns.

Hope this can be useful...

answered Nov 18, 2021 at 15:04

Jonas Lundman's user avatar

3 Comments

Nice! works, i only needed to change the syntax of .ua-flex > :not(:nth-child(4n+4) to .ua-flex:not(:nth-child(4n+4) to make that rule work too. Thanks!

2022-01-12T00:27:40.94Z+00:00

Agreed with @ViktorBorítás this works with that minor change.

2022-03-31T00:57:06.313Z+00:00

What if we don't want the items to be equal in width; they have variable width based on contents? Is this still possible?

2024-04-18T03:08:10.737Z+00:00

I just stumbled across the same problem and came up with another solution. I can't decide whether it feels kind of dirty or elegant, but decide for yourself.

Add as many empty divs as your maximum number of items per row to the container, assign them the same class as row items but remove any margin or padding from them (basically anything which gives them a height). That'll result in the row behaving as expected because after the last row item, there'll always be enough invisible "spacers" to pad the row. Those being wrapped to the next row have no height, so they shouldn't affect the rest of your page.

Example here: https://jsfiddle.net/amknwjmj/

.products { display: flex; flex-wrap: wrap; justify-content: space-between; } .product { /* set top and bottom margin only, as left and right will be handled by space-between */ margin: 0.25rem 0; /* account your desired margin two times and substract it from the base width of the row item */ flex-basis: calc(25% - (0.25rem * 2)); } .product.spacer { /* remove all properties increasing the element height from the spacers to avoid them being visible */ margin: 0; padding: 0; height: initial; } /* start demo styles (purely eye-candy not required for this to work) */ * { box-sizing: border-box; font-family: sans-serif; } .products { padding: .25rem .5rem; background: powderblue; } .product { height: 75px; line-height: 75px; padding: .25rem; text-align: center; background: steelblue; color: #fff; } .product.spacer { background: none; /* of course, spacers should NOT have a border but they are helpful to illstrate what's going on. */ border: 1px dashed gray; } /* end demo styles */ <div class="products"> <article id="product-1" class="product">P1</article> <article id="product-2" class="product">P2</article> <article id="product-3" class="product">P3</article> <article id="product-4" class="product">P4</article> <article id="product-5" class="product">P5</article> <div class="product spacer"></div> <div class="product spacer"></div> <div class="product spacer"></div> <div class="product spacer"></div> </div>

answered Mar 22, 2017 at 17:19

Moritz Mazetti's user avatar

so, you have a container and some stuff inside?

<div class="container"> <span>msg1</span> <span>msg1</span> <span>msg1</span> <span>msg1</span> <span>msg1</span> </div>

This is how it works, when you declare display: flex for your container all of its direct children will become flex too, with some default config (align-items:strech).

I guess you already got that, so, maybe you can try using justify-content: space-between which will align your items in the row leaving them equally spaced plus flex-basis: 25% (to certify that there will be always 4 items in a row, change de % as you wish) that is supposed to work for all your lines except the last one. For the last one you can use a css selector (like last-child) and set its property to flex-grow: 0 / flex-shrink:0 (solving one of your problems, if you used flex: 1 1 25% instead of flex-basis) and also align-self: flex-start or whatever you like

answered Dec 17, 2015 at 10:39

cbcaio's user avatar

4 Comments

There are a couple of problems.. I'd like the first rows to behave as justify-content: space-between and the last row to behave like justify-content: flex-start. Also, the last element won't shrink or grow per your suggestion but it might not be alone in the row, and won't prevent the others from shrinking or growing (maybe related to the first point).

2015-12-17T10:58:46.503Z+00:00

I don't know how many items, nor their width - it's dynamically loaded.

2015-12-17T11:07:27.113Z+00:00

You will need a way to figure out which elements are inside your last row. You can try nested flexbox and aplying some logic to whatever you are using to dinamically load your stuff. Or you can use regular bootstrap columns (which will work just as fine) and flexbox only to align inside content, sorry I couldn't help more.

2015-12-17T11:12:01.13Z+00:00

Columns + inner flex might be the way to go, I'll give it a shot. Thanks :)

2015-12-17T11:17:03.773Z+00:00

You could try it with a fixed-with-pseudo-element:

.container { display:flex; justify-content:space-between; } .container:after { content: ''; flex-grow: 0; width: 25%; } .container .element { width: 25%; }

answered Dec 8, 2017 at 17:57

tFranz's user avatar

You can specify margin-right on every item except the last one in the row of flex-wrap by doing the following:

.item:not(:nth-child(4n)) { margin-right: 20px; }

answered Aug 25, 2021 at 20:51

Fiddle Freak's user avatar

I was facing the same issue and in fact it's really simple. No need to put some SCSS and or jQuery.

You just need to specify a maximum number of "square", and make a modulo to know if that match with your maxNumber. If its not, you just have to define a quick function that increment your number until that modulo return 0.

When you have your number you just have to loop your html. For me I was coding on ReactNative:

const RenderInvisibleSquare = (nb) => { let tab = []; let i = 1; for (; (nb + i) % 3 !== 0; i++) {} for (let n = 0; n < i; n++) { tab.push(<View style={{ width: 120, height: 120, backgroundColor: 'red' }}/>); } return tab; }; <ScrollView> { data.map(item => ( <View> <View style={{ marginVertical: 5 }}> <Text style={{ textAlign: 'center', color: '#7E8BF5' }}>{item.title}</Text> </View> <View style={{ flex: 1, flexWrap: 'wrap', alignItems: 'center', justifyContent: 'space-around', flexDirection: 'row' }}> { item.data.map(elem => ( <View style={{ width: 120, height: 120, marginBottom: 10, backgroundColor: 'red' }} /> )) } { item.data.length % 3 !== 0 && RenderInvisibleSquare(item.data.length)} </View> </View> )) } </ScrollView>

If you dont want content (backgroundColor: 'red') in my case you juste have to make it 'transparent'.

marc_s's user avatar

marc_s

761k186 gold badges1.4k silver badges1.5k bronze badges

answered Feb 21, 2019 at 16:06

Jean-Charles Lateltin's user avatar

1 Comment

"no need to use SCSS or [jQuery]" but you need to use React? Not really useful for a CSS question...

2019-02-21T16:20:32.937Z+00:00

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.