Can I use css-grid to display an unknown number of items, in left-to-right reading order, over two rows?

I'd like to display a number of divs in left-to-right order, giving them an equal amount of space and spreading them over two rows.

e.g.

[div 1] | [div 2] | [div 3]
[div 4] | [div 5] | [div 6]

or

[div 1] | [div 2] | [div 3] | [div 4]
[div 5] | [div 6] | [div 7] | [div 8]

What's important is that I don't know how many elements there will be. If it's six, then I'll need three columns. If it's eight, then I'll need four columns. And so on.

If I give the grid container the following attributes:

  grid-auto-flow: column;
  grid-template-rows: repeat(2, 1fr);

Then my elements are given the right amount of space, but are displayed in the wrong order.

[div 1] | [div 3] | [div 5] | [div 7]
[div 2] | [div 4] | [div 6] | [div 8]

If I give the grid container the following attributes instead:

  grid-auto-flow: row;
  grid-template-rows: repeat(2, 1fr);

Then the elements appear in a single column.

[div 1]
[div 2]
...

Is there a simple way to display my elements across 2 rows, while maintaining left-to-right reading order, if I don't know how many elements there will be?


Solution 1:

A CSS only solution where you need to write a bit of CSS to cover many cases. The below code covers up to 12 elements and the CSS isn't that big. You can keep adding more to cover more cases.

.box {
  display: grid;
  grid-auto-flow:columns;
  grid-auto-columns:1fr;
  counter-reset: divs;
  margin: 5px;
  border:1px solid;
}

.box div  {
  border:1px solid red;
}

.box div:before {
  counter-increment: divs;
  content: counter(divs);
}

.box div:nth-child(1):nth-last-child(2) ~ *,
.box div:nth-child(2):nth-last-child(3) ~ *,
.box div:nth-child(3):nth-last-child(4) ~ *,
.box div:nth-child(4):nth-last-child(5) ~ *,
.box div:nth-child(5):nth-last-child(6) ~ *,
.box div:nth-child(6):nth-last-child(7) ~ *
/*
.box div:nth-child(N):nth-last-child(N+1) ~ * 
*/{
   grid-row:2;
}
<div class="box">
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
</div>


<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div> 
  <div></div> 
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div> 
  <div></div> 
  <div></div> 
  <div></div> 
</div>

A SASS code to generate the CSS (you can try here: https://www.sassmeister.com/)

$n:12; /* make this a big as you need */

@for $i from 1 through $n {
  .box div:nth-child(#{$i}):nth-last-child(#{$i + 1}) ~ *{grid-row:2}
}

You can also cover odd number of elements:

.box {
  display: grid;
  grid-auto-flow:columns;
  grid-auto-columns:1fr;
  counter-reset: divs;
  margin: 5px;
  border:1px solid;
}

.box div  {
  border:1px solid red;
   grid-row:1;
}

.box div:before {
  counter-increment: divs;
  content: counter(divs);
}

.box div:nth-child(1):nth-last-child(2) ~ *,
.box div:nth-child(2):nth-last-child(2) ~ *,
.box div:nth-child(2):nth-last-child(3) ~ *,
.box div:nth-child(3):nth-last-child(3) ~ *,
.box div:nth-child(3):nth-last-child(4) ~ *,
.box div:nth-child(4):nth-last-child(4) ~ *,
.box div:nth-child(4):nth-last-child(5) ~ *,
.box div:nth-child(5):nth-last-child(5) ~ *,
.box div:nth-child(5):nth-last-child(6) ~ *,
.box div:nth-child(6):nth-last-child(6) ~ *,
.box div:nth-child(6):nth-last-child(7) ~ *,
.box div:nth-child(7):nth-last-child(7) ~ *{
   grid-row:2;
}
<div class="box">
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div> 
  <div></div> 
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div> 
  <div></div> 
  <div></div> 
</div>

<div class="box">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div> 
  <div></div> 
  <div></div> 
  <div></div> 
</div>

The SASS code:

$n:12;

@for $i from 1 through $n {
  .box div:nth-child(#{$i}):nth-last-child(#{$i + 1}) ~ *,
  .box div:nth-child(#{$i + 1}):nth-last-child(#{$i + 1}) ~ *{grid-row:2}
}

Solution 2:

You will need javascript to calculate a min-width value for the grid column , then auto-fit,mimax() can do.

possible example

let yop =document.querySelectorAll('#test  div')
document.body.style.setProperty('--minWidth', Math.ceil(200 / yop.length) -1 +'%' );
#test {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--minWidth), 1fr));
  counter-reset: divs;
  /* width:800px; or not */
}

div div  {
  border: solid;
  box-sizing:border-box;
}

div div:before {
  counter-increment: divs;
  content: counter(divs);
}
<div id="test">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div> 
  <div></div> 
</div>

Its a basic approach , a percentage calculated from 200 willdraw 2 rows, 300 will draw 3 rows and so on.

But it can be a flex layout or floatting element, grid is not the magic here. CSS is unable to read the dom and adapt a value on the fly like you would wish.