Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making flexbox wrapped items absorb vertical space [duplicate]

This is, in effect, the Pinterest layout. However, the solutions found online are wrapped in columns, which means the container inadvertently grows horizontally. That is not the Pinterest layout, and it does not work well with dynamically-loaded content.

What I want to do is have a bunch of images of fixed width and asymmetrical height, laid out horizontally but wrapping in a new row when the limits of the fixed-width container are met:

Can flexbox do this, or do I have to resort to a JS solution like Masonry?

like image 894
Guybrush Threepwood Avatar asked Dec 13 '25 00:12

Guybrush Threepwood


2 Answers

Flexbox is a "1-dimensional" layout system: It can align items along horizontal OR vertical lines.

A true grid system is "2-dimensional": It can align items along horizontal AND vertical lines. In other words, cells can span across columns and rows, which flexbox cannot do.

This is why flexbox has a limited capacity for building grids. It's also a reason why the W3C has developed another CSS3 technology, Grid Layout (see below).


In a flex container with flex-flow: row wrap, flex items must wrap to new rows.

This means that a flex item cannot wrap under another item in the same row.

enter image description here

Notice above how div #3 wraps below div #1, creating a new row. It cannot wrap beneath div #2.

As a result, when items aren't the tallest in the row, white space remains, creating unsightly gaps.

enter image description here

enter image description here

image credit: Jefree Sujit


column wrap Solution

If you switch to flex-flow: column wrap, flex items will stack vertically and a grid-like layout is more attainable. However, a column-direction container has three potential problems right off the bat:

  1. It expands the container horizontally, not vertically (like the Pinterest layout).
  2. It requires the container to have a fixed height, so the items know where to wrap.
  3. As of this writing, it has a deficiency in all major browsers where the container doesn't expand to accommodate additional columns.

As a result, a column-direction container may not be feasible in many cases.


Other Solutions

  • Add containers

    In the first two images above, consider wrapping items 2 and 3 in a separate container. This new container can be a sibling to item 1. Done.

    Here's a detailed example: Calculator keypad layout with flexbox

    One downside worth highlighting: If you're wanting to use the order property to re-arrange your layout (such as in media queries), this method may eliminate that option.

  • Desandro Masonry

    Masonry is a JavaScript grid layout library. It works by placing elements in optimal position based on available vertical space, sort of like a mason fitting stones in a wall.

    source: http://masonry.desandro.com/

  • How to Build a Site that Works Like Pinterest

    [Pinterest] really is a cool site, but what I find interesting is how these pinboards are laid out... So the purpose of this tutorial is to re-create this responsive block effect ourselves...

    source: https://benholland.me/javascript/2012/02/20/how-to-build-a-site-that-works-like-pinterest.html

  • CSS Grid Layout Module Level 1

    This CSS module defines a two-dimensional grid-based layout system, optimized for user interface design. In the grid layout model, the children of a grid container can be positioned into arbitrary slots in a predefined flexible or fixed-size layout grid.

    source: https://drafts.csswg.org/css-grid/

    Grid Layout example: CSS-only masonry layout but with elements ordered horizontally

like image 55
Michael Benjamin Avatar answered Dec 14 '25 13:12

Michael Benjamin


What you want can be achieved in 3 2 ways, CSS wise:

  1. flexbox: =

     .parent {
         display: flex;
         flex-direction: column;
         flex-wrap: wrap;
         max-width: {max-width-of-container} /* normally 100%, in a relative container */
         min-height: {min-height-of-container}; /* i'd use vh here */
     }
     .child {
         width: {column-width};
         display: block;
     }
    
  2. CSS columns = (this solution has the very neat advantage of built-in column-span - pretty handy for titles). The disadvantage is ordering items in columns (first column contains first third of the items and so on...). I made a jsFiddle for this.

     .parent {
         -webkit-columns: {column width} {number of columns}; /* Chrome, Safari, Opera */
         -moz-columns: {column width} {number of columns}; /* Firefox */
         columns: {column width} {number of columns};
     }
     .child {
          width: {column width};
     }
     /* where {column width} is usually fixed size 
      * and {number of columns} is the maximum number of columns.
      * Additionally, to avoid breaks inside your elements, you want to add:
      */
     .child {
         display: inline-block;
         -webkit-column-break-inside: avoid;
         page-break-inside: avoid;
         break-inside: avoid-column;
     }
    
  3. Masonry plugin

absolute positioning after calculating rendered item sizes, via JavaScript (masonry plugin).

like image 23
tao Avatar answered Dec 14 '25 14:12

tao



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!