'Split items into two columns CSS grid

Is there a way to achieve the following via CSS grid?

  1. If the number of items is odd, the first column should have 1 more row than the second column.
1 4
2 5
3
  1. If the number of items is even, the columns should have the same number of rows.
1 3 
2 4

Tried using grid-template-rows but the number of rows per column is fixed.



Solution 1:[1]

UPDATED: Added two more options how to handle it by HTML/CSS. What about using grid? There is 3 options, how it can be done:

  1. Using default item order;
  2. Using CSS variable, where you can define row number to break;
  3. Using additional class, added to child item, after wich it's siblinks will go to next column;

/* Option-1. Default */
.container {
  display: grid;
  grid-template-columns: repeat(2, minmax(50px, 1fr));
  grid-gap: 1rem;
  width: fit-content;
}

.container > div {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  background-color: var(--color, tomato);
  aspect-ratio: 1/1;
}
/* End of Option-1. */

/* Option-2. With additional class */
.container--with-break {
  grid-auto-flow: column;
}
.container--with-break > div {
  grid-column: 1;
}
.container--with-break > .break ~ div {
  grid-column: 2;
}
/* End of Option-2. */

/* Option-3. With CSS variable */
.container--with-break-row {
  grid-auto-flow: column;
  grid-template-rows: repeat(var(--break-row), 1fr);
}
/* End of Option-3. */

/* Example stylings */
.wrapper {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-gap: 2rem;
  align-items: start;
  width: fit-content;
}

hr {
  margin-top: 2rem;
  margin-bottom: 2rem;
}
/* End of Ex. stylings */
<p>Option with default order</p>
<div class="wrapper">
  <div class="container">
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
  </div>

  <div class="container">
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
  </div>
</div>

<hr />

<p>Option with changing order. Using CSS variables</p>
<div 
  class="wrapper"
  style="--color: mediumturquoise;"
>
  <div 
    class="container container--with-break-row"
    style="--break-row: 3;"
  >
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
  </div>
  
  <div 
    class="container container--with-break-row"
    style="--break-row: 2;"
  >
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
  </div>
  
  <div 
    class="container container--with-break-row"
    style="--break-row: 3;"
  >
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
    <div>6</div>
  </div>
  
  <div 
    class="container container--with-break-row"
    style="--break-row: 4;"
  >
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
    <div>6</div>
    <div>7</div>
  </div>
</div>

<hr />

<p>Option with changing order. Using additional class to child item</p>
<div 
  class="wrapper"
  style="--color: mediumpurple;"
>
  <div class="container container--with-break">
    <div>1</div>
    <div>2</div>
    <div class="break">3</div>
    <div>4</div>
    <div>5</div>
  </div>
  
  <div class="container container--with-break">
    <div>1</div>
    <div class="break">2</div>
    <div>3</div>
    <div>4</div>
  </div>
</div>

Solution 2:[2]

If the number of items is always 4 or 5 you can do the following:

.container {
  display: grid;
  grid-gap: 1rem;
  grid-auto-flow: column;
  grid-template-rows: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  justify-content: start;
  margin: 10px;
  border: 2px solid red;
}

.container>div {
  background-color: lightblue;
  padding: 20px;
}

/* create the extro row for the case of odd items */
.container>div:nth-child(3):nth-last-child(odd) {
  grid-row: 3;
}
<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>

For the generic case like below

.container {
  display: grid;
  grid-gap: 1rem;
  grid-template-rows: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  grid-auto-flow: dense; /* you need this */
  justify-content: start;
  margin: 10px;
  border: 2px solid red;
}

.container>div {
  background-color: lightblue;
  padding: 10px;
}

/* create the extro row for the case of odd items */
.container>div:nth-child(3):nth-last-child(odd) {
  grid-row: 3;
}
/* all odd items counting from the end at the second row*/
.container > div:nth-last-child(odd) {
 grid-row: 2;
}

/* the first and second item should never change*/
.container > div:first-child {
 grid-row: 1;
}
.container > div:nth-child(2) {
 grid-row: 2;
}
<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
  <div>8</div>
  <div>9</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
  <div>8</div>
  <div>9</div>
  <div>10</div>
</div>

Solution 3:[3]

Credits for the grid go to @YaroslavTrach but the order of HTML elements can be achieved by splitting your elements into two arrays and then alternating between them when rendering.

const elements = [ 1, 2, 3, 4, 5 ];
const half = Math.ceil(elements.length / 2);

const firstHalf = elements.slice(0, half);
const secondHalf = elements.slice(-half + 1);

const toDiv = (i) => i ? `<div>${i}</div>` : ``;

document.getElementById('container').innerHTML = `
  ${firstHalf.map((el, id) => toDiv(el) + toDiv(secondHalf[id])).join('')}
`;
#container {
  display: grid;
  grid-template-columns: repeat(2, minmax(50px, 1fr));
  grid-gap: 1rem;
  width: fit-content;
}

#container>div {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  background-color: tomato;
  aspect-ratio: 1/1;
}
<div id="container">
</div>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3