ChatGPT解决这个技术问题 Extra ChatGPT

Better way to set distance between flexbox items

To set the minimal distance between flexbox items I'm using margin: 0 5px on .item and margin: 0 -5px on container. For me it seems like a hack, but I can't find any better way to do this.

#box { display: flex; width: 100px; margin: 0 -5px; } .item { background: gray; width: 50px; height: 50px; margin: 0 5px; }

It's not a hack - it's one of the intended methods for aligning items. There are other properties though. See w3.org/TR/css3-flexbox/#alignment
Yeah, I'm understand. But for example there is column-gap property what gives us ability to control distance from container: w3.org/TR/css3-multicol/#column-gap
Of cause it is a margin collapses of the flexbox.The other question have the right awser of [How can I stop the last margin collapsing in flexbox? ](stackoverflow.com/questions/38993170/…)
The CSS Box Alignment Module Level 3 includes a section on gaps between boxes - which applies to multi-column elements, flex containers, and grid containers. So eventually this will be simple as: row-gap: 5px - done.
This is my go-to reference for flexbox: css-tricks.com/snippets/css/a-guide-to-flexbox Hope you figure it out!

g
guettli

Flexbox doesn't have collapsing margins.

Flexbox doesn't have anything akin to border-spacing for tables (edit: CSS property gap fulfills this role in newer browsers, Can I use)

Therefore achieving what you are asking for is a bit more difficult.

In my experience, the "cleanest" way that doesn't use :first-child/:last-child and works without any modification on flex-wrap:wrap is to set padding:5px on the container and margin:5px on the children. That will produce a 10px gap between each child and between each child and their parent.

Demo

.upper { margin: 30px; display: flex; flex-direction: row; width: 300px; height: 80px; border: 1px red solid; padding: 5px; /* this */ } .upper > div { flex: 1 1 auto; border: 1px red solid; text-align: center; margin: 5px; /* and that, will result in a 10px gap */ } .upper.mc /* multicol test */ { flex-direction: column; flex-wrap: wrap; width: 200px; height: 200px; }

aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa
aaa


This doesn't accomplish the same thing as the question asks, you will have a 10px indent on the far left and right, which I'm assuming they don't intend to have. Hence, the negative margins in the original question.
What about if order property set? :first-child/:last-child will not work as expected.
“Flexbox doesn't have collapsing margins.” Very insightful, and apparently true, but may I ask for a citation?
Isn't this a worse answer than the original question? This method requires that you have space around the container and the guttering must always be an even number.
@chharvey, from the spec w3.org/TR/css-flexbox-1/#item-margins, "The margins of adjacent flex items do not collapse."
F
Flimm

CSS gap property:

There is a new gap CSS property for multi-column, flexbox, and grid layouts that works in newer browsers now! (See Can I use link 1; link 2). It is shorthand for row-gap and column-gap.

#box {
  display: flex;
  gap: 10px;
}

CSS row-gap property:

The row-gap CSS property for both flexbox and grid layouts allows you to create a gap between rows.

#box {
   display: flex;
   row-gap: 10px;
}

CSS column-gap property:

The column-gap CSS property for multi-column, flexbox and grid layouts allows you to create a gap between columns.

#box {
  display: flex;
  column-gap: 10px;
}

Example:

#box { display: flex; flex-wrap: wrap; width: 200px; background-color: red; gap: 10px; } .item { background: gray; width: 50px; height: 50px; border: 1px black solid; }


It doesn't work on my Windows 10 + Chrome 90.0.4430. It acts like margin and the fourth element go to a new line. Please check a screenshot of your own snippet on my display i2.paste.pics/a1f42d884cb5a64ddf80632e87178869.png
@MarcoPanichi the result you are seeing is the intended result. Notice how there is no red margin before the first item in each line. If you don't want wrapping, you can turn it off: flex-wrap: nowrap. You also might want to try: justify-content: space-between
One limitation I am facing with this approach is that, if there are two columns that require different gap than rest of the columns, then you cannot achieve it.
@sallushan Flex layouts don't have columns per se. If you need features like different gaps for different columns, then I recommend CSS grid, not flex.
I wouldn´t use "gap" on flex box (correct me if im wrong, but it´s more suited for display: grid) since it makes columns wrap prematurely. Better to stick to old margin/padding solution. Example: jsfiddle.net/wmtz8dch/1
R
Roumelis George

