CSS: Why are my margins being ignored?

Written on

Sometimes when you're building your website you'll find that the margins you put on an element are not working properly. This is referred to as collapsing margins. It's not a bug, but a feature of CSS.

What are collapsing margins?

Sometimes two or more elements that are near each other will have their vertical margins merged. There are two ways this can happen:

  • Parent-child collapsing
  • Sibling collapsing

Parent-child collapsing

An element will have its margins merged with its first and last block level child if it does not have padding or borders at the top or bottom. It will merge with the first child if there's no top border or padding and it will merge with its last child if it has no bottom border or padding. Parent-child collapsing can apply to several levels of nesting.

When the margins collapse, the largest of the two margins is used on the parent and the child has its margin removed. If a parent has a margin of 10px and the child has a margin of 20px, the resulting margin will be 20px, not 30px.

Now we know where that margin went.

Sibling collapsing

The margin of an element refers to its distance from the edge of other elements, not its distance from other elements' margins. This is why margins collapse between siblings. The behavior is similar to the parent-child collapsing: The largest of the two margins will be used. If one element has a bottom margin of 15px and the following sibling element has a top margin of 20px, the resulting margin between the elements will be 20px, not 35px.

How do I stop collapsing margins?

Many times you'll want to stop margins from collapsing because it just doesn't fit your layout. There are several different techniques for doing this.

Parent-child collapsing

Since margins don't collapse when there is padding or a border on the parent element, one way to prevent the margins from collapsing is to give the parent element padding or a transparent border.

.parent {
  padding-top: 1px;
  padding-bottom: 1px;
}

Sometimes you don't want padding on the parent element, because this often interferes with the page's layout. Another technique to prevent collapsing margins is to set the overflow property to "hidden" or "auto." This works because, according to the W3C, setting the overflow property establishes a new "block formatting context."

.parent {
  overflow: hidden;
}

The overflow will not cause content to be hidden or scroll bars to appear as long as the parent element doesn't have a set height. Sometimes you want to set the height of the parent or you have children positioned outside of the parent element. Don't worry! There's yet another solution for collapsing margins.

Now, collapsing margins only affect the first and last child of the element, so what if we put an element before the first child? We could put an empty element there and set its height to zero to prevent it from changing the layout. We could also do the same by putting an element after the last child.

The only problem with this is that you would have to add new elements to the HTML, and we don't want to do that because CSS is supposed to separate content from presentation. Instead, we can rely on pseudo-elements. Pseudo-elements are elements that are placed onto the page using CSS. We can use the ::before and ::after to place elements before and after the element we've selected. The ::before and ::after elements are not actually before and after the element itself, instead they are before and after the content inside the element, this means that they are still contained by the element, we can use the ::before and ::after pseudo-elements as first and last children.

.parent::before {
  content: " "; /* To make the pseudo-element exist, we have to give it content. */
  display: table; /* table works across the widest range of browsers */
}

.parent::after {
  content: " ";
  display: table;
  clear: both; /* This helps fix an issue with CSS floated elements, but that's a topic for another article */
}

This block of CSS code is longer than the other solutions, but it is the most robust with the fewest side effects.

Sibling collapsing

There is no way to prevent sibling margins from collapsing using just CSS, but it is not a big deal. Unlike with parent-child collapsing, your margins are not lost. You just need to take collapsing margins into account when setting margins on your sibling elements. If you want 40 pixels of space between the two siblings, set the margin of at least one of them to 40px.