162

I want my grid to fill in vertically like this:

1 4 7 
2 5 8
3 6 9
... arbitrary number of additional rows.

Instead, it fills in horizontally like this:

1 2 3
4 5 6
7 8 9

I want to specify the number of columns in my grid, not the number of rows.

This is what my div looks like with inline CSS styling:

<div style="display:grid; grid-template-columns:1fr 1fr 1fr;">
  <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>

It's important that my grid be 3 columns wide, but I want the items to be populated by column, not by row. Is this possible in CSS Grid? I've read through this https://css-tricks.com/snippets/css/complete-guide-grid/ but didn't see anything about order.

CSS Flexbox has flex-direction, isn't there an attribute like that for CSS Grid?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Glen Pierce
  • 4,401
  • 5
  • 31
  • 50

6 Answers6

122

For a vertically-flowing grid that creates new columns as necessary, and rows are not defined, consider using CSS Multi-Column Layout (example). CSS Grid Layout (at least the current implementation - Level 1) cannot perform this task. Here's the problem:

In CSS Grid Layout, there is an inverse relationship between the grid-auto-flow and grid-template-rows / grid-template-columns properties.

More specifically, with grid-auto-flow: row (the default setting) and grid-template-columns both defined, grid items flow nicely in a horizontal direction, automatically creating new rows as necessary. This concept is illustrated in the code in the question.

#container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-auto-flow: row;
}
<div id="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>

However, with a switch to grid-template-rows, grid items stack in a single column.

#container {
  display: grid;
  grid-template-rows: 1fr 1fr 1fr;
  grid-auto-flow: row;
}
<div id="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>

There is no automatic creation of columns with grid-auto-flow: row and grid-template-rows. grid-template-columns must be defined (hence, the inverse relationship with grid-auto-flow).

#container {
  display: grid;
  grid-template-rows: 1fr 1fr 1fr;
  grid-template-columns: 1fr 1fr 1fr;
  grid-auto-flow: row;
}
<div id="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>

The same behavior is true in the reverse scenario.

With grid-auto-flow: column and grid-template-rows both defined, grid items flow nicely in a vertical direction, automatically creating new columns as necessary.

#container {
  display: grid;
  grid-template-rows: 1fr 1fr 1fr;
  grid-auto-flow: column;
}
<div id="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>

However, with a switch to grid-template-columns, grid items stack in a single row. (This is the problem most people ask about, including in this question.)

#container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-auto-flow: column;
}
<div id="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>

There is no automatic creation of rows. That requires grid-template-rows to be defined. (This is the solution most often provided, but it is usually rejected because the layouts have a variable number of rows.)

#container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  grid-auto-flow: column;
}
<div id="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>

Hence, consider a multi-column layout solution, as suggested above.

Spec reference: 7.7. Automatic Placement: the grid-auto-flow property

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • Would love for that to work, but it just ends up on one line now: https://codepen.io/glenpierce/pen/XRyevJ – Glen Pierce May 21 '17 at 02:32
  • I see what you're saying, I wasn't sufficiently clear. It's important that I have 3 columns. – Glen Pierce May 21 '17 at 02:36
  • More background: I'm building a widget that's going to be used in several places. The containers can be any height and should be able to contain any number of rows. The only limit I'm trying to impose on the grid is that it be 3 columns wide and order the items by column first then row. – Glen Pierce May 21 '17 at 02:43
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/144755/discussion-between-glen-pierce-and-michael-b). – Glen Pierce May 21 '17 at 02:50
65

Another option is to drop CSS Grid and use CSS Columns, which does exactly what you ask and also have much better browser support.

.csscolumn {
  -webkit-column-count: 3;  /* Chrome, Safari, Opera */
  -moz-column-count: 3;     /* Firefox */
  column-count: 3;
}

/* styling for this demo */
.csscolumn {
  width: 50%;
}
.csscolumn + .csscolumn {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid;
}
<div class="csscolumn">
  <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="csscolumn">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>

