40

Following is my code which is working fine, except the last line in a container which is adding margin-bottom: 5px; from .tag css class.

Problem I am facing is the tag list is dynamic so I cannot directly target the Item-13,14 and so on and so forth.

Let me know in flex do we have any privilege to add custom css w.r.t the last row in my flex container.

* {
    margin: 0;
    padding: 0;
}

html, body {
    box-sizing: border-box;
}

.container {
    width: 600px;
    margin: 0 auto;
    margin-top: 25px;
    border: 1px solid;
    padding: 5px;
}

.tags {
    list-style-type: none;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: flex-start;
}

.tag {
    padding: 5px;
    background-color: #76FF03;
    margin: 0 5px 5px;
}
<div class="container">
  <ul class="tags">
    <li class="tag item-1">Tag Item 1</li>
    <li class="tag item-2">Tag Item 2</li>
    <li class="tag item-3">Tag Item 3</li>
    <li class="tag item-4">Tag Item 4</li>
    <li class="tag item-5">Tag Item 5</li>
    <li class="tag item-6">Tag Item 6</li>
    <li class="tag item-7">Tag Item 7</li>
    <li class="tag item-8">Tag Item 8</li>
    <li class="tag item-9">Tag Item 9</li>
    <li class="tag item-10">Tag Item 10</li>
    <li class="tag item-11">Tag Item 11</li>
    <li class="tag item-12">Tag Item 12</li>
    <li class="tag item-13">Tag Item 13</li>
    <li class="tag item-14">Tag Item 14</li>
    <li class="tag item-15">Tag Item 15</li>
    <li class="tag item-16">Tag Item 16</li>
  </ul>
</div>
Maik Lowrey
  • 15,957
  • 6
  • 40
  • 79
Nesh
  • 2,389
  • 7
  • 33
  • 54

6 Answers6

41

Update in late 2021

Now the gap property also works with Flexbox (on newer browser versions).

* {
  margin: 0;
  padding: 0;
}

html, body {
  box-sizing: border-box;
}

.container {
  width: 600px;
  margin: 0 auto;
  margin-top: 25px;
  border: 1px solid;
  padding: 5px;
}

.tags {
  list-style-type: none;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 5px;                          /* added */
}

.tag {
  padding: 5px;
  background-color: #76FF03;
  /* margin: 0 5px 5px;                 removed */
}
<div class="container">
  <ul class="tags">
    <li class="tag item-1">Tag Item 1</li>
    <li class="tag item-2">Tag Item 2</li>
    <li class="tag item-3">Tag Item 3</li>
    <li class="tag item-4">Tag Item 4</li>
    <li class="tag item-5">Tag Item 5</li>
    <li class="tag item-6">Tag Item 6</li>
    <li class="tag item-7">Tag Item 7</li>
    <li class="tag item-8">Tag Item 8</li>
    <li class="tag item-9">Tag Item 9</li>
    <li class="tag item-10">Tag Item 10</li>
    <li class="tag item-11">Tag Item 11</li>
    <li class="tag item-12">Tag Item 12</li>
    <li class="tag item-13">Tag Item 13</li>
    <li class="tag item-14">Tag Item 14</li>
    <li class="tag item-15">Tag Item 15</li>
    <li class="tag item-16">Tag Item 16</li>
  </ul>
</div>

Older versions


The most common way, which most framework use to solve that, is to set a top margin on the items (tag) instead, and then compensate that with a negative margin on the items parent (tags)

* {
  margin: 0;
  padding: 0;
}

html, body {
  box-sizing: border-box;
}

.container {
  width: 600px;
  margin: 0 auto;
  margin-top: 25px;
  border: 1px solid;
  padding: 5px;
}

.tags {
  list-style-type: none;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
  margin-top: -5px;                 /*  compensate top  */
}

.tag {
  padding: 5px;
  background-color: #76FF03;
  margin: 5px 5px 0 0;              /*  top, right  */
}
<div class="container">
  <ul class="tags">
    <li class="tag item-1">Tag Item 1</li>
    <li class="tag item-2">Tag Item 2</li>
    <li class="tag item-3">Tag Item 3</li>
    <li class="tag item-4">Tag Item 4</li>
    <li class="tag item-5">Tag Item 5</li>
    <li class="tag item-6">Tag Item 6</li>
    <li class="tag item-7">Tag Item 7</li>
    <li class="tag item-8">Tag Item 8</li>
    <li class="tag item-9">Tag Item 9</li>
    <li class="tag item-10">Tag Item 10</li>
    <li class="tag item-11">Tag Item 11</li>
    <li class="tag item-12">Tag Item 12</li>
    <li class="tag item-13">Tag Item 13</li>
    <li class="tag item-14">Tag Item 14</li>
    <li class="tag item-15">Tag Item 15</li>
    <li class="tag item-16">Tag Item 16</li>
  </ul>