This is not a hack. The same technique is also used by bootstrap and its grid, though, instead of margin, bootstrap uses padding for its cols.

.row {
  margin:0 -15px;
}
.col-xx-xx {
  padding:0 15px;
}

The only issue with this method are maintaining equal height items with background colors. Absolute positioning with height:100%; width:100% ignores the item's padding.
The problem here is with IE10 and 11. flex-basis values do not account for box-sizing: border-box, so a child with any padding or border will overflow the parent (or wrap in this case). Source
There is another problem with this approach: adjusting the margin like this can expand the page width. Demo: jsfiddle.net/a97tatf6/1
Though I agree this is not a hack, the fact that something is widely used does not mean it is not a hack. See polyfills, temporary security patches, hex editing, etc etc
Of course this is a hack. Because Bootstrap uses this method doesn't mean it's not a hack. It just means that Bootstrap uses a hack.
D
Dariusz Sikorski

Flexbox and css calc with multiple rows support

Hello, below is my working solution for all browsers supporting flexbox. No negative margins.

Fiddle Demo

.flexbox { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; } .flexbox > div { /* 1/3 - 3 columns per row 10px - spacing between columns */ box-sizing: border-box; margin: 10px 10px 0 0; outline: 1px dotted red; width: calc(1/3*100% - (1 - 1/3)*10px); } /* align last row columns to the left 3n - 3 columns per row */ .flexbox > div:nth-child(3n) { margin-right: 0; } .flexbox::after { content: ''; flex: auto; } /* remove top margin from first row -n+3 - 3 columns per row */ .flexbox > div:nth-child(-n+3) { margin-top: 0; }

col
col
col
col
col

Take a note this code can be shorter using SASS

Update 2020.II.11 Aligned columns on the last row to the left

Update 2020.II.14 Removed margin-bottom in the last row


I like this solution but it fails if there are only 2 items in the last row. The items aren't stacked together due to the justify-content.
to fix the two item issue just change to justify-content: space-evenly; or justify-content: space-around;.
@PaulRooney On site with multiple lists you might not always know the number of items, if the lists are generated by a CMS.
Unfortunately this solution isn't responsive, whereas the negative margins one is.
T
TylerH

You can use & > * + * as a selector to emulate a flex-gap (for a single line):

#box { display: flex; width: 230px; outline: 1px solid blue; } .item { background: gray; width: 50px; height: 100px; } /* ----- Flexbox gap: ----- */ #box > * + * { margin-left: 10px; }

If you need to support flex wrapping, you can use a wrapper element:

.flex { display: flex; flex-wrap: wrap; } .box { background: gray; height: 100px; min-width: 100px; flex: auto; } .flex-wrapper {outline: 1px solid red; } /* ----- Flex gap 10px: ----- */ .flex > * { margin: 5px; } .flex { margin: -5px; } .flex-wrapper { width: 400px; /* optional */ overflow: hidden; /* optional */ }


One point to note though is that the * operator does not increase specificity as you might expect. So depending on your exisiting css you may need to either increase the specificity on the .flex > * selector or add !important to the rule if there are other selectors applying margin to that element.
this won't work if you have that buttons width:100% on mobile
T
Tracker1

You can use transparent borders.

I have contemplated this issue while trying to build a flex grid model which can fallback to a tables + table-cell model for older browsers. And Borders for column gutters seemed to me the best appropriate choice. i.e. Table-cells don't have margins.

e.g.

.column{
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 10px solid transparent;
}

Also note that you need min-width: 50px; for flexbox. The flex model will not handle fixed sizes unless you do flex: none; on the particular child element you want as fixed and therefore excluded from being "flexi". http://jsfiddle.net/GLpUp/4/ But all columns together with flex:none; is no longer a flex model. Here is something closer to a flex model: http://jsfiddle.net/GLpUp/5/

