4

When using space-around as a param to justify-content, it will space the items evenly within the container. What I'd like is for all the items to start (flex-start) on the left but have the same margin that space-around would offer.

I think this shows the issue:

.container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  width: 100%;
  border: 1px solid red;
}

.item {
  flex: 0 0 16%; /*6 items per line leaving some for margin*/
  border: 1px solid black;
  box-sizing: border-box;
}
<div class="container">
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
</div>

What I'd like is for the second line to wrap and take up the first 2 columns. I know I could specify a padding on the .item class and set the flex prop to flex-start but I can't always be 100% sure of the size the padding should be as it's scaling for multiple devices.

This can be demonstrated like so:

.container {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  width: 100%;
  border: 1px solid red;
}

.item {
  flex: 0 0 16%; /*6 items per line leaving some for padding*/
  border: 1px solid black;
  box-sizing: border-box;
  margin-right: 0.66%
}
<div class="container">
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
</div>

The overall spacing of the items between the red border isn't right and again, without knowing specific values, I don't know how I can set it to be so.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
webnoob
  • 15,747
  • 13
  • 83
  • 165
  • Are you looking for `justify-content: space-between` ? – Huangism Jan 29 '18 at 14:41
  • No because in my first example, that would make the 2 items on the second line go to one in the first column and one in the last column. – webnoob Jan 29 '18 at 14:42
  • https://www.youtube.com/watch?v=0e02dl66PYo this is really good to help understand what you can do with flex box's – Bradley Coupland Jan 29 '18 at 14:44
  • @BradleyCoupland Does it cover my specific scenario (its a 20 min vid)? There are a lot of resources out there explaining flexbox but I've not found anything covering this specific scenario. – webnoob Jan 29 '18 at 14:48
  • This is the limitation of flex, if you want the second row (assuming dynamic amount of items) to align to the left then it's best to specify the margins yourself. Alternatively (which is what I have done at work and our dev team agreed to this), you can add empty items so that each row always have 6(or n number) items. So that it would align properly all the time. This has been asked here https://stackoverflow.com/questions/16377972/how-to-align-left-last-row-line-in-multiple-line-flexbox – Huangism Jan 29 '18 at 14:55
  • @Huangism Ok fair enough, I thought that might be the case and kind of expected that to be the case. With flex being so powerful, it's hard when it doesn't do **everything** you want to it to :) – webnoob Jan 29 '18 at 15:01
  • @webnoob the newer css grid solves this issue but if you are using flex, adding empty container is the best way to go, avoid declaring margins so it won't be an issue when changing width of the items. You can declare css for each media query range you have so that the extra containers can be hidden at each range if needed – Huangism Jan 29 '18 at 15:09
  • @Huangism Yes, thanks I think that is the route I'll need to go down. Grid does sound interesting (I've not looked into it much - I'm a back-end guy :) ). As with most new and shiny CSS, I assume it's support is lacking for production :) If you add an answer explaining the column solution, I'll happily accept. – webnoob Jan 29 '18 at 15:13

3 Answers3

4

One can, as mentioned, add ghost elements to fill the last row, but in this case, one doesn't need to pollute the markup with unused elements, just use the nth-child selector and simply calculate the needed left margin on all items but the first in each row.

The calculation is done by remove the sum of the items width (16% * 6)) from the parent's full width (100% -, and then divide what's left with the amount of gaps / 5)

.item:not(:nth-child(6n+1)) will select all items but every 6th, starting from the 1st (e.g. 1st, 7th, 13th, and so on).

If the item count in each row will change, use a media query to adjust the 6n+1 selector and calculation values.

Stack snippet

.container {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  border: 1px solid red;
}

.item {
  flex: 0 0 16%; /*6 items per line leaving some for margin*/
  border: 1px solid black;
  box-sizing: border-box;
}

.item:not(:nth-child(6n+1)) {
  margin-left: calc( (100% - (16% * 6)) / 5 );          /*  added  */
}
<div class="container">
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
</div>
Asons
  • 84,923
  • 12
  • 110
  • 165
3

Unfortunately this is the limitation of flex

Work around

You can add empty flex items so that each row always have 6 (or n number) of items. It's extra html however this avoids you setting margins at different width for responsive design.

<div class="container">

  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item">ITEM</div>

  <div class="item">ITEM</div>
  <div class="item">ITEM</div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>

</div>

in your media query ranges you can define different width for your items and create a hide item class so that at that width it will be hidden.

An example from my work

we have a 12 column grid for large and medium, 6 column grid for small and 4 columns for phone. it's using a Freemarker macro but this is just to show you how to set it up, the below represents 1 row on any width that we support

<div class="<@grid large="1" medium="11" small="5" phone="0" />"></div>
<div class="<@grid large="11" medium="1" small="1" phone="4" />"></div>

The final output of the above is

<div class="large-col-1 medium-col-11 small-col-5 phone-col-0"></div>
<div class="large-col-1 medium-col-1 small-col-1 phone-col-4"></div>

Each of these classes are defined within their own media query range only.

You could use css grid to solve this but check browser support first

Huangism
  • 16,278
  • 7
  • 48
  • 74
1

Can't be done with flexbox, at least not without ultra specific hacks: some common approaches are javascript spaghetti or empty divs / pseudo-elements on the container, but all of those are really fragile and far from ideal for a dynamic context.

But you can go with a flex-like grid instead, using autofit minmax if you want the items to stretch and keep a constant grid-spacing

.grid{
  /*sets the container as a grid with variable number of columns*/
  display:grid;
  grid-template-columns: repeat(auto-fill, minmax(160px,1fr));
  grid-gap:1em;
}

https://codepen.io/facundocorradini/pen/XVMLEq

or just add a fixed width to the elements, and maybe even a justify-items: center if you want them space-around style. Examples at the linked pen.

The above examples have a auto-adaptable number of columns, but if you want it fixed at 16%, just declare the columns as such.

A good strategy would be to use the grid approach and let flexbox handle the fallback for browsers that don't support it. Luckily grid is extremely smart at overriding the fallbacks, so should be easy enough.

Facundo Corradini
  • 3,825
  • 9
  • 24