This question already has answers here:
Closed 2 years ago.
I want to use flexbox to set-up a simple elastic* grid...
.container{
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
...but I want the outer margins to be equal to the space between the items.
*By "elastic" I mean that the widths not fixed. They are all percentages, and so different total widths at different screen widths.
This does not appear to be the default behavior of justify-content: space-between
. It generates equal space "around" each item but the space does not collapse the way margins do. So the result is that the "between" spaces are twice as wide as the far right and left (assuming you're flex-direction is row
).
In the sketch below, the first diagram is what justify-content: space-between
does naturally. Notice widths A and B. The second diagram is what I want (only one consistent gutter C).
Is this possible with flexbox?
Update
I would like to avoid having to add "row" wrapping elements if possible. The pseudo element solution sounds like just the ticket (see answers below), but let's work it out so that the items can be of an undetermined number (and wrap onto new rows)
You can use space-between
with pseudo elements from the parent
.flex {
display: flex;
width: 500px;
border: 1px solid black;
justify-content: space-between;
}
.flex > div {
background: #eee;
border: 1px solid #aaa;
height: 100px; width: 100px;
}
.flex:before,.flex:after {
content: '';
}
<div class="flex">
<div></div>
<div></div>
<div></div>
</div>
How about using 0 width pseudo elements before and after the items?
.container{
display: flex;
flex-wrap: wrap;
justify-content: space-between;
border: 1px solid #C55;
}
.column {
flex: 1 20%;
max-width: 20%;
height: 100px;
background: #C55;
}
.even:before, .even:after{
content: '';
display: block;
width: 0;
}
no side spacing:
<div class="container">
<div class="column"></div>
<div class="column"></div>
<div class="column"></div>
</div>
<br>
side spacing:
<div class="container even">
<div class="column"></div>
<div class="column"></div>
<div class="column"></div>
</div>
A couple of pseudo-elements will accomplish that:
flex-container {
display: flex;
justify-content: space-between;
background-color: orangered;
}
flex-item {
flex: 0 0 20%;
height: 100px;
margin: 5px 0;
background-color: lightyellow;
}
flex-container::after {
content: '';
}
flex-container::before {
content: '';
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
Most, if not all, browsers interpret pseudo-elements on a flex container to be flex items. The ::before
pseudo is the first item, and the ::after
is the last.
Here's a reference from Firefox documentation:
In-flow ::after
and ::before
pseudo-elements are now flex
items
(bug 867454).
The code above works because justify-content: space-between
now factors in five flex items, but the first and last have 0 width.
I'm not sure this can be made to work in a multi-line container (flex-wrap: wrap
) with pure CSS. You can try using auto
margins on flex items instead of justify-content
on the container. Otherwise, JavaScript.
You can avoid the use of pseudos using margins to position the elements.
The first element is a special case, and needs a margin-left; all the others have only margin-right.
But this solution won't work with wrapping enabled. (There is no way to target the first element of the second and following rows)
.flex {
display: flex;
width: 500px;
border: 1px solid black;
}
.flex > div {
background: lightblue;
height: 100px;
width: 100px;
margin-right: auto;
}
.flex > div:first-child {
margin-left: auto;
}
<div class="flex">
<div></div>
<div></div>
<div></div>
</div>
Calculate the difference and add a margin-left
to the left element and a margin-right
to the right element. Though I know you wanted an HTML/CSS solution, you can do this with JavaScript and make it perfectly responsive or just feel out the margin sizes with HTML/CSS.
HTML
<div class="box" id="element"></div>
<div class="box"></div>
<div class="box"></div>
JS
window.addEventListener('load', setMargins);
var boxClass = document.getElementsByClassName('box');
function setMargins() {
var element = document.getElementById('element');
var widthOfElement = element.clientWidth;
var sizeOfEachMargin = (window.innerWidth - (widthOfElement * 3)) / 4;
for (var i = 0; i < boxClass.length; i++) {
boxClass[i].style.marginLeft = sizeOfEachMargin + 'px';
}
}