So you can actually use margins normally if you don't need the table-cell fallback for older browsers. http://jsfiddle.net/GLpUp/3/

Setting background-clip: padding-box; will be necessary when using a background, as otherwise the background will flow into the transparent border area.


great answer. margins are used differently in flexbox's ( like to absorb extra space ) so transparent borders provide an excellent solution to evenly spaced elements that can wrap with a margin-like behavior
except when you use background color, your background exceeds your desired bounds.
@ahnbizcad Well with different background colors, you can use white or the adequate color depending on which way is the background.
@ahnbizcad: If you don't need IE8 support, this is a better solution: background-clip: padding-box
Albin's comment here needs more votes! This is the best solution. Transparent borders, in combination with background-clip: padding-box (and negative margins on the container, if needed, for proper edge alignment) is a perfect solution. IE8 doesn't support flexbox anyway so its lack of support for background-clip shouldn't matter.
T
TylerH

This solution will work for all cases even if there are multiple rows or any number of elements. But the count of the section should be same you want 4 in first row and 3 is second row it won't work that way the space for the 4th content will be blank the container won't fill.

We are using display: grid; and its properties.

#box { display: grid; width: 100px; grid-gap: 5px; /* Space between items */ grid-template-columns: repeat(4,1fr); /* Decide the number of columns(4) and size(1fr | 1 Fraction | you can use pixels and other values also) */ } .item { background: gray; width: 100%; /* width is not necessary only added this to understand that width works as 100% to the grid template allocated space **DEFAULT WIDTH WILL BE 100%** */ height: 50px; }

The Downside of this method is in Mobile Opera Mini will not be supported and in PC this works only after IE10.

Note for complete browser compatability including IE11 please use Autoprefixer

OLD ANSWER Don't think of it as an old solution, it's still one of the best if you only want single row of elements and it will work with all the browsers.

This method is used by CSS sibling combination, so you can manipulate it many other ways also, but if your combination is wrong it may cause issues also.

.item+.item{
  margin-left: 5px;
}

The below code will do the trick. In this method, there is no need to give margin: 0 -5px; to the #box wrapper.

A working sample for you:

#box { display: flex; width: 100px; } .item { background: gray; width: 22px; height: 50px; } .item+.item{ margin-left: 5px; }


I've edited your grid-based answer to demonstrate that it does work correctly, even if the final row has fewer items. To prove it works in that case. (Feel free to revert if you don't like this change.)
As per this question they have only asked to fill the space properly with the child elements without a hack, that what I did, using grid we can manage as per the grid-template-columns we define not dynamically. So if you give the value to 1fr 1fr 1fr 1fr then it will split the div into 4fractions and try to fill the child elements in each fractions, in my knowledge thats the only way in grid. What I have stated in the answer is that if the user needs to split the div into 4 and use them with many elements even for multiple rows, the gid will help.
S
Stickers

Let's say if you want to set 10px space between the items, you can just set .item {margin-right:10px;} for all, and reset it on the last one .item:last-child {margin-right:0;}

You can also use general sibling ~ or next + sibling selector to set left margin on the items excluding the first one .item ~ .item {margin-left:10px;} or use .item:not(:last-child) {margin-right: 10px;}

Flexbox is so clever that it automatically recalculates and equally distributes the grid.

body { margin: 0; } .container { display: flex; } .item { flex: 1; background: gray; height: 50px; } .item:not(:last-child) { margin-right: 10px; }

If you want to allow flex wrap, see the following example.

body { margin: 0; } .container { display: flex; flex-wrap: wrap; margin-left: -10px; } .item { flex: 0 0 calc(50% - 10px); background: gray; height: 50px; margin: 0 0 10px 10px; }


This wouldn't work if the items are wrapped, since :last-child does not affect every last child at the end of a line, correct?
@Flimm I added an approach to work with flex wrap, see the second part above.
O
Ollie Williams

