Spacing between flexbox items
Asked Answered
A

9

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?

Absa answered 2/5, 2014 at 17:5 Comment(1)
CSS Grid Layout solution: https://mcmap.net/q/45216/-how-do-i-set-distance-between-flexbox-itemsSweatshop
H
52

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).

Hoad answered 10/8, 2019 at 21:17 Comment(2)
The Safari bug is resolved and support was shipped with 14.1. Finally! :o)Stibine
You can also use row-gap and column-gap respectively.Retire
S
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; }
Stibine answered 2/5, 2014 at 17:13 Comment(5)
..but then there is a margin on the right side of the last element: jsfiddle.net/kWkmxParliamentary
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.Stibine
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.Interclavicle
But how do you avoid horizontal scrollbars for containers with overflow: auto?Underpants
@Underpants 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?Stibine
B
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);
}
Ben answered 18/3, 2017 at 14:32 Comment(0)
E
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; }
Engen answered 23/4, 2015 at 9:47 Comment(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 <li> elements, or anything else.Beverleebeverley
M
7

Another solid point for using Flex is that you can specify the strategy to use for the remaining space:

.container {
    justify-content: space-between;
}
Mina answered 20/9, 2017 at 17:14 Comment(0)
B
4

EDIT - I don't suggest using this approach, it's hackish. I'll leave it here for posterity.

What I did to approach this, since I wasn't sure how many elements I'd have within each flex space. For example, I am building a Drupal theme and have four regions that align side-by-side, but I want the full width to be taken up, even if there is content in only three of the regions.

  • Gave each region a padding of 10px
  • Set the background colour of each region to match the theme background colour - in my case white
  • Created a div inside each region (to create the illusion of a margin between them.

HTML looks like this:

<div class="flexy">
    <div class="region">
       <div class="region-inner">
       </div>
    </div>
</div>

CSS looks like this:

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

.flexy .region {
    box-sizing: border-box;
    flex-grow: 1;
    flex-basis: 0;
    padding: 10px;
}

This leaves me with a layout like so (ignore the ugliness, the colours are only to demonstrate): Multi-region layout with spacing between items

There are some other classes added such as 'halves', 'thirds', and 'quarters' to help out making things responsive on smaller and/or larger screens.

Brownstone answered 22/3, 2016 at 21:19 Comment(2)
but your content doesn't meet container bordersSurbeck
See the note at the top of my answer.Brownstone
U
2

The desired layout can be achieved using a wrapper div with negative margin

.flex-wrapper{
    margin: -20px;
}

Working code http://jsfiddle.net/8cju5jpd/

Unlookedfor answered 15/11, 2016 at 4:54 Comment(1)
This does not work as expected when using flex flow instead of manually defining the rowsBen
A
2

Trying to stick on your question:

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

I would say no, the same way you cannot style an element based on the value of, let's say, its background color. To do so, you could write custom classes to set the flex order and then subclass based on them.

Please check out this interesting thread where I posted my solution on spacing: Better way to set distance between flexbox items

Astaire answered 8/8, 2018 at 6:14 Comment(0)
Y
1

is it bad to create an dummy div / View for spacing?

<Item style={{flex:1}} />
<View style={{width: 10}}
<Item style={{flex:1}} />
Yestreen answered 12/10, 2019 at 4:53 Comment(1)
It's not against the rules, but it's bad practice to use markup to define styling. Sometimes you need to do it. But there's CSS methods to achieving that and separate content from styling.Discountenance

© 2022 - 2024 — McMap. All rights reserved.