<div id="fun">
<items-per-row :cycle="2">
   <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=">
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=">
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=">
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=">
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=">
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=">
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=">
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=">
  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=">
</items-per-row>
</div>
// Rotating 2 styles!
// It kinda works out as long as ther is an odd number of items per row.
// But when there's not it just makes gross stripes. And we want to play chess.


.grid {
  margin-top: 20px;
  outline: 1px solid #ddd;
  width: 600px;
  display: grid;
  grid-template-columns: repeat(3, auto);
  grid-gap: 10px;
  
  // this is the magic. 👇
  grid-auto-flow: dense;
  // 'Dense' is a little bit like float or absolute except it leaves it IN the order. Beautiful! (without it, when you reposition a grid item, it repositions every following item as well.)
}

// per row numbers! Normally this would happen per breakpoint.

.grid[data-per-row="2"]{
    img:nth-child(4n+3) {
    // this outline makes figuring out the nth-child stuff
    // way easier 👇
    outline: 2px solid red;
  }
  &[data-adjust] img:nth-child(4n+3) {
    // push it to the last column.
    grid-column: 2;
  }
  
}


.grid[data-per-row="4"]{
    img:nth-child(8n+5) {
    // this outline makes figuring out the nth-child stuff
    // way easier 👇
    outline: 2px solid red;
  }
  &[data-adjust] img:nth-child(8n+5) {
    // push it to the last column.
    grid-column: 4;
  }
}
.grid[data-per-row="6"]{
  img:nth-child(12n+7) {
    // this outline makes figuring out the nth-child stuff
    // way easier 👇
    outline: 2px solid red;
  }
  &[data-adjust] img:nth-child(12n+7) {
    // push it to the last column.
    grid-column: 6;
  }
}

// We could probably collapse all these rules and have them get mathed out via a Sass Mixin, or a Vue component. But I've sunk enough time into this already 😉


// repeaty
.grid[data-per-row="1"]{
  grid-template-columns: repeat(1, auto);
}
.grid[data-per-row="2"]{
  grid-template-columns: repeat(2, auto);
}
.grid[data-per-row="3"]{
  grid-template-columns: repeat(3, auto);
}
.grid[data-per-row="4"]{
  grid-template-columns: repeat(4, auto);
}
.grid[data-per-row="5"]{
  grid-template-columns: repeat(5, auto);
}
.grid[data-per-row="6"]{
  grid-template-columns: repeat(6, auto);
}
.grid[data-per-row="7"]{
  grid-template-columns: repeat(7, auto);
}

img {
  width: 100%;
  border: 1px solid black;
}
View Compiled
Vue.component('items-per-row', 
  { 
    name: 'items-per-row',
    props: {
      maxItems: {type: Number, default: 7},
      cycle: {type: Number, default: 3}
    },
    data: function(){
      return {
        items: 4,
        adjust: true
      }
    },
    computed: {
      options(){
        return Array.from(Array(this.maxItems).keys())
      }
    },
    template: `
<div>
  <label>Items per row: 
  <select v-model="items">
    <option v-for="opt in options">{{opt + 1}}</option>
  </select>
  </label>
  <label v-if="!(items % cycle)">Adjust positions: <input type="checkbox" v-model="adjust"></label>
  <div class="grid" :data-per-row="items" :data-adjust="adjust" :style="{ gridTemplateColumns: items }">
    <slot></slot>
  </div>
</div>
    `
});

new Vue({ el: '#fun' })
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js