1

So I have a collection of images of variable heights, and I'm displaying them in a three-column CSS grid – the result looks like this

da bears

Note the sequential numbering and the fact that a longer image can take up more of the column's height, but widths are fixed

The only way I know to implement this is with something like

<div class="grid">
    <div class="col">
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
    </div>
    <div class="col">
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
    </div>
    <div class="col">
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
    </div>
</div>

which works fine, but I want to make it responsive – specifically, such that the cards get stacked sequentially in a single column. I am not sure how to do this. Typically I'd arrange the elements by row and then compressing to a single column is easy, but if I were to arrange by row, I wouldn't get the dynamic height effect. Also, I could just stack each of these columns on top of each other, but then I lose the sequential nature of the bears.

Any ideas on what I can do?

Edit: Also note, I want the height to be dynamic – I shouldn't have to manually specify the height of each cell

mjkaufer
  • 4,047
  • 5
  • 27
  • 55

2 Answers2

0

Unfortunately there aren't any magic CSS properties here that will achieve what you're looking for. But I can think of a few options:

Implement a JavaScript Library

Your best option is probably to implement a JavaScript library. There are several existing libraries out there that were built to achieve this exact cascading grid layout you're looking for.

Here are a couple options:

  1. Masonry
  2. Isotope

Re-order Elements with JavaScript/jQuery

Another option would be to reorder your elements on mobile using JavaScript. This would allow you to display your elements on desktop using the columns, and on mobile, you could manually re-order them.

Using the answer from this stack overflow question as a guide, you might be able to do something like this (which uses jQuery):

// when window is resized
$(window).on("resize", function() {
    // if width is less than 480px
    if ($(this).width() < 480) {
        // reorder elements
        var wrapper = $('.grid'), 
            items = wrapper.children('.bear'),
            arr = [11, 8, 10, 7, 9, 6];

        wrapper.append( $.map(arr, function(v){ return items[v] }) );
    }
});

You'd have to be sure to add each new bear to this script if your layout is updated, so if bears are added frequently this could be annoying.

Display a Separate Grid on Mobile

You could also create an entirely a separate grid element for mobile. Then, on mobile, you could hide the desktop grid and display the mobile grid:

/* hide mobile grid by default */
.grid--mobile {
  display: none;
}

@media only screen and (max-width: 480px) {
  /* display mobile grid */
  .grid--mobile {
    display: block;
  }
  
  /* hide desktop grid */
  .grid--desktop {
    display: none;
  }
}
<div class="grid grid--desktop">
    <div class="col">
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
    </div>
    <div class="col">
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
    </div>
    <div class="col">
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
    </div>
</div>

<div class="grid grid--mobile">
    <div class="col">
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
        <div class="bear">...</div>
    </div>
</div>

This is, however, redundant as it would result in repeating the same exact elements twice in your HTML.

mmshr
  • 937
  • 1
  • 7
  • 9
0

Maybe you can play around with column spans using the grid-auto-flow: dense property. This property tells the grid how it should distribute the items on its space.

What must be kept in mind is that depending on the specified size of each item they may start flowing out of order.

dense

Is a keyword specifying that the auto-placement algorithm uses a “dense” packing algorithm, which attempts to fill in holes earlier in the grid, if smaller items come up later. This may cause items to appear out-of-order, when doing so would fill in holes left by larger items.

Further information here: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-gap: 20px;
  grid-auto-flow: dense;
}

.grid-item {
  border: 1px solid black;
}

.item-2 {
  grid-row: span 2;
}

.item-3 {
  grid-row: span 3;
}

.item-4 {
  grid-row: span 2;
}

.item-6 {
  grid-row: span 3;
}

.item-5 {
  grid-row: span 3;
}
<div class="grid">
  <div class="grid-item item-1">1</div>
  <div class="grid-item item-2">2</div>
  <div class="grid-item item-3">3</div>
  <div class="grid-item item-4">4</div>
  <div class="grid-item item-5">5</div>
  <div class="grid-item item-6">6</div>
</div>
IvanS95
  • 5,364
  • 4
  • 24
  • 62
  • This works if I set the `grid-row` property manually, but I want them to size themselves dynamically based on the content inside of them. – mjkaufer Apr 08 '19 at 20:35
  • You're right; I'll research a bit to see if there's a way to do what you need, hopefully I'll find something good – IvanS95 Apr 08 '19 at 22:06