4

I have a container that is 608px wide, and my design calls for having 3 items per row. Where each item is 192px. And only the first 2 items in the row have a margin-right of 16px.

The problem is the CSS is adding a margin-right of 16px to all items.

How can I update the code to make it 3 items per row, where just the 1st and 2nd items per row have the margin-right padding?

I had a hacky version working where I made the margin-right and margin-left 8px and then added a negative -8px margin on the container but that feels hacky and I'm curious if there is now a more elegant way to accomplish this with CSS3.

.container {
  width: 608px;
  min-width: 608px;
  max-width: 608px;
  background: #efefef;
  display: flex;
  flex-wrap: wrap;
}

.item {
  width: 192px;
  min-width: 192px;
  max-width: 192px;
  margin: 0 16px 16px 0;
  border: 1px solid #ccc;
  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>
Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
AnApprentice
  • 108,152
  • 195
  • 629
  • 1,012

4 Answers4

3

Here is the CSS-grid solution where you can easily control the gap between element and you also don't need to explicitly specify a width for them:

.container {
  width: 608px;
  display:grid;
  grid-template-columns:repeat(3, 1fr);
  grid-column-gap:16px;
  background: #efefef;
}

.item {
  border: 1px solid #ccc;
  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>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
2

You can add pseudo selector nth-child(3n+3) to remove the right margin for every .item that is multiple of 3 (ie. 3,6,9...). Try adding this piece of code to yours.

.item:nth-child(3n+3) {
  margin-right: 0;
}
Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
Aryan Twanju
  • 2,464
  • 1
  • 9
  • 12
2

These are your layout requirements:

  • three items per row
  • each item is 192px wide
  • the container is 608px wide
  • the space between items must be 16px
  • the space between items and the container must be 0px.

Well, you've already done the math, so you don't even need to use margins.

192 * 3 = 576

576 + 16 + 16 = 608

Since you've calculated the container's width to fit three items, plus an extra 32px of free space, the margins are unnecessary. Use justify-content, which is designed to distribute free space.

8.2. Axis Alignment: the justify-content property

The justify-content property aligns flex items along the main axis of the current line of the flex container. [...] It helps distribute free space when all the flex items on a line are inflexible.

.container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between; /* NEW */
  width: 608px;
  background: #efefef;
}

.item {
  flex: 0 0 192px;
  border: 1px solid #ccc;
  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>

But your question ends with:

I'm curious if there is now a more elegant way to accomplish this with CSS3.

In cases where the item, spacing and/or container lengths vary, then YES.

Your goal, in essence, is to create "gutters", which means space between items, but not between items and the container.

Flexbox offers standard margins (which can cause trouble in multi-line containers, as you've seen) and alignment properties (like justify-content and align-items, which aren't precise unless you do the math beforehand).

Therefore, flexbox is not the optimal solution when gutters are important. It doesn't provide a natural method for creating space between items, without also applying that space between items and the container.

However, gutters are not a problem in CSS Grid Layout, which provides specific properties for horizontal and vertical gaps:

  • row-gap
  • column-gap
  • gap (the shorthand for both)

These properties create space between grid items, but not between items and the container.

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.

NOTE that the CSS Grid spec was recently revised. These properties were previously listed as grid-column-gap, grid-row-gap and grid-gap (reference). I'm using these versions in the example below for compatibility purposes.

.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, 192px);
  grid-column-gap: 16px;
  width: 608px;
  background: #efefef;
}

.item {
  border: 1px solid #ccc;
  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>

Most major browsers now provide full support for CSS Grid.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
1

In flexbox you can use the justify-content property to choose how you want to align your items when they don't use up all the remaining space in a row. In this case, just remove the margin and use justify-content: space-between on the container and it will accomplish what you want. In this case the justify-content: space-between will automatically even out the remaining space in the row between the items.

.container {
  width: 608px;
  min-width: 608px;
  max-width: 608px;
  background: #efefef;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.item {
  width: 192px;
  min-width: 192px;
  max-width: 192px;
  border: 1px solid #ccc;
  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>

Another option if you aren't using flexbox, would be to just choose every third .item class and remove the margin-right from them using the nth-of-type() selector:

.item:nth-of-type(3n) {
  margin-right: 0;
}
Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
Blake
  • 553
  • 1
  • 4
  • 7