2

I have a situation where I want to display items on a web page vertically within columns. I want the number of columns to change based upon the available screen width. The items belong to groups. When there is enough screen size, I want display those in the same group in their own column. When the screen size is too narrow to display separate columns, I would like to combine the items into fewer columns. At the narrowest breakpoint, the items will be in one column. For now, a solution that supports two columns for wider screens and one column for narrow screens is sufficient.

There is another catch: the items have a global ordering, which must be preserved within columns.

Below is a snippet showing my current approach using float (based on this answer.) (I have included the global ordering (1-5) in the items to help illustrate.) This approach is close, but it has a problem where there is extra space between group items when they float below the last item in that group. (You might need to play with the media query breakpoint to see the behavior on your device.)

.items {
  overflow: auto;
}
.item {
  width: 50%;
}
.item.group-a {
  float: left;
  clear: left;
  background-color: #FAF;
}
.item.group-b {
  float: right;
  clear: right;
  background-color: #AAF;
}

@media only screen and (max-width: 400px) {
  .item.group-a, .item.group-b {
    float: none;
    width: 100%;
  }
}
<div class="items">
  <div class="item group-a">
    1.  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus ipsum quis nibh pretium commodo.
  </div>
  <div class="item group-b">
    2. Quisque dolor ex, maximus non orci vel, luctus rhoncus lectus. Aliquam vulputate lacus et ipsum placerat commodo.
  </div>
  <div class="item group-a">
    3. Cras gravida rhoncus elit, eu porttitor ipsum tempor at. Sed blandit pulvinar purus, eget pellentesque arcu rhoncus in.
  </div>
  <div class="item group-a">
    4. Morbi accumsan, lectus a hendrerit congue, sapien dolor accumsan purus, eu iaculis nulla turpis ut sapien.
  </div>
  <div class="item group-b">
    5. Aliquam elementum sapien ut dignissim lacinia. Etiam in nulla feugiat, porta massa at, tempor nulla. Suspendisse ullamcorper at ligula ut auctor.
  </div>
</div>

In the production system, the number of items, group assignment, and ordering can change, so I cannot depend on the particular items or their ordering shown here. I will know the possible groups in advance.

Here are images of the behavior in case the snippet doesn't work for you. This first image shows the items combined into a single column for a narrow viewport. Note that the items are in order.

Single column at narrow viewport

This second image shows the items split into two columns. Note that the the gap between items 2 and 5 is not intentional and is what I am trying to get rid of.

Two columns at wider viewport

This is new development, so I'm not tied to any particular HTML or approach. I was attempting to accomplish this using only CSS for simplicity, but if the consensus is that JavaScript is necessary, that's okay. I saw some answers using flex order to re-order items, but I didn't see how I could design a system to support that generally when I don't know which items/groups/orders are coming out of the system.

At some point I would like to support more than two columns. Ideally, I would have a maximum of five groups/columns for the widest screens, then, as screens get narrower: three, two, and just one column. (There are business rules that dictate how groups can be combined.)

This answer offsetting the float doesn't work for me because the items are dynamic so I don't know in advance which floats might need offsetting. This answer about multi-col doesn't work because I must separate the items by their group. I am not just trying to layout a homogenous list of items in a columnar format.

Thanks!

Community
  • 1
  • 1
Carl G
  • 14,291
  • 12
  • 78
  • 103

1 Answers1

2

If your container height can be set before hand, you have a solution using flex display.

Order is used to set the columns as you want, and a pseudo element is used as a spacer.

For a solution with more than 3 columns, since there are only 2 pseudos available, you would need extra elements to act as separators

.items {
  overflow: auto;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  height: 90vh;
}
.item {
  width: 50%;
}
.item.group-a {
  background-color: #FAF;
  order: 10;
}
.item.group-b {
  background-color: #AAF;
  order: 20;
}


.items:before {
  content: "";
  order: 15;
  height: 100vh;
}
@media only screen and (max-width: 400px) {
  .item.group-a, .item.group-b {
    width: 100%;
  }
  .item.group-a {
      order: 10;
  }
  .item.group-b {
      order: 10;
  }
}
<div class="items">
  <div class="item group-a">
    1.  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus ipsum quis nibh pretium commodo.
  </div>
  <div class="item group-b">
    2. Quisque dolor ex, maximus non orci vel, luctus rhoncus lectus. Aliquam vulputate lacus et ipsum placerat commodo.
  </div>
  <div class="item group-a">
    3. Cras gravida rhoncus elit, eu porttitor ipsum tempor at. Sed blandit pulvinar purus, eget pellentesque arcu rhoncus in.
  </div>
  <div class="item group-a">
    4. Morbi accumsan, lectus a hendrerit congue, sapien dolor accumsan purus, eu iaculis nulla turpis ut sapien.
  </div>
  <div class="item group-b">
    5. Aliquam elementum sapien ut dignissim lacinia. Etiam in nulla feugiat, porta massa at, tempor nulla. Suspendisse ullamcorper at ligula ut auctor.
  </div>
</div>
vals
  • 54,758
  • 10
  • 75
  • 124