Make grid container fill columns not rows

55,501

Solution 1

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

Solution 2

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>

Solution 3

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>

Solution 4

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>

Share:
55,501

Related videos on Youtube

Glen Pierce
Author by

Glen Pierce

Software engineer using Java, JavaScript, HTML, CSS, SQL, Python, and a little C#. I love git. I've got some personal projects using Node.js and some Unity. I'd love to do more with D3.js. I'm also interested in doing some more work with Blender.

Updated on July 08, 2022

Comments

  • Glen Pierce
    Glen Pierce almost 2 years

    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?

    • benface
      benface almost 7 years
      This is a very interesting problem, I have the same question and I'm wondering if you found a better solution than the answers here, which are not suitable for an arbitrary number of items/rows (or don't use CSS Grid).
    • Manohar Reddy Poreddy
      Manohar Reddy Poreddy over 5 years
      Answer that works is deep down: stackoverflow.com/a/44099977/984471
  • Glen Pierce
    Glen Pierce about 7 years
    Would love for that to work, but it just ends up on one line now: codepen.io/glenpierce/pen/XRyevJ
  • Glen Pierce
    Glen Pierce about 7 years
    I see what you're saying, I wasn't sufficiently clear. It's important that I have 3 columns.
  • Glen Pierce
    Glen Pierce about 7 years
    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
    Glen Pierce about 7 years
  • Ilya Streltsyn
    Ilya Streltsyn about 7 years
    Just as a small note: recent versions of Chrome (50+) and Firefox (52+) have already unprefixed column-related properties (developer.mozilla.org/en-US/docs/Web/CSS/…)
  • Asons
    Asons about 7 years
    @IlyaStreltsyn I know, but as the amount of users that still use older versions is quite many, it is a good idea to keep the prefix a little bit longer :)¨
  • Michael Benjamin
    Michael Benjamin about 7 years
    Amazing use of :nth-child().
  • vals
    vals about 7 years
    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.
  • Michael Benjamin
    Michael Benjamin about 7 years
    @vals, agreed, unless of course, it's equally important for child elements to accept grid / flex properties.
  • MattTreichel
    MattTreichel over 5 years
    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.
  • mix3d
    mix3d over 4 years
    Simple, yes, but does not work if you need a variable number of rows, depending on the length of the incoming data