I’m writing the Box Model chapter of the new book and came to the point where I had to treat negative margins. To my surprise, I found that there is no systematic treatment of negative margins anywhere. So I had to figure it out for myself. Below is my initial draft of the negative margin section.
I'm writing a CSS book.
The latest specification only says: “Negative values for margin properties are allowed, but there may be implementation-specific limits.” and leaves it at that. Not extremely helpful. MDN is mostly silent as well, and Rachel Andrew’s big overview article doesn’t mention negative margins at all.
That’s odd, especially since negative margins are a very old functionality that I might even have used in my very first CSS test somewhere back in 1998. (Unless I used position: relative
; I can’t remember.)
But anyway, here is, apparently, the first-ever systematic treatment of negative margins in simple situations.
It is possible to give margins a negative value. This allows you to draw the element closer to its top or left neighbour, or draw its right and bottom neighbour closer to it. Also, there is an exception we’ll get to in a minute.
Here is our test element: a simple container with three paragraphs in it. Note that the paragraphs have a width of 250px. This is extremely important, due to the exception we’ll get to in a minute.
First paragraph with a bit of text in it to provide some content.
Second paragraph with a bit of text in it to provide some content.
Third paragraph with a bit of text in it to provide some content.
To start, let’s give the first paragraph a -15px margin-bottom. Essentially, when the browser calculates the point where the second paragraph should start, it moves that point 15px upward. From then on the browser lays out all paragraphs as normal.
First paragraph with margin-bottom: -15px
.
Second paragraph with a bit of text in it to provide some content.
Third paragraph with a bit of text in it to provide some content.
Therefore the second paragraph, being the bottom neighbour of the first one, is draw 15px closer to the first paragraph. The margin between the second and third paragraphs remains intact; the browser calculates it normally. Thus, the rest of the vertical rhythm is preserved.
This trick is useful for subtle tweaks, where the content of one element should slightly overlap the content of the one above it.
Now let’s give the second paragraph a -15px margin-top. As you see, this yields exactly the same effect. Again, the second paragraph is moved upward by 15px, and the subsequent paragraphs follow.
First paragraph with a bit of text in it to provide some content.
Second paragraph with margin-top: -15px
.
Third paragraph with a bit of text in it to provide some content.
Also note that margin collapsing behaves different when you use negative margins. This, at least, is specified in CSS 2.1:
In the case of negative margins, the maximum of the absolute values of the negative adjoining margins is deducted from the maximum of the positive adjoining margins. If there are no positive margins, the maximum of the absolute values of the adjoining margins is deducted from zero.
In the last example, the first paragraph still has its default margin-bottom of 1em (Chrome; can’t find Firefox’s value).
Normally, the browser would take the first paragraph’s margin-bottom and the second one’s margin-top, figure out which one is larger, and apply that margin between the two, which would yield max(-15px,1em) = 1em. That’s not how it works, though.
In case of negative margins we take the absolute value of the negative margin (15px) and deduct it from the positive margin (1em). This yields about 1px (depending on the font size, of course).
Thus, negative margins are actually allowed to pull elements closer to their neighbours without being hindered by regular margin collapsing.
Now we treated negative margin-top and -bottom fully. It’s an occasionally useful effect.
Negative margin-left and -right work the same, provided the element has a width. Here we apply margin-left: -10px and margin-right: 10px.
First paragraph with margin-left: -10px
.
Second paragraph with margin-right: -10px
.
Third paragraph with a bit of text in it to provide some content.
As you see, the first paragraph is now offset 10px to the left, while retaining its width. Thus, its right edge also moves 10px to the left.
The second paragraph with the negative margin-right is unaffected. The negative margin-right would influence any element to the right of the second paragraph, but there aren’t any.
To show negative margin-right in its full glory, let’s float the paragraphs, so that they have a right neighbour. Here is the reference element.
First paragraph with a bit of text in it to provide some content.
Second paragraph with a bit of text in it to provide some content.
Third paragraph with a bit of text in it to provide some content.
Now we’re going to sprinkle some negative margins on the paragraphs.
First paragraph with margin-right: -10px
.
Second paragraph with margin-top: -10px
.
Third paragraph with margin-bottom: -10px
.
As you see, the second paragraph is now drawn 10px closer to the first one due to the first’s negative margin-right. This is exactly the same effect as with a negative margin-bottom.
Also note that the second paragraph has a negative margin-top, which means it is offset 10px upward. The third paragraph has a negative margin-bottom, which has no effect, since it does not have a bottom neighbour.
Remember: margin collapsing does not work on margin-left and -right; just on -top and -bottom. Therefore we do not have to worry about it in this case.
If we give the second paragraph a margin-left: -10px, the same happens. Just like with top and bottom, left and right are interchangeable for this effect.
First paragraph with a bit of text in it to provide some content.
Second paragraph with margin-left: -10px
.
Third paragraph with a bit of text in it to provide some content.
So far, negative margin-left and -right behave exactly like negative margin-top and -bottom.
Now let’s change the behaviour of negative margin-right by giving the paragraphs width: auto. They do not have a fixed width any more; instead they fill up their parent element completely while respecting its padding. That’s how width: auto works.
The paragraph with margin-left: -10px is still offset 10px to the left, but its width grows. Thus, its right edge is not offset but stays where it is.
Reference paragraph
First paragraph with margin-left: -10px
.
Second paragraph with margin-right: -10px
.
Third paragraph with margin-left: -10px; margin-right: -10px
The negative margin-right now does the same thing. It offsets the paragraph’s right margin by 10px to the right, and the paragraph’s width increases, causing its left edge to stay where it is. This only happens when an element has width: auto
. As we saw before, elements with a fixed width behave quite differently.
Finally, the third paragraph has both. Both its left and its right margins are offset by 10px, essentially negating the container’s padding: 10px;.
This is by far the most common use case for negative margins. You give a container a padding so that its contents have some breathing space. However, you want the header to span the entire container, ignoring the padding. Negative margins are the way to go.
This is a regular content paragraph.
This is a regular content paragraph.
These are the header styles; the container has padding: 10px
h5 { margin-left: -10px; margin-right: -10px; padding-left: 10px; margin-top: 0; margin-bottom: 0; background-color: grey; color: white; /* no width, so defaults to width: auto */ }
Again, this is only possible if the header has width: auto
. Fortunately that’s the case in 99% of real-world use cases.
This is how negative margins behave in simple situations. Now that I established a baseline I can look into how they behave in flexboxes and grids.
I'm writing a CSS book.
This is the blog of Peter-Paul Koch, web developer, consultant, and trainer.
You can also follow
him on Twitter or Mastodon.
Atom
RSS
If you like this blog, why not donate a little bit of money to help me pay my bills?
Categories: