Flexbox: 4 items per row

I'm using a flex box to display 8 items that will dynamically resize with my page. How do I force it to split the items into two rows? (4 per row)?

Here is a relevant snip:

(Or if you prefer jsfiddle - http://jsfiddle.net/vivmaha/oq6prk1p/2/)

.parent-wrapper {
  height: 100%;
  width: 100%;
  border: 1px solid black;
}
.parent {
  display: flex;
  font-size: 0;
  flex-wrap: wrap;
  margin: -10px 0 0 -10px;
}
.child {
  display: inline-block;
  background: blue;
  margin: 10px 0 0 10px;
  flex-grow: 1;
  height: 100px;
}
<body>
  <div class="parent-wrapper">
    <div class="parent">
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
      <div class="child"></div>
    </div>
  </div>
</body>

You've got flex-wrap: wrap on the container. That's good, because it overrides the default value, which is nowrap (source). This is the reason items don't wrap to form a grid in some cases.

In this case, the main problem is flex-grow: 1 on the flex items.

The flex-grow property doesn't actually size flex items. Its task is to distribute free space in the container (source). So no matter how small the screen size, each item will receive a proportional part of the free space on the line.

More specifically, there are eight flex items in your container. With flex-grow: 1, each one receives 1/8 of the free space on the line. Since there's no content in your items, they can shrink to zero width and will never wrap.

The solution is to define a width on the items. Try this:

.parent {
  display: flex;
  flex-wrap: wrap;
}

.child {
  flex: 1 0 21%; /* explanation below */
  margin: 5px;
  height: 100px;
  background-color: blue;
}
<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

With flex-grow: 1 defined in the flex shorthand, there's no need for flex-basis to be 25%, which would actually result in three items per row due to the margins.

Since flex-grow will consume free space on the row, flex-basis only needs to be large enough to enforce a wrap. In this case, with flex-basis: 21%, there's plenty of space for the margins, but never enough space for a fifth item.


Add a width to the .child elements. I personally would use percentages on the margin-left if you want to have it always 4 per row.

DEMO

.child {
    display: inline-block;
    background: blue;
    margin: 10px 0 0 2%;
    flex-grow: 1;
    height: 100px;
    width: calc(100% * (1/4) - 10px - 1px);
}

Here is another apporach.

You can accomplish it in this way too:

.parent{
  display: flex;
  flex-wrap: wrap;
}

.child{
  width: 25%;
  box-sizing: border-box;
}

Sample: https://codepen.io/capynet/pen/WOPBBm

And a more complete sample: https://codepen.io/capynet/pen/JyYaba


I would do it like this using negative margins and calc for the gutters:

.parent {
  display: flex;
  flex-wrap: wrap;
  margin-top: -10px;
  margin-left: -10px;
}

.child {
  width: calc(25% - 10px);
  margin-left: 10px;
  margin-top: 10px;
}

Demo: https://jsfiddle.net/9j2rvom4/


Alternative CSS Grid Method:

.parent {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 10px;
  grid-row-gap: 10px;
}

Demo: https://jsfiddle.net/jc2utfs3/