</div>

And an even better way might be to give the same margin to all item's sides, though half the value.

* {
  margin: 0;
  padding: 0;
}

html, body {
  box-sizing: border-box;
}

.container {
  width: 600px;
  margin: 0 auto;
  margin-top: 25px;
  border: 1px solid;
  padding: 5px;
}

.tags {
  list-style-type: none;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
}

.tag {
  padding: 5px;
  background-color: #76FF03;
  margin: 2.5px;
}
<div class="container">
  <ul class="tags">
    <li class="tag item-1">Tag Item 1</li>
    <li class="tag item-2">Tag Item 2</li>
    <li class="tag item-3">Tag Item 3</li>
    <li class="tag item-4">Tag Item 4</li>
    <li class="tag item-5">Tag Item 5</li>
    <li class="tag item-6">Tag Item 6</li>
    <li class="tag item-7">Tag Item 7</li>
    <li class="tag item-8">Tag Item 8</li>
    <li class="tag item-9">Tag Item 9</li>
    <li class="tag item-10">Tag Item 10</li>
    <li class="tag item-11">Tag Item 11</li>
    <li class="tag item-12">Tag Item 12</li>
    <li class="tag item-13">Tag Item 13</li>
    <li class="tag item-14">Tag Item 14</li>
    <li class="tag item-15">Tag Item 15</li>
    <li class="tag item-16">Tag Item 16</li>
  </ul>
</div>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • What seems a bit strange is how negative margin is working on the parent. I would have assumed negative margin would have meant that the entire div would have moved up, instead it seems to be acting on the space inside i.e. between the parent div & the child – copenndthagen Jun 18 '19 at 05:53
  • @testndtv And the entire element (that has the margin set) does move up. In my first sample the `ul` with class `tags` moves up, inside the `div`. Add a border to it and you'll see how it works. – Asons Jun 18 '19 at 13:06
  • works perfect, if one wants to use margin-left & margin-bottom together gap does the same for you – Kaustubh Bagwe Jul 18 '22 at 17:05
17

As mentioned in another answer, the common way to solve this problem, which is used by many frameworks, is to apply negative margin and padding combinations. Even flexbox provides no clean solution. Ugly hacks are the norm in this situation.

However, if you're open to another CSS3 technology – Grid Layout – the solution is simple, clean and easy.

CSS Grid provides the grid-column-gap and grid-row-gap properties. The shorthand for both is grid-gap. These properties allow you to create space between grid items. But they never apply to the space between items and the container. Again, they only work between items (i.e., the gutters).

So what you would do is use grid-gap for spacing between the items. Then use padding for the same spacing between the items and the container.

