Flexbox full reverse

I'd like to make something like a news list (not really, but it makes sense when explained like this) of unknown number of items. I'd like to reverse it, so if I add one at the and, it will appear as the first one. I tried using flexbox with flex-wrap: wrap-reverse; flex-direction: row-reverse (with shorthand flex-flow).

#news {
  display: flex;
  flex-flow: row-reverse wrap-reverse;
  justify-content: space-around;
  width: 270px;
}

.report {
  height: 50px;
  width: 70px;
  background-color: yellow;
  margin: 5px;
}
<div id="news">
  <div class="report">Other report</div>
  <div class="report">Other report</div>
  <div class="report">Other report</div>
  <div class="report">Other report</div>
  <div class="report">Other report</div>
  <div class="report">2 mins old</div>
  <div class="report">The new one</div>
</div>
However, as you see above, the first (last in HTML) item is alone on the "first" row. Can I fix this or how else can I achieve this (ideally without JS)?

3 answers

  • answered 2018-01-11 19:56 Xavier

    Update

    Because this uses flex-wrap: wrap-reverse, the last element in the DOM order is still treated by flexbox as the last element to "fit in" to the flexbox schema. The wrap-reverse just means that elements are wrapped above previous ones instead of below. Basically, to have the first row filled with non "flex-growed" items with pure CSS is not possible. The below solution works because flex-grow allows the last element to take up the empty space.

    Another option would be to make sure you always have the proper number of elements to make sure that the last (rendered on top) line is always full, like so:

    #news {
      display: flex;
      flex-flow: row-reverse wrap-reverse;
      justify-content: space-around;
      width: 270px;
    }
    
    .report {
      height: 50px;
      width: 70px;
      background-color: yellow;
      margin: 5px;
      flex: auto;
    }
    <div id="news">
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">2 mins old</div>
      <div class="report">The new one</div>
    </div>

    Original Answer

    You have to specify a flex property on the children reports:

    #news {
      display: flex;
      flex-flow: row-reverse wrap-reverse;
      justify-content: space-around;
      width: 270px;
    }
    
    .report {
      height: 50px;
      width: 70px;
      background-color: yellow;
      margin: 5px;
      flex: auto;
    }
    <div id="news">
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">2 mins old</div>
      <div class="report">The new one</div>
    </div>

    Etc

  • answered 2018-01-11 20:28 LGSon

    With the given markup, w/o using script, one could make use of the order property.

    Combined with an unknown amount of items, still will need to be based on a max number of items, where I here started for up to 100.

    Stack snippet

    #news {
      display: flex;
      flex-flow: row wrap;
      justify-content: space-around;
      width: 270px;
    }
    
    .report {
      height: 50px;
      width: 70px;
      background-color: yellow;
      margin: 5px;
    }
    
    .report:nth-child(1) { order: 100; }
    .report:nth-child(2) { order:  99; }
    .report:nth-child(3) { order:  98; }
    .report:nth-child(4) { order:  97; }
    .report:nth-child(5) { order:  96; }
    .report:nth-child(6) { order:  95; }
    .report:nth-child(7) { order:  94; }
    .report:nth-child(8) { order:  93; }
    .report:nth-child(9) { order:  92; }
    /*  and so on...  */
    <div id="news">
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">Other report</div>
      <div class="report">2 mins old</div>
      <div class="report">The new one</div>
    </div>


    Another option could be, if these elements are rendered dynamically server/client side, to add the order property inline, as suggested in this answer of mine:

    With this you could start at e.g. order: 10000, save some CSS coding and count down from there.

  • answered 2018-01-11 22:05 Daniel Khoroshko

    The problem here how I see it lies in the fact that in your own example the last item is already centered on the last line. Is it supposed to be? Usually you want it to be aligned to the left side of the screen, right?

    If that's correct, the only non-js way to fix it I know is to add hidden zero-height placeholders. It is described somewhere here on stackoverflow.

    When you don't know how many placeholders to add, you can add any safe number, because they have 0 height they don't affect to visual representation.

    Now if we adapt this technique to the example it turns out to be like this.

    https://jsfiddle.net/4L3actqd/

    #news {
      display: flex;
      flex-flow: row wrap;
      justify-content: space-around;
      width: 270px;
    
      & > .hidden-f { display: none; }
      & > .hidden-e { display: block; }
    
      &.reversed {
        flex-flow: row-reverse wrap-reverse;    
    
        & > .hidden-f { display: block; }
        & > .hidden-e { display: none; }
      }
    }
    
    .report {
      height: 50px;
      width: 70px;
      background-color: yellow;
      margin: 5px;
    }
    
    .hidden-e, .hidden-f {
      width: 70px;
      height: 0px;
      margin: 5px;  
      opacity: 0.1;
    }
    

    enter image description here