0

WANTED

I'd like to have a grid layout. A bit like Pinterest, except the items have fixed height (so it should be a lot easier).

It should be responsive. The number of colums should be set according to the viewport size.

The DOM should be a simple list of childs inside a container. I don't want a wrapper element for each row. What I want is this:

<div class="container">
    <div class="item">a</div>
    <div class="item">b</div>
    <div class="item">c</div>
    <div class="item">d</div>
    <div class="item">e</div>
    <div class="item">f</div>
    <div class="item">g</div>
    <div class="item">h</div>
    <div class="item">i</div>
</div>

The ordering of items matter, so the "b" item of the DOM should be on the right of the "a" item (and not under).

layout

MY ATTEMPT

I have done this layout using flexbox but it feels quite hacky as I have to compute some margins with maths. Also I don't understand how to control the vertical space between my flexbox rows, as it seems to grow with the container height.

Here is my LESS mixin of the Codepen above:

.flexboxGridMixin(@columnNumber,@spacingPercent) {
  @contentPercent: 100% - @spacingPercent;
  @sideMargin: @spacingPercent/(@columnNumber*2);
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
  > * {
    box-sizing: border-box;
    width: (100% - @spacingPercent)/@columnNumber;
    margin-left: @sideMargin;
    margin-right: @sideMargin;
  }
}

It seems doing math like that somehow defeat the initial purpose of flexbox no?

EXPECTED ANSWER

So, can someone explain me how to fix my flexbox layout? Or tell me what is the best, most elegant and modern way to solve this layout problem?

I don't want to use floats, probably not inline-block, nor JS based solution like Packery/Masonry

Please use modern CSS in your answers because I already know how to solve this with plain old CSS / JS. I target modern browsers only (to be defined)


Solution

Here is my final result, for those interested to reuse this layout.

Also a very nice visual Flexbox tutorial here: https://scotch.io/tutorials/a-visual-guide-to-css3-flexbox-properties

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419

1 Answers1

2

I have done this layout using flexbox but it feels quite hacky as I have to compute some margins with maths.

Yes, I think you are using more code than necessary, and not taking advantage of several flexbox features. Your desired layout can be achieved simply and efficiently with flex properties.

HTML (removed unnecessary outer container in codepen)

<ul>
    <li>a</li>
    <li>b</li>
    <li>c</li>
    <li>d</li>
    <li>e</li>
    <li>f</li>
    <li>g</li>
    <li>h</li>
    <li>i</li>
    <li>j</li>
    <li>k</li>
</ul>

CSS (I compiled your CSS for people who don't use preprocessors)

html, body { height: 100%; }

ul {
    display: flex;
    flex-wrap: wrap;
    height: 100%;
    list-style: none;
    padding: 0;
    align-content: flex-start; /* solves vertical margin problem (see note below) */
}

li {
    flex: 0 1 200px; /* don't grow, shrink proportionally, start at 200px width */
    background: pink;
    height: 100px;
    line-height: 100px; /* for demo only: center text vertically */
    text-align: center; /* for demo only: center text horizontally */
    box-sizing: border-box;
    margin: 15px;
}

DEMO: http://jsfiddle.net/mk8uksb2/

NOTE: In the original code, the space between rows was contracting or expanding relative to the viewport height because when a flexbox is created, a default rule is align-content: stretch. This means that the layout will expand to fill the size of the container. In the modified code, I override the default with align-content: flex-start, which packs the rows at the start of the container.

You were trying to meet several requirements for your grid. I think the modified code covers them all:

  • I'd like to have a grid layout.
  • ... the items have fixed height.
  • It should be responsive.
  • The number of colums should be set according to the viewport size.
  • The DOM should be a simple list of childs inside a container.
  • I don't want a wrapper element for each row.
  • The ordering of items matter, so the "b" item of the DOM should be on the right of the "a" item.
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 1
    thanks a lot @Michael_B . This is not exactly what I was looking for but gave me the inspiration to do it: http://jsfiddle.net/mk8uksb2/2/ – Sebastien Lorber Oct 28 '15 at 18:29
  • by chance do you know how to do the same thing with non-fixed height items? – Sebastien Lorber Oct 29 '15 at 17:49
  • also do you know how to make so that my boxes keep the same width/height ratio? I've tried this but it does not exactly work... http://jsfiddle.net/mk8uksb2/7/ – Sebastien Lorber Oct 29 '15 at 18:27
  • re: same width/height ratio: For your particular code, I would say pick *one* value for `flex-basis`, and use it in all your media queries: http://jsfiddle.net/mk8uksb2/10/ – Michael Benjamin Oct 30 '15 at 01:05
  • I know you're using media queries, but there's another way to maintain the aspect ratio without any media queries: [**viewport percentage units**](http://stackoverflow.com/a/32174347/3597276). demo: http://jsfiddle.net/mk8uksb2/9/ – Michael Benjamin Oct 30 '15 at 01:05
  • see also: [How to maintain the aspect ratio of a div using only CSS](http://stackoverflow.com/q/1495407/3597276) – Michael Benjamin Oct 30 '15 at 01:05
  • 1
    thanks, I have been able to get my final result :) http://jsfiddle.net/mk8uksb2/21/ – Sebastien Lorber Oct 30 '15 at 11:06