Update: gap for flexbox is now supported in all modern browsers (Edge/Chrome/Opera/Samsung Internet/Safari/Firefox)

Eventually they will add the gap property to flexbox. Until then you could use CSS grid instead which already has the gap property, and just have a single row. Nicer than dealing with margins.


Discussion here: github.com/w3c/csswg-drafts/issues/1696 — they will also streamline the naming, across CSS Grid, Flexbox and CSS Columns.
It's probably fine to just mention Chromium instead of Edge/Chrome/Opera. Also, Samsung Internet is a major browser? Wut? Anyway it's also Chromium-powered.
T
TylerH

I have found a solution that is based on the general sibling selector, ~, and allows infinite nesting.

See this code pen for a working example

Basically, inside of column containers, every child that is preceded by another child gets a top margin. Likewise, inside every row container, every child that is preceded by another gets a left margin.

.box { display: flex; flex-grow: 1; flex-shrink: 1; } .box.columns { flex-direction: row; } .box.columns>.box~.box { margin-left: 5px; } .box.rows { flex-direction: column; } .box.rows>.box~.box { margin-top: 5px; }


This results in different sized items due to the margins not being globally applied.
You will also need to add some extra CSS to handle smaller screens as it looks a bit weird on mobile, I would apply the .box ~ .box rule to larger screens and for smaller screens set the .box class to have a max-width of 100% and a margin bottom.
T
TylerH

According to #ChromeDevSummit there's an implementation of the gap property for Flexbox in Firefox and Chromium-based browsers.

Here's a Live Demo


c
chris

Moving on from sawa's answer, here's a slightly improved version that allows you to set a fixed spacing between the items without the surrounding margin.

http://jsfiddle.net/chris00/s52wmgtq/49/

Also included is the Safari "-webkit-flex" version.

.outer1 {
    background-color: orange;
    padding: 10px;
}

.outer0 {
    background-color: green;
    overflow: hidden;
}

.container
{
    display: flex;
    display: -webkit-flex;
    flex-wrap: wrap;    
    -webkit-flex-wrap: wrap;
    background-color: rgba(0, 0, 255, 0.5);
    margin-left: -10px;
    margin-top: -10px;
}

.item
{
    flex-grow: 1;
    -webkit-flex-grow: 1;
    background-color: rgba(255, 0, 0, 0.5);
    width: 100px;
    padding: 10px;
    margin-left: 10px;
    margin-top: 10px;
    text-align: center;
    color: white;
}

<div class="outer1">
    <div class="outer0">
        <div class="container">
            <div class="item">text</div>
            <div class="item">text</div>
            <div class="item">text</div>
            <div class="item">text</div>
            <div class="item">text</div>
            <div class="item">text</div>
        </div>
    </div>
</div>

Isn't this essentially the same as the example given in the question?
V
Veiko Jääger

I have used this for wrapped and fixed width columns. The key here is calc()

SCSS sample

$gap: 10px;