Here are just a few benefits to this method:

  • The last row margin problem is eliminated. The number of items is no longer a concern.
  • No need for hacks.
  • No need for margins.
  • Your items align in columns (if that's something you want).
  • All free space in the container is distributed evenly among items (if that's something you want).

.container {
  width: 600px;
  margin: 0 auto;
  margin-top: 25px;
  border: 1px solid;
  padding: 5px;
}

.tags {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  grid-gap: 5px;
  list-style-type: none;
}

.tag {
  padding: 5px;
  background-color: #76FF03;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
<div class="container">
  <ul class="tags">
    <li class="tag item-1">Tag Item 1</li>
    <li class="tag item-2">Tag Item 2</li>
    <li class="tag item-3">Tag Item 3</li>
    <li class="tag item-4">Tag Item 4</li>
    <li class="tag item-5">Tag Item 5</li>
    <li class="tag item-6">Tag Item 6</li>
    <li class="tag item-7">Tag Item 7</li>
    <li class="tag item-8">Tag Item 8</li>
    <li class="tag item-9">Tag Item 9</li>
    <li class="tag item-10">Tag Item 10</li>
    <li class="tag item-11">Tag Item 11</li>
    <li class="tag item-12">Tag Item 12</li>
    <li class="tag item-13">Tag Item 13</li>
    <li class="tag item-14">Tag Item 14</li>
    <li class="tag item-15">Tag Item 15</li>
    <li class="tag item-16">Tag Item 16</li>
  </ul>
</div>

jsFiddle demo

Spec reference: https://www.w3.org/TR/css3-grid-layout/#gutters

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 6
    The CSS Working Group [has recently decided](https://github.com/w3c/csswg-drafts/issues/1696) to add the `gap` property to Flexbox, too (making the `*-gap` properties not Grid-specific anymore). But I havent heard about any browser adopting this yet. – Ilya Streltsyn Nov 29 '17 at 17:57
4

Now you can use the gap property! The row gap will not be applied at the bottom of the last line.

gap: 5px;
2

Adjust the padding-bottom for .tags instead.

Because you'll never know how many tags will appear on the bottom part of the list (this solution is much flexible),

Like:

.tags {
  padding: 5px 0 0;
}

Have a look at the snippet below:

* {
 margin: 0;
 padding: 0;
}

html, body {
 box-sizing: border-box;
}

.container {
 width: 600px;
 margin: 0 auto;
 margin-top: 25px;
 border: 1px solid;
 padding: 5px;
}

.tags {
 list-style-type: none;
 display: flex;
 flex-direction: row;
 flex-wrap: wrap;
 justify-content: flex-start;
  padding: 5px 0 0;
}

.tag {
 padding: 5px;
 background-color: #76FF03;
 margin: 0 5px 5px;
}
<div class="container">
  <ul class="tags">
    <li class="tag item-1">Tag Item 1</li>
    <li class="tag item-2">Tag Item 2</li>
    <li class="tag item-3">Tag Item 3</li>
    <li class="tag item-4">Tag Item 4</li>
    <li class="tag item-5">Tag Item 5</li>
    <li class="tag item-6">Tag Item 6</li>
    <li class="tag item-7">Tag Item 7</li>
    <li class="tag item-8">Tag Item 8</li>
    <li class="tag item-9">Tag Item 9</li>
    <li class="tag item-10">Tag Item 10</li>
    <li class="tag item-11">Tag Item 11</li>
    <li class="tag item-12">Tag Item 12</li>
    <li class="tag item-13">Tag Item 13</li>
    <li class="tag item-14">Tag Item 14</li>
    <li class="tag item-15">Tag Item 15</li>
    <li class="tag item-16">Tag Item 16</li>
  </ul>
</div>

Hope this helps!

Saurav Rastogi
  • 9,575
  • 3
  • 29
  • 41
1

I know this has been years, but I think the best way to do it is to use the line-height CSS property.

https://developer.mozilla.org/en-US/docs/Web/CSS/line-height

yankeedoodle
  • 353
  • 2
  • 11
0

use this

li.tag:nth-last-child(-n+4) {
    margin-bottom: 0;
}

* {
 margin: 0;
 padding: 0;
}
html, body {
box-sizing: border-box;
}

.container {
 width: 600px;
 margin: 0 auto;
 margin-top: 25px;
 border: 1px solid;
 padding: 5px;
}

.tags {
 list-style-type: none;
 display: flex;
 flex-direction: row;
 flex-wrap: wrap;
 justify-content: flex-start;
}

.tag {
 padding: 5px;
 background-color: #76FF03;
 margin: 0 5px 5px;
}

li.tag:nth-last-child(-n+4) {
    margin-bottom: 0;
}
<div class="container">
  <ul class="tags">
    <li class="tag item-1">Tag Item 1</li>
    <li class="tag item-2">Tag Item 2</li>
    <li class="tag item-3">Tag Item 3</li>
    <li class="tag item-4">Tag Item 4</li>
    <li class="tag item-5">Tag Item 5</li>
    <li class="tag item-6">Tag Item 6</li>
    <li class="tag item-7">Tag Item 7</li>
    <li class="tag item-8">Tag Item 8</li>
    <li class="tag item-9">Tag Item 9</li>
    <li class="tag item-10">Tag Item 10</li>
    <li class="tag item-11">Tag Item 11</li>
    <li class="tag item-12">Tag Item 12</li>
    <li class="tag item-13">Tag Item 13</li>
    <li class="tag item-14">Tag Item 14</li>
    <li class="tag item-15">Tag Item 15</li>
    <li class="tag item-16">Tag Item 16</li>
  </ul>
</div>
Hiren Vaghasiya
  • 5,454
  • 1
  • 11
  • 25