84

This is what I want:

flex with spacing

But this is the closest I've got:

body{
    margin: 0;
    padding: 0;
    border: 1px solid red;
}

.flex{
  display: -ms-flexbox;    
  display: -webkit-box;    
  display: -webkit-flexbox; 
  display: -webkit-flex;
  display: flex;            
}    

.flex > *{ margin: 0 10px; }    
.flex > :first-child{ margin-left: 0; }
.flex > :last-child{ margin-right: 0; }

.flex.vertical > *{ margin: 10px 0; }    
.flex.vertical > :first-child{ margin-top: 0; }
.flex.vertical > :last-child{ margin-bottom: 0; }

.vertical{
      -webkit-box-orient: vertical;    
         -moz-box-orient: vertical;    
              box-orient: vertical;    
  -webkit-flex-direction: column;
     -moz-flex-direction: column;
      -ms-flex-direction: column;
          flex-direction: column;      
}

.box{
  background: #000;
  flex: auto;
  min-height: 100px;
}
<div class="flex vertical">    
  <div class="flex">
    <div class="box"> </div>    
    <div class="box"> </div>                
  </div>   
  <div class="flex">    
    <div class="box"> </div>    
    <div class="box"> </div>                
    <div class="box"> </div>              
  </div>   

  <div class="flex">
    <div class="box"> </div>    
    <div class="box"> </div>                
  </div>           
</div>

I'm applying a margin on flexbox items, then removing half of it from the first & last children.

The problem is that :first-child is not always the first visually, because I may alter the layout order using flexbox ordering utilities. For example:

.flex > *{
  -webkit-box-ordinal-group: 2;
     -moz-box-ordinal-group: 2;
             -ms-flex-order: 2;
              -webkit-order: 2;
                      order: 2;    
}

#important{
  -webkit-box-ordinal-group: 1;
     -moz-box-ordinal-group: 1;
             -ms-flex-order: 1;
              -webkit-order: 1;
                      order: 1;
}

body{
    margin: 0;
    padding: 0;
    border: 1px solid red;
}

.flex{
  display: -ms-flexbox;    
  display: -webkit-box;    
  display: -webkit-flexbox; 
  display: -webkit-flex;
  display: flex;            
}    

.flex > *{ margin: 0 10px; }    
.flex > :first-child{ margin-left: 0; }
.flex > :last-child{ margin-right: 0; }

.flex.vertical > *{ margin: 10px 0; }    
.flex.vertical > :first-child{ margin-top: 0; }
.flex.vertical > :last-child{ margin-bottom: 0; }

.vertical{
      -webkit-box-orient: vertical;    
         -moz-box-orient: vertical;    
              box-orient: vertical;    
  -webkit-flex-direction: column;
     -moz-flex-direction: column;
      -ms-flex-direction: column;
          flex-direction: column;      
}

.box{
  background: #000;
  flex: auto;
  min-height: 100px;
}
<div class="flex vertical">    
  <div class="flex">
    <div class="box"> </div>    
    <div class="box"> </div>                
  </div>   
  <div class="flex">    
    <div class="box"> </div>    
    <div class="box" id="important"> </div>                
    <div class="box"> </div>              
  </div>   

  <div class="flex">
    <div class="box"> </div>    
    <div class="box"> </div>                
  </div>           
</div>

Is there a way to take the visual order into account when applying the margin?

miken32
  • 42,008
  • 16
  • 111
  • 154
nice ass
  • 16,471
  • 7
  • 50
  • 89

9 Answers9

50

The CSS spec has recently been updated to apply gap properties to flexbox elements in addition to CSS grid elements. This feature is supported on the latest versions of all major browsers. With the gap property, you can get what you want with just gap: 10px (or whatever size you want).

neurodynamic
  • 4,294
  • 3
  • 28
  • 39
24

You can try setting the same margin for all the boxes, and then revert this on the container:

So replace this:

.flex > * { margin: 0 10px; }    
.flex > :first-child { margin-left: 0; }
.flex > :last-child { margin-right: 0; }

.flex.vertical > :first-child { margin-top: 0; }
.flex.vertical > :last-child { margin-bottom: 0; }

With this:

.flex.vertical { margin: -20px 0 0 -20px; }
.flex > * { margin: 0 0 0 20px; }
.flex.vertical > * { margin: 20px 0 0 0; }
agrm
  • 3,735
  • 4
  • 26
  • 36
  • 3
    ..but then there is a margin on the right side of the last element: http://jsfiddle.net/kWkmx/ – Josh Crozier May 02 '14 at 17:14
  • Updated my answer. In the last suggestion margins are only added to the top and to the left. Then these are reverted with corresponding negative margins on the container. – agrm May 02 '14 at 17:18
  • 5
    Isn't that "cheating"? The question was for one flexbox, not creating multiple ones. The flexbox is supposed to be responsible for arranging items on a new column or row itself. On a dynamic website, you'd have to calculate how many go into one row or column first, before you could render the HTML using this approach. Imo it's not satisfying. – Zelphir Kaltstahl Dec 27 '15 at 16:25
  • But how do you avoid horizontal scrollbars for containers with overflow: auto? – jbyrd May 03 '19 at 14:34
  • @jbyrd I'm not sure why this would cause a horizontal scrollbar. Do you have images or fixed width contents in the inner boxes preventing them from shrinking or flexing. Do you have an example? – agrm May 19 '19 at 13:12
9

While Rejoy answer works perfectly, it's not responsive-ready, because the rows are locked.

flex-flow is your new friend. However, flex is not without bugs. The negative margin trick we know from various grid framework does work, unless you are on IE, where the elements get wrapped too early because it uses content-box as box-size. But there is an easy workaround.

Working example: https://jsfiddle.net/ys7w1786/

.flex {
  display: flex;  
  flex-direction: row; /* let the content flow to the next row */
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: flex-start;
  margin: -4px -4px; /* grid trick, has to match box margin */
}

The boxes come with flex-basis: auto, because of IE. But we can simply use width instead:

.box {
    flex: 0 0 auto; /* auto is important because of an IE bug with box-size */
    height: 100px;
    display: inline-block;
    background-color: black;
    margin: 4px; /* spacing between boxes, matches parent element */
}

.s1-2{
  width: calc(50% - 8px); 
}
.s1-4{
  width: calc(25% - 8px);
}
Community
  • 1
  • 1
dube
  • 4,898
  • 2
  • 23
  • 41
7

here is another way of getting the same thing.

.vertical > div{ margin-bottom: 10px; }
.vertical > div:last-child{ margin-bottom: 0; }
.box + .box{ margin-left: 10px; }
galki
  • 8,149
  • 7
  • 50
  • 62
  • 1
    Can you explain the benefits of this over @agrm's answer? With `div:not(:first-child)`, instead of `div + div`, it wouldn't matter whether they were within
  • elements, or anything else.
  • – Michael Scheper Apr 28 '15 at 01:19