dl {
  display: flex;
  flex-wrap: wrap;
  padding: $gap/2;

  dt, dd {
    margin: $gap/2;}

  dt { // full width, acts as header
    flex: 0 0 calc(100% - #{$gap});}

  dd { // default grid: four columns 
    flex: 0 0 calc(25% - #{$gap});}

  .half { // hall width columns
    flex: 0 0 calc(50% - #{$gap});}

}

Full Codepen sample


This still adds a gutter before the first and after the last item, which OP prevents using negative margins.
Flexbox doesn't support calc() inside "flex" item in IE 11.
Cant be always used. imagine if there is also border needed for direct childs
padding is not a good choice
T
Tim

A flex container with -x (negative) margin and flex items with x (positive) margin or padding both lead to the desired visual result: Flex items have a fixed gap of 2x only between each other.

It appears to be simply a matter of preference, whether to use margin or padding on the flex items.

In this example, the flex items are scaled dynamically in order to preserve the fixed gap:

.flex-container { 
  margin: 0 -5px;
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
}

.flex-item {
  margin: 0 5px; // Alternatively: padding: 0 5px;
  flex: 1 0 auto;
}

Sorry, I didn't get it. What new does your answer introduce that is not said directly in the question?
Firstly, i wanted to sum up that both, margin and padding on the flex-item lead to the desired result, because existing answers only mention one or the other. Secondly, i wanted to give an example, where gaps are preserved by scaling the flex-items themselves.
here's a codepen example showing this effect. codepen.io/dalgard/pen/Dbnus
i
iClusterDev

Using Flexbox in my solution I've used the justify-content property for the parent element (container) and I've specified the margins inside the flex-basis property of the items. Check the code snippet below:

.container { display: flex; flex-flow: row wrap; justify-content: space-around; margin-bottom: 10px; } .item { height: 50px; display: flex; justify-content: center; align-items: center; background-color: #999; } .item-1-4 { flex-basis: calc(25% - 10px); } .item-1-3 { flex-basis: calc(33.33333% - 10px); } .item-1-2 { flex-basis: calc(50% - 10px); }

1
2
3
4
1
2
3
1
2


nice oneeeeeeee
M
Michael Benjamin

With flexbox, creating gutters is a pain, especially when wrapping is involved.

You need to use negative margins (as shown in the question):

#box {
  display: flex;
  width: 100px;
  margin: 0 -5px;
}

... or alter the HTML (as shown in another answer):

<div class='flex-wrapper'>
  <div class='flex'>
    <div class='box'></div>
    <div class='box'></div>
            ...
  </div>
</div>

... or something else.

In any case, you need an ugly hack to make it work because flexbox doesn't provide a "flex-gap" feature (at least for now).

The issue of gutters, however, is simple and easy with CSS Grid Layout.

The Grid spec provides properties that create space between grid items, while ignoring the space between items and the container. These properties are:

grid-column-gap

grid-row-gap

grid-gap (the shorthand for both properties above)

Recently, the spec has been updated to conform with the CSS Box Alignment Module, which provides a set of alignment properties for use across all box models. So the properties are now:

column-gap

row-gap

gap (shorthand)

However, not all Grid-supporting browsers support the newer properties, so I'll use the original versions in the demo below.

Also, if spacing is needed between items and the container, padding on the container works just fine (see the third example in the demo below).

From the spec:

10.1. Gutters: the row-gap, column-gap, and gap properties The row-gap and column-gap properties (and their gap shorthand), when specified on a grid container, define the gutters between grid rows and grid columns. Their syntax is defined in CSS Box Alignment 3 §8 Gaps Between Boxes. The effect of these properties is as though the affected grid lines acquired thickness: the grid track between two grid lines is the space between the gutters that represent them.

.box { display: inline-grid; grid-auto-rows: 50px; grid-template-columns: repeat(4, 50px); border: 1px solid black; } .one { grid-column-gap: 5px; } .two { grid-column-gap: 10px; grid-row-gap: 10px; } .three { grid-gap: 10px; padding: 10px; } .item { background: lightgray; }



More information:

Browser Support for CSS Grid

Make it easier to define margins that only apply between flex-items (discussion)

Spacing between flexbox items


t
tillsanders

Why not do it like this:

.item + .item {
    margin-left: 5px;
}

This uses the adjacent sibling selector, to give all .item elements, except the first one a margin-left. Thanks to flexbox, this even results in equally wide elements. This could also be done with vertically positioned elements and margin-top, of course.


This would work as long as the flex items are always in a single row. If wrapping is allowed then it probably won't be sufficient.
M
MK10

Here's my solution, that doesn't require setting any classes on the child elements:

.flex-inline-row {
    display: inline-flex;
    flex-direction: row;
}

.flex-inline-row.flex-spacing-4px > :not(:last-child) {
    margin-right: 4px;
}

Usage:

<div class="flex-inline-row flex-spacing-4px">
  <span>Testing</span>
  <span>123</span>
</div>

The same technique can be used for normal flex rows and columns in addition to the inline example given above, and extended with classes for spacing other than 4px.


But it doesn't work with flex-wrap
w
wutzebaer

I often use the + operator for such cases

#box { display: flex; width: 100px; } .item { background: gray; width: 50px; height: 50px; } .item + .item { margin-left: 5px; }


l
lukefrake

I find the easiest way of doing this is with percentages and just allowing the margin to tally up your width

This means you end up with something like this if you where using your example

#box {
   display: flex;
}

.item {
   flex: 1 1 23%;
   margin: 0 1%;
}

Does mean your values are based on the width though which might not be good for everybody.


Just came across this solution and yep, it's very nice. Works in my case, at least (a pen for those who could find it to be interesting: codepen.io/rishatmuhametshin/pen/XXjpaV).
A
Andres Paul

You could use the new property gap. I copy paste the explanation I found in this article, as well as more information

CSS grid layout has had gap (previously grid-gap) for some time. By specifying the internal spacing of a containing element rather than the spacing around child elements, gap solves many common layout issues. For example, with gap, you don't have to worry about margins on child elements causing unwanted whitespace around the edges of a containing element:

Unfortunately right now, only FireFox supports gap in flex layouts.

@use postcss-preset-env { stage: 0; browsers: last 2 versions } section { width: 30vw; display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(12ch, 1fr)); &[flex] { display: flex; flex-wrap: wrap; } margin-bottom: 3rem; } .tag { color: white; background: hsl(265 100% 47%); padding: .5rem 1rem; border-radius: 1rem; } button { display: inline-flex; place-items: center; gap: .5rem; background: hsl(265 100% 47%); border: 1px solid hsl(265 100% 67%); color: white; padding: 1rem 2rem; border-radius: 1rem; font-size: 1.25rem; } body { min-height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; }

Grid

Awesome
Coo
Rad
Math

Flex

Awesome
Coo
Rad
Math


gap was already recommended in an answer here stackoverflow.com/a/58041749/2756409 Also, this is not CSS.
gap still not supported in Safari
A
Andreas

Here's a grid of card UI elements with spacing completed using flexible box:

https://i.stack.imgur.com/7km7e.png

I was frustrated with manually spacing the cards by manipulating padding and margins with iffy results. So here's the combinations of CSS attributes I've found very effective:

.card-container { width: 100%; height: 900px; overflow-y: scroll; max-width: inherit; background-color: #ffffff; /*Here's the relevant flexbox stuff*/ display: flex; flex-direction: row; justify-content: center; align-items: flex-start; flex-wrap: wrap; } /*Supplementary styles for .card element*/ .card { width: 120px; height: 120px; background-color: #ffeb3b; border-radius: 3px; margin: 20px 10px 20px 10px; }

Hope this helps folks, present and future.


Update: This spacing is in effective for mobile rendering HTML elements that need a certain alignment (e.g. center, left, etc.). If you find yourself using flex box for mobile development, I've found relief in switching to purely margin-based alignment.
z
zurfyx

Columnify - A solo class for N columns

Flexbox and SCSS

.columnify {
  display: flex;

  > * {
    flex: 1;

    &:not(:first-child) {
      margin-left: 2rem;
    }
  }
}

Flexbox and CSS

.columnify { display: flex; } .columnify > * { flex: 1; } .columnify > *:not(:first-child) { margin-left: 2rem; }

Play with it on JSFiddle.


A
Amo Wu

#box { display: flex; width: 100px; } .item { background: gray; width: 50px; height: 50px; } /* u mean utility */ .u-gap-10 > *:not(:last-child) { margin-right: 10px; }


i
iamandrewluca

Just use .item + .item in selector to match from second .item

#box { display: inline-flex; margin: 0 -5px; } .item { background: gray; width: 10px; height: 50px; } #box .item + .item { margin-left: 10px; }


K
Keviin Cosmos

I found a hack because i really need this my self.

/* grid */ .container { display: flex; flex-flow: row wrap; justify-content: space-between; } .container::after, /* this makes sure odd element goes left and not space between */ .item { content:""; width: calc(33.3333% - 20px); margin-bottom: 40px; } /* extra styling - not important */ .item { height: 100px; background: #787878; }

Here's a post grid with nice flex grow categories also. I think you'd like it. See Codepen


S
Salman A

Assuming:

You want 4 column grid layout with wrapping

The number of items is not necessarily a multiple of 4

Set a left margin on every item except 1st, 5th, 9th item and so on; and set fixed width on each item. If the left margin is 10px then each row will have 30px margin between 4 items, the percentage width of item can be calculated as follows:

100% / 4 - horizontal-border - horizontal-padding - left-margin * (4 - 1) / 4

This is a decent workaround for issues involving last row of flexbox.

.flex { display: flex; flex-direction: row; flex-wrap: wrap; margin: 1em 0; background-color: peachpuff; } .item { margin-left: 10px; border: 1px solid; padding: 10px; width: calc(100% / 4 - 2px - 20px - 10px * (4 - 1) / 4); background-color: papayawhip; } .item:nth-child(4n + 1) { margin-left: 0; } .item:nth-child(n + 5) { margin-top: 10px; }

1
2
3
4
1
2
3
4
5
6
1
2
3
4
5
6
7
8
9


N
Nick

There is indeed a nice, tidy, CSS-only way to do this (that one may consider "better").

Of all the answers posted here, I only found one that uses calc() successfully (by Dariusz Sikorski). But when posed with: "but it fails if there are only 2 items in the last row" there was no solution expanded.

This solution addresses the OP's question with an alternative to negative margins and addresses the problem posed to Dariusz.

notes:

This example only demonstrates a 3-column layout

It uses calc() to let the browser do math the way it wants -- 100%/3 (although 33.3333% should work just as well), and (1em/3)*2 (although .66em should also work well).

It uses ::after to pad the last row if there are fewer elements than columns

.flex-container { display: flex; justify-content: space-between; flex-wrap: wrap; } .flex-container:after { content: ""; } .flex-container > div, .flex-container:after { box-sizing: border-box; width: calc((100%/3) - ((1em/3)*2)); } .flex-container > :nth-child(n + 4) { margin-top: 1em; } /* the following is just to visualize the items */ .flex-container > div, .flex-container:after { font-size: 2em; } .flex-container { margin-bottom:4em; } .flex-container > div { text-align: center; background-color: #aaa; padding: 1em; } .flex-container:after { border: 1px dashed red; }

Example 1 (2 elements)

1
2

Example 2 (3 elements)

1
2
3

Also at https://codepen.io/anon/pen/rqWagE


j
jnmrobinson

It won't work in every case but if you have flexible child widths (%) and know the number of items per row you can very cleanly specify the margins of the necessary elements by using nth-child selector/s.

It depends largely on what you mean by "better". This way doesn't require additional wrapper markup for child elements or negative elements - but those things both have their place.

section { display: block width: 100vw; } .container { align-content: flex-start; align-items: stretch; background-color: #ccc; display: flex; flex-flow: row wrap; justify-content: flex-start; width: 100%; } .child-item { background-color: #c00; margin-bottom: 2%; min-height: 5em; width: 32%; } .child-item:nth-child(3n-1) { margin-left: 2%; margin-right: 2%; }


Not responsive. Works only for fixed width parent.
The OP doesn't ask for a responsive solution and their example uses a fix width. Given that this uses % values it's easy to argue that this is responsive since the items will adapt to the size of the parent which is set by percentage.
T
Tunasing

I came across the same issue earlier, then stumbled upon the answer for this. Hope it will help others for future reference.

long answer short, add a border to your child flex-items. then you can specify margins between flex-items to whatever you like. In the snippet, i use black for illustration purposes, you can use 'transparent' if you like.

#box { display: flex; width: 100px; /* margin: 0 -5px; *remove this*/ } .item { background: gray; width: 50px; height: 50px; /* margin: 0 5px; *remove this*/ border: 1px solid black; /* add this */ } .item.special{ margin: 0 10px; }