<div class="csscolumn">
  <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>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • 2
    Just as a small note: recent versions of Chrome (50+) and Firefox (52+) have already unprefixed column-related properties (https://developer.mozilla.org/en-US/docs/Web/CSS/column-count#Browser_compatibility) – Ilya Streltsyn May 21 '17 at 20:35
  • @IlyaStreltsyn I know, but as the [**amount of users**](http://caniuse.com/#feat=multicolumn) that still use older versions is quite many, it is a good idea to keep the prefix a little bit longer :)¨ – Asons May 21 '17 at 20:39
  • 3
    Absolutely right .... Most of the time the question shouldn't be how can i force this tool to behave like I want, but instead if I have choosen the right tool. – vals May 22 '17 at 17:24
  • 2
    @vals, agreed, unless of course, it's equally important for child elements to accept grid / flex properties. – Michael Benjamin May 22 '17 at 17:42
  • 2
    Yeah, CSS Columns seem best suited for purely textual content rather than blocks, because of spotty wrapping behaviour that can break blocks apart unintentionally, with handling that differs cross-browser. – MattTreichel Feb 12 '19 at 19:01
  • 2
    this answer made me really happy right now – Hannes Schneidermayer Feb 02 '23 at 09:08
19

The simplest method I've seen follows:

.grid {
 display: grid;
 grid-auto-flow: column;
 grid-gap: 1px;
 grid-template-columns: repeat(3, 1fr); 
 grid-template-rows: repeat(5, auto);    
}
<div class="grid">
<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>11</div>
<div>12</div>
<div>13</div>
</div>
RayYates
  • 207
  • 2
  • 3
  • 12
    Simple, yes, but does not work if you need a variable number of rows, depending on the length of the incoming data – mix3d Mar 09 '20 at 19:35
  • 2
    ^ it does if you count the rows in JS and divide by the number of columns then round to the whole number and assign the CSS declaration in your JS file (what I ended up doing) --------------------- Thank you huge help!! – tiadotdev Jan 25 '23 at 20:33
9

More as a technical exercise than as a practical solution, you can get somehow your result using specific styles depending on the number of items

Let's see how it works:

.item:first-child:nth-last-child(n+4):nth-last-child(-n + 6) ~ .item:nth-child(n+3)

the first selector

.item:first-child:nth-last-child(n+4):nth-last-child(-n + 6)

is active is our list has between 4 and 6 elements. In this case, some item will be both in the first condition and in the second.

In this case, we want 2 items to be in the first column. target the remaining items (from the third onwards) with

~ .item:nth-child(n+3)

and put them on the second column. A similar rule, now for the 5th and onwards

~ .item:nth-child(n+5)

puts the other items in the third column. These 2 rules have the same precedence, and target both the last items, so it's critical that they appear in this order.

We need to repeat similar rules up to the maximum amount of items that can be present (probably a job for a preprocessor)

var elements = 5;

function add () {
    var ctn = document.getElementById("container");
    var ele = document.createElement("div");
    elements ++;
    ele.innerHTML = elements;
    ele.className = "item";
    ctn.appendChild (ele);
}
#container {
  width: 90%;
  border: solid 1px red;
  display: grid;
  grid-template-rows: 33% 33% 33%;
  grid-auto-flow: column dense;
}

.item {
  width: 90%;
  height: 80px;
  background-color: lightgreen;
  margin: 10px;
  grid-column: 1;
}

.item:first-child:nth-last-child(n+4):nth-last-child(-n + 6) ~ .item:nth-child(n+3) {
  background-color: yellow;
  grid-column: 2;
}

.item:first-child:nth-last-child(n+4):nth-last-child(-n + 6) ~ .item:nth-child(n+5) {
  background-color: tomato;
  grid-column: 3;
}

.item:first-child:nth-last-child(n+7):nth-last-child(-n + 9) ~ .item:nth-child(n+4) {
  background-color: burlywood;
  grid-column: 2;
}

.item:first-child:nth-last-child(n+7):nth-last-child(-n + 9) ~ .item:nth-child(n+7) {
  background-color: blueviolet;
  grid-column: 3;
}

.item:first-child:nth-last-child(n+10):nth-last-child(-n + 12) ~ .item:nth-child(n+5) {
  background-color: darkcyan;
  grid-column: 2;
}

.item:first-child:nth-last-child(n+10):nth-last-child(-n + 12) ~ .item:nth-child(n+9) {
  background-color: chartreuse;
  grid-column: 3;
}

.item:first-child:nth-last-child(n+13):nth-last-child(-n + 15) ~ .item:nth-child(n+6) {
  background-color: yellow;
  grid-column: 2;
}

.item:first-child:nth-last-child(n+13):nth-last-child(-n + 15) ~ .item:nth-child(n+11) {
  background-color: tomato;
  grid-column: 3;
}
<button onclick="add()">Add</button>
<div id="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
vals
  • 61,425
  • 11
  • 89
  • 138
0

Here is one CSS Grid based approach using javascript and the CSSOM to insert a pair of:

transform: translate(x, y)

rules into a generated stylesheet.

The two transform rules (there are only two, based on the grid being 3 columns wide displace the lower elements of the original single column grid, moving the elements upwards and to the right.

Consequently, you can add any number of elements to the single-column grid, and the script will always adjust the grid so that it has three columns of more or less equal size.

If the columns cannot be of exactly equal size, then the taller column(s) will always be the first and / or second column (never the rightmost, third column).

Working Example (9 grid units):

var numberOfColumns = 3;

document.head.appendChild(document.createElement('style'));
var newStyles = document.styleSheets[(document.styleSheets.length - 1)];

var myGrid = document.getElementsByClassName('my-grid')[0];
var myGridUnits = myGrid.getElementsByTagName('div');

var tallColumn = Math.ceil(myGridUnits.length /  numberOfColumns);
var shortColumn = Math.floor(myGridUnits.length / numberOfColumns);

var nextUnit = 1;
var unitsRemaining = myGridUnits.length;
var xTranslate, yTranslate;
var columns = [];

for (var i = 0; i < (numberOfColumns - 1); i++) {


    if (unitsRemaining % shortColumn === 0) {
    
        columns.push(shortColumn);
    }

    else {

        columns.push(tallColumn);
    }
    
    nextUnit += columns[(columns.length - 1)];
    unitsRemaining -= columns[(columns.length - 1)];
    
    xTranslate = ((i + 1) * 48);
    yTranslate = 0;
    columns.forEach(function(columnHeight){yTranslate += (columnHeight * 48);});
                         
    newStyles.insertRule('.my-grid div:nth-of-type(n+' + nextUnit + ') {transform: translate(' + xTranslate + 'px, ' + (0 - (yTranslate)) + 'px);}', newStyles.cssRules.length);

}
.my-grid {
display: inline-grid;
grid-row-gap: 6px;
}

.my-grid div {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border: 1px solid rgb(127, 127, 127);
}
<div class="my-grid">
<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>

Working Example (10 grid units):

var numberOfColumns = 3;

document.head.appendChild(document.createElement('style'));

var newStyles = document.styleSheets[(document.styleSheets.length - 1)];

var myGrid = document.getElementsByClassName('my-grid')[0];
var myGridUnits = myGrid.getElementsByTagName('div');

var tallColumn = Math.ceil(myGridUnits.length /  numberOfColumns);
var shortColumn = Math.floor(myGridUnits.length / numberOfColumns);

var nextUnit = 1;
var unitsRemaining = myGridUnits.length;
var xTranslate, yTranslate;
var columns = [];

for (var i = 0; i < (numberOfColumns - 1); i++) {


    if (unitsRemaining % shortColumn === 0) {
    
        columns.push(shortColumn);
    }

    else {

        columns.push(tallColumn);
    }
    
    nextUnit += columns[(columns.length - 1)];
    unitsRemaining -= columns[(columns.length - 1)];
    
    xTranslate = ((i + 1) * 48);
    yTranslate = 0;
    columns.forEach(function(columnHeight){yTranslate += (columnHeight * 48);});
                         
    newStyles.insertRule('.my-grid div:nth-of-type(n+' + nextUnit + ') {transform: translate(' + xTranslate + 'px, ' + (0 - (yTranslate)) + 'px);}', newStyles.cssRules.length);

}
.my-grid {
display: inline-grid;
grid-row-gap: 6px;
}

.my-grid div {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border: 1px solid rgb(127, 127, 127);
}
<div class="my-grid">
<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>

Working Example (11 grid units):

var numberOfColumns = 3;

document.head.appendChild(document.createElement('style'));

var newStyles = document.styleSheets[(document.styleSheets.length - 1)];

var myGrid = document.getElementsByClassName('my-grid')[0];
var myGridUnits = myGrid.getElementsByTagName('div');

var tallColumn = Math.ceil(myGridUnits.length /  numberOfColumns);
var shortColumn = Math.floor(myGridUnits.length / numberOfColumns);

var nextUnit = 1;
var unitsRemaining = myGridUnits.length;
var xTranslate, yTranslate;
var columns = [];

for (var i = 0; i < (numberOfColumns - 1); i++) {


    if (unitsRemaining % shortColumn === 0) {
    
        columns.push(shortColumn);
    }

    else {

        columns.push(tallColumn);
    }
    
    nextUnit += columns[(columns.length - 1)];
    unitsRemaining -= columns[(columns.length - 1)];
    
    xTranslate = ((i + 1) * 48);
    yTranslate = 0;
    columns.forEach(function(columnHeight){yTranslate += (columnHeight * 48);});
                         
    newStyles.insertRule('.my-grid div:nth-of-type(n+' + nextUnit + ') {transform: translate(' + xTranslate + 'px, ' + (0 - (yTranslate)) + 'px);}', newStyles.cssRules.length);

}
.my-grid {
display: inline-grid;
grid-row-gap: 6px;
}

.my-grid div {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border: 1px solid rgb(127, 127, 127);
}
<div class="my-grid">
<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>11</div>
</div>

Working Example (14 grid units):

var numberOfColumns = 3;

document.head.appendChild(document.createElement('style'));

var newStyles = document.styleSheets[(document.styleSheets.length - 1)];

var myGrid = document.getElementsByClassName('my-grid')[0];
var myGridUnits = myGrid.getElementsByTagName('div');

var tallColumn = Math.ceil(myGridUnits.length /  numberOfColumns);
var shortColumn = Math.floor(myGridUnits.length / numberOfColumns);

var nextUnit = 1;
var unitsRemaining = myGridUnits.length;
var xTranslate, yTranslate;
var columns = [];

for (var i = 0; i < (numberOfColumns - 1); i++) {


    if (unitsRemaining % shortColumn === 0) {
    
        columns.push(shortColumn);
    }

    else {

        columns.push(tallColumn);
    }
    
    nextUnit += columns[(columns.length - 1)];
    unitsRemaining -= columns[(columns.length - 1)];
    
    xTranslate = ((i + 1) * 48);
    yTranslate = 0;
    columns.forEach(function(columnHeight){yTranslate += (columnHeight * 48);});
                         
    newStyles.insertRule('.my-grid div:nth-of-type(n+' + nextUnit + ') {transform: translate(' + xTranslate + 'px, ' + (0 - (yTranslate)) + 'px);}', newStyles.cssRules.length);

}
.my-grid {
display: inline-grid;
grid-row-gap: 6px;
}

.my-grid div {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border: 1px solid rgb(127, 127, 127);
}
<div class="my-grid">
<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>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
</div>
Rounin
  • 27,134
  • 9
  • 83
  • 108
-4

One way to fill the grid vertically is using column-count = 3; CSS property and removing the display: grid;. But in my case it caused an issue (big white space) on Iphone 13. Here is how I switched the order from horizontal to vertical in grid layout:

function horizontalToVerticalGridSorting(gridSelector) {
    const grid = document.querySelector(gridSelector);
    const gridItems = grid.children;
    const gridComputedStyle = window.getComputedStyle(grid);
    // Grid columns count
    const colCount = gridComputedStyle.gridTemplateColumns.split(' ').length;
    // Grid rows count
    const rowsCount = gridComputedStyle.gridTemplateRows.split(' ').length;
    // Total items count
    const itemsCount = gridItems.length;
    // Full columns amount
    const fullColCount = itemsCount % colCount || colCount;
    // Amount of items in full columns
    const fullColItems = fullColCount*rowsCount;
    // Current column
    let curX = 1;
    // Current row
    let curY = 1;
    for (let i = 0; i < itemsCount; i++) {
        gridItems[i].style.order = (curY - 1) * colCount + curX;
        // Number of items in column
        let perColItems = rowsCount;
        // Not full column
        if (i >= fullColItems) {
            perColItems--;
        }
        curY++;
        // Switching to the next column
        if (curY > perColItems) {
            curY = 1;
            curX++;
        }
    }
}
horizontalToVerticalGridSorting('.cols');
.cols {
  text-align: center;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.col {
  padding: 10px;
}
<div class="cols">
  <div class="col">1</div>
  <div class="col">2</div>
  <div class="col">3</div>
  <div class="col">4</div>
  <div class="col">5</div>
  <div class="col">6</div>
  <div class="col">7</div>
  <div class="col">8</div>
  <div class="col">9</div>
  <div class="col">10</div>
</div>

The function goes through all grid items and calculates the new coordinates [X, Y] for vertical sorting. Then the order is calculates based on that new coordinates. It works with any grid size

san4o
  • 13
  • 3