Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a CSS Grid that flows by column, with dynamic columns and rows based on content

Tags:

html

css

css-grid

I have a dynamic number of elements (all of equal dimensions). I want to position them in a grid, such that they are ordered in columns, with a dynamic number of columns based on the available container width, and with a dynamic number of rows based on the number of elements.

For example, assuming there are 9 elements:

1 4 7
2 5 8
3 6 9

But if the container width expands:

1 3 5 7 9
2 4 6 8

Or shrinks:

1 6
2 7
3 8
4 9
5

This works fine when the items are positioned by row, but not by column. Also, if I explicitly set the number of rows, it works fine, but if I use auto-fill for both rows and columns, it just displays everything in a single row.

Here's a simple example I would expect to be rendered as a 3x3 grid: https://codepen.io/anon/pen/ZxPQNd

#grid {
  width: 320px;
  border: 1px solid red;
  display: grid;
  grid-gap: 10px;
  grid-auto-flow: column;
  grid-template-rows: repeat(auto-fill, 100px);
  grid-template-columns: repeat(auto-fill, 100px);
  grid-auto-columns: 100px;
  grid-auto-rows: 100px;
}

#grid>div {
  background: lime;
}
<div id="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>
like image 630
hackel Avatar asked Apr 10 '18 23:04

hackel


2 Answers

The OP has the question exactly right. You've got an indeterminate number of elements to display and an indeterminately wide viewport in which to display them. And you want to display the elements in a grid.

Depending on the width of the viewport, you're going to want more or fewer columns. So the question is how to set that up.

One solution that comes to mind is to use CSS media queries to test the viewport width and then alter your layout accordingly. But that's tedious and prone to error. What you really want is a solution that works independently of viewport width.

The simplest solution, if all your elements are of uniformly fixed width, is to use flexbox. You might try something like this:

#grid {
  display: flex;
  flex-wrap: wrap;
}

#grid div {
  flex: 1 0 110px;
  background-color: yellow;
  width: 100px;
  height: 100px;
  margin: 10px;
  max-width: 100px;
  text-align: center;
}
<div id="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>

But you might also want the elements to expand to fill the horizontal space while still maintaining a uniform grid. For this you can use a CSS grid. The trick is to use a repeat and a minmax on the grid-template-columns property:

#grid {
  display: grid;
    grid-auto-flow: row;
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    grid-template-rows: repeat(auto-fill, 1fr);
    column-gap: 10px;
    row-gap: 10px;
}

#grid div {
  background-color: yellow;
  height: 100px;
  text-align: center;
}
<div id="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>
like image 144
lflier Avatar answered Nov 20 '22 21:11

lflier


Your desired layout is achievable using CSS multi-column layout using column-width property. Demo:

#grid {
  border: 1px solid red;
  column-width: 100px;
  column-gap: 10px;
}

#grid > div {
  break-inside: avoid-column; /* Prevent element from breaking */
  page-break-inside: avoid; /* Prevent element from breaking in Firefox */
  background: lime;
  width: 100px;
  height: 100px;
  margin-bottom: 10px;
}
<div id="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>
like image 31
Vadim Ovchinnikov Avatar answered Nov 20 '22 21:11

Vadim Ovchinnikov