Search code examples
cssresponsive-designflexboxcss-grid

Responsive two column layout reordering the blocks


On a mobile layout, I have six divs one below another in order of importance. On desktop, I would like to reorder the items. On the image below, the mobile layout is on the left, the desktop layout on the right.

Mobile and desktop layout

The heights of the six items are not related to each other. The layout should work with any heights.

I tried different solutions. The easiest to achieve the desktop layout would be to create two wrapper divs. But this does not allow to have a different order on mobile.

I tried to adapt forced horizontal wrapping in flexbox to vertical. This answer for column breaks almost worked, but it needs a fixed height for the container.

I searched about girds, and unfortunately, it seems impossible to have more than one element in a grid area.

Is there a way to solve this problem, without using Javascript ? (Our current solution involves Javascript, but the visitor can notice the rearrangement of the divs).


Solution

  • @Paulie_D's comment and @G-Cyr's answer inspired me this solution:

    The six divs that must be aligned are grouped in two intermediate <div>, representing the two columns in the desktop layout. They are grand-children of the container, instead of children.

    On mobile, we ignore the column divs with display: contents; and reorder the grand-children using flexbox's order: n;. On desktop we display the two columns using display: inline-block;, and leave the grand-children in source-order.

    for(let e of document.getElementsByClassName('item')) {
      e.style.minHeight = Math.floor(25 + Math.random() * 72) + 'px';
    }
    .container {
      display: flex;
      flex-direction: column;
    }
    .item {
      margin-bottom: 0.5em;
      text-align: center;
      background:#ddd;
      padding: 1em;
    }
    .left, .right{
      display: contents;
    }
    .item1, .item2{
      order: 1;
    }
    .item3, .item4{
      order: 2;
    }
    .item5, .item6{
      order: 3;
    }
    
    @media (min-width: 40em){
      .left, .right{
        display: inline-block;
        vertical-align: top;
      }
      .container{
        display: block;
      }
      .left{
        width: calc(67% - 0.5em);
        margin-right: 0.5em;
      }
      .right{
        width: 33%;
      }
    }
    <div class="container">
      <div class="left">
        <div class="item item1">1</div>
        <div class="item item3">3</div>
        <div class="item item5">5</div>
      </div><div class="right">
        <div class="item item2">2</div>
        <div class="item item4">4</div>
        <div class="item item6">6</div>
      </div>
    </div>