1

We have an odd number of items inside of a flex: flex-wrap container and at a certain resolution when they wrap the last of the items is over to the left but I want it to (continue to) be to the right.

I googled and found a resource discussing a similar issue at: https://haizdesign.com/css/flexbox-align-last-item-grid-left/

The ::after pseudo-element they applied to achieve this is:

.speakers::after {
  content: '';
  flex: auto;
}

So I tried to apply this knowledge, but instead use the ::before pseudo-element to try to move my last item over to the right, but I could not get it to work. Below is some HTML and CSS code followed by a link to the CodePen:

#container {
  display: flex;
  flex-wrap: wrap;
}

.el-width {
  min-width: 40%;
}

.last-el::before {
  content: '';
  flex: auto;
}
<div id="container">
  <div class="el-width">Foo</div>
  <div class="el-width">Bar</div>
  <!-- uncomment to move Baz under Bar 
      <div class="el-width last-el">&nbsp;</div>
      -->
  <div class="el-width last-el">Baz</div>
</div>

https://codepen.io/dexygen/pen/ExaNZYv

As you can see in the HTML if you interpose an actual (empty) div, Baz gets moved under Bar. I've also been able to introduce an actual element in my application and it does likewise. However I'd like to know how or if it can be achieved using ::before

Dexygen
  • 12,287
  • 13
  • 80
  • 147
  • It's a little unclear which outcome you want. Do you want uneven amounts of flex items to be like the first image you posted or the second image? – TylerH Dec 16 '19 at 19:45
  • @TylerH I don't want them like the images per se but it's an example of how ::after got used to solve a similar problem. Instead please consult the codepen. – Dexygen Dec 16 '19 at 19:59
  • We are *not* using CSS grid anywhere else in our code-base, and introducing it here is not feasible due to the necessity for testing and meeting deadlines etc. So all answers suggesting CSS grid are useless, sorry. – Dexygen Dec 16 '19 at 20:44
  • @GeorgeJempty I didn't say anything about grids. – TylerH Dec 16 '19 at 22:11
  • @TylerH 1) I never said YOU said anything about grids and 2) FFFS why do you insist on making the title less specific? In particular I resent it now saying "not working", that statement should be anathema to developers. I said *I* could not get it to work, that's on me, "not working" on the other hand is vague BS that I regularly call out developers for using. Please if I revert it leave it alone. – Dexygen Dec 16 '19 at 22:30
  • Well, you directly pinged me and mentioned grids as if I had suggested them. Why emphasize to me that grids won't work if I haven't suggested them as a solution? Regarding the title: questions are not just for you, but for all future readers as well. I rolled back to Michael's title not only because I think it's *more accurate* in describing your problem, but also because it's more likely to show up in search queries for people googling for a similar problem. Broader is typically better when there is a canonical solution. – TylerH Dec 16 '19 at 22:33
  • @TylerH I don't think I directly pinged you about grids, see above -- about the image yes, but not grids – Dexygen Dec 16 '19 at 22:33
  • @GeorgeJempty, I revised the title to make it more search friendly. That was it. But it's your question, so do as you like. – Michael Benjamin Dec 17 '19 at 02:27
  • 1
    @Michael_B There's probably some middle ground. I think flex-wrap is important to keep. I also would never say "doesn't work" in general, that's why I said I cannot get it to work -- the former sounds like I'm insinuating the issue is with flex, and not me ;) I do see the benefit of maybe generalizing it to pseudo selectors though. Let me re-think it, thanks I'd never have thought to do so if not for the original edit and this discussion which I know we're not supposed to be having in comments ;) – Dexygen Dec 17 '19 at 02:38

3 Answers3

2

Pseudo elements on a flex container are treated as flex items (source).

So the first problem is that the pseudo element in your code is applied to the flex item (.last-el). It needs to be applied to the flex container (#container).

Then, the default order matters. The ::before() pseudo is the first flex item, and an ::after() pseudo would be the last.

So, if you're going to use a pseudo element as a flex item, to bump over an inner item, you need to use the order property to re-arrange the visual order. (Incidentally, this obviates the need to choose between ::before() and ::after().)

#container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.el-width {
  flex: 0 0 33.33%;
}

#container::before {
  order: 1;
  flex: 0 0 33.33%;
  content: '';
}

.el-width:nth-child(-n+3) {
  order: 0;
}

.el-width:nth-last-child(-n+2) {
  order: 2;
}
<div id="container">
  <div class="el-width">Item 1</div>
  <div class="el-width">Item 2</div>
  <div class="el-width">Item 3</div>
  <div class="el-width">Item 4</div>
  <div class="el-width">Item 5</div>
</div>

The pseudo element method you're describing in your question is explained here:

A description of the problem, along with potential solutions, can be found here:

And a clean and efficient solution for handling this problem, using CSS Grid, is here:

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • I have yet to try this but when I do and I get it to work I will up-vote and probably accept. My biggest concern from just perusing the code are the hard-coded orders, but again I haven't delved in to ascertain if this is going to be an issue. – Dexygen Dec 18 '19 at 14:53
2

This would be a lot easier with CSS Grid. I leave this here as an alternative answer, in case it helps others.

#container {
  display: grid;
  grid-template-columns: 40% 40%;
}

.el-width {
  border: 1px solid;
}

.last-el {
  grid-column-start: 2;
}
<div id="container">
  <div class="el-width">Foo</div>
  <div class="el-width">Bar</div>
  <!-- uncomment to move Baz under Bar 
      <div class="el-width last-el">&nbsp;</div>
      -->
  <div class="el-width last-el">Baz</div>
</div>
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
0

Fixing this is simple and to do it, we use a pseudo element. Going back to our container, (in this case, my container has a class of .speakers)

So they applied the ::after to the container to create a last element, not they apply to the last element last-el. And they did that because they used justify-content: space-between to justify their items leads their last element to unexpected position, which seems to not be of your case. If you want to layout in 2 dimensions, CSS Grid is the best. If you want better browser compatible, then you might already have the answer yourself in the codepen you gave. But I think what you really want might just be the answer that you basically can't solve this by just adding styles to .el-width::before?

Loi Nguyen Huynh
  • 8,492
  • 2
  • 29
  • 52