26

I have a list of items of unknown length (from a CMS). I want to display them in 2 vertical columns reading down. e.g.

1 4
2 5
3 6

etc...

I am trying to achieve this with CSS grid, however, it doesn't seem possible unless you set the number of rows up front. I have tried grid-auto-flow: column as per https://gridbyexample.com/examples/example18/ but this just adds additional columns when it gets to the end.

I feel like this should be possible with grid, but I can't find a way. Anyone have any ideas?

P.S. Please don't suggest CSS text columns.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
matthewbeta
  • 696
  • 1
  • 7
  • 17

4 Answers4

21

Without knowing the exact amount of items this is not possible with CSS grid alone.

The only way to get around this limitation is to add a class to your second half of the items.

body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-flow: row dense;
  
  /* extra styles */
  grid-gap: 0.5rem;
}

span {
  grid-column-start: 1;
  
  /* extra styles */
  background-color: #def;
  padding: 0.5rem;
}

.second-half {
  grid-column-start: 2;
  
  /* extra styles */
  background-color: #abc;
}
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span class="second-half">5</span>
<span class="second-half">6</span>
<span class="second-half">7</span>
chrona
  • 1,853
  • 14
  • 24
  • Hi. thanks for the response but in the question, I explained I have a list of unknown length do this won't be possible. – matthewbeta Jul 04 '17 at 15:16
  • In that case your problem can't be solved (with CSS grid alone). I changed my answer, to showcase that you could apply the styles to a class instead of knowing the amount of items. – chrona Jul 04 '17 at 15:23
  • You can also make the list a fixed height but then if you have too few items it will be too tall and if you have too many items a third column will probably be auto-generated. – thinsoldier Jul 05 '17 at 22:20
  • If you have the option to use javascript you can add the classes for the latter half. const $allItems = $('span'); if($allItems.length > 1) { const half = Math.floor($allItems.length / 2); $allItems.slice(-half).addClass('second-row'); } – Japo Domingo Oct 07 '21 at 04:04
6

Example:

// This is just to simulate infinite scrolling

var counter = 9;
document.addEventListener('scroll', function(e) {
  if (document.body.scrollTop > 50 || document.documentElement.scrollTop > 50) {
    var span = document.createElement('span');
    span.innerHTML = ++counter;
    document.body.appendChild(span);
  }
})
body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: 200px;
  /* how much height must each element occupy! change that! */
  grid-gap: 0.5rem;
}

span {
  background: #3A3A3A;
  text-align: center;
  color: #FFFFFF;
  line-height: 200px;
  font-size: xx-large;
}
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
<span>6</span>
<span>7</span>
<span>8</span>
George Metsis
  • 107
  • 1
  • 3
5

One solution if your HTML is generated you can calculate the grid-template-rows property on the container element with Math.ceil( NUM_ITEMS / NUM_COLUMNS )

In React:

function VerticalColumns(props) {
    // props.numColumns matches `grid-template-columns` on `.container` element
    const numRows = Math.ceil(props.items.length / props.numColumns);

    const style = {
        gridTemplateRows: `repeat(${numRows}, 1fr)`,
    };

    return (
        <ul className='container' style={ style }>
            { props.items.map((item, index) => (
                <li key={index}>{ item }</li>
            )) }
        </ul>
    )
}

Base CSS:

.container {
    display: grid;
    grid-auto-flow: column;
    grid-template-columns: repeat(2, 1fr);
}
user2166772
  • 51
  • 1
  • 2
2

You can use a flex in which there is a container and a flex item. You can limit the height of the container and then wrap the contents of flex to continue in the next column :-

<body>
<div class="container">
  <p>1</p>
  <p>1</p>
  <p>1</p>
  <p>1</p>
  <p>1</p>
</div>
</body>

CSS:

.container {
  height: 300px;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
}

Read more about flexbox

benedemon
  • 411
  • 2
  • 5
  • 14