Uncollapsing Margins
Like many basic concepts, margin collapsing can lead to unexpected and sometimes counterintuitive results. Before we explore those results and how to work around them, however, let's take a look at the most basic form of margin collapsing.
Consider a series of paragraphs that have the following style applied to them:
p {margin: 10px 0; background: #CCC;}
The separation between the outer border edges of these paragraphs will be ten pixels, as shown in Figure 1.
Why is this? Because it's what authors would tend to expect. If you declare top and bottom margins of 1em
for paragraphs, odds are that you want one em of space between successive paragraphs. If you're feeling dubious about that assertion, consider this:
p {margin: 1em 0;} ul {margin: 1em 0;}
Closing Up Headings
A common typographical effect is to place paragraph text up close to a heading. Some people solve this by adding a class to a paragraph that immediately follows a heading, and then removing the top margin from such paragraphs. Thanks to margin collapsing, though, there's a more elegant way to solve the problem. Consider:
h1, h2, h3, h4, h5, h6 {margin-bottom: 0;} p {margin: 0 0 1em;}
Given those rules, any paragraph that immediately follows a heading will sit right up next to it, while successive paragraphs will still be separated by a "blank line". Because margin collapsing between paragraphs causes the top and bottom margins to collapse, we can remove one of the two without changing the layout situation at all. Thus, the presence of the bottom margin on paragraphs keeps them their usual distance apart, while still allowing them to snuggle up close to headings.
The odds are very high that what the author expects, given those simple rules, is that the separation between a paragraph and a list will be one em, not two em. If more is desired, then the rules can be adjusted; for example, the margins of the ul
element could be increased.
Having established all that, let's talk about a situation where margin collapsing does something that authors rarely want to see.
Marginal Excesses
The workhorse of tableless layout is the div
element. Sidebars, mastheads, footers, and more all find themselves defined by an ID'ed div
in such designs. Aside from its relative semantic neutrality, the div
is particularly suited to this purpose because it is a flow element, which in HTML is an element that can contain both block and inline elements. In other words, you can put just about any element inside a div
.
The other primary advantage of the div
is that it traditionally generates a block-level box, but has no other inherent presentation. In CSS terms, a browser's built-in style sheet would assert, simply:
div {display: block;}
Astonishingly, this simple bit of style masks a potential layout problem—one that arises not because of something the author does, but more because of what the author neglects to do.
We can illustrate this problem easily enough simply by adding two elements and a small bit of style. Consider the following markup:
<div id="masthead"> <h1>ConHugeCo</h1> <p>Making the world safe for super sizes</p> </div>
To that very simple bit of structure, we'll apply the following very simple styles, with the result seen in Figure 2.
#masthead {background: #F80; margin: 10px;} #masthead h1 {margin: 20px 10px;} #masthead p {margin: 5px 10px; font-style: italic;}
Notice how close the content of the h1
gets to the top of the div
's background area? You might expect that there would be twenty pixels of margin between the top of the h1
and the top of the div
. Instead, the top margin of the h1
is actually "sticking out" of the div
. The two margins actually overlap. The same is true for the bottom margin of the p
element and the bottom margin of the div
.
Let's illustrate this by adding dashed lines to represent the outer margin edges of the three elements.
In Figure 3, the margins of the div
are represented by a dotted gray border, the margins of the h1
by a dotted red border, and the p
element by dotted blue. As you can see, the margins of the h1
and the p
overlap each other, which is normal collapsing behavior. It's the same thing that happens between successive paragraphs. You can also see that the top margin of the h1
sticks out of the masthead's content area, overlapping and exceeding the top margin of the div
. A similar thing happens with the bottom of the p
element.
You might well wonder why this is anything resembling a good idea. To understand it, let's take that paragraph and put it into a different set of circumstances.
Looking At Lists
The style and markup that will illustrate this point are as simple as our earlier example:
li {background: #FB8; margin: 2px;} p {margin: 10px;} <li> <p>This is a paragraph in a two-paragraph list item.</p> <p>This is also a paragraph in a two-paragraph item.</p> </li>
For the purposes of this exercise, we'll assume the list item is part of an unordered list. List items can contain paragraphs because they, like div
s, are flow elements. In fact, a list item can contain any markup that a div
can contain.
Here's how the markup is rendered, with additional dotted lines to mark where the margins fall.
See how the paragraph margins stick out of the top and bottom of the list item? It's the same behavior that we saw with the masthead example earlier in the article. Now notice how the first line of the first paragraph lines up nicely with the list item's marker (the filled bullet symbol). That can only happen because of margin collapsing, which is what's allowing the paragraph's top margin to stick out of the list item. Imagine that margins were prevented from doing this. If that were true, the result could very easily be what we see in Figure 5.
Nobody would want that to happen: it just doesn't look good. On the other hand, this is exactly what we want to have happen in the masthead: to have the margins sit inside the masthead, not collapse to the outside. So how do we keep the margins from sticking out of the masthead? There are a few ways, as it happens.
Uncollapsing
In order to "un-collapse" the margin, there are two basic solutions: padding or borders. The key is to place them appropriately. Which solution is better will depend on the design requirements for a given project.
In either case, you need to apply the padding or border to the masthead itself. Let's consider padding first. We'll add the smallest possible padding to the masthead, just one pixel on the top and bottom and none on the sides, with the result seen in Figure 6.
#masthead {background: #F80; margin: 10px; padding: 1px 0;} #masthead h1 {margin: 20px 10px;} #masthead p {margin: 5px 10px; font-style: italic;}
Because of the padding, the margins of the h1
and p
elements do not collapse with the margins of the masthead. They are instead "contained" within the masthead's content area. This is in accordance with CSS2.1, section 8.3.1[1], but the behavior actually goes back to CSS1. Its intent, as is so often the case, is to satisfy expected authorial desires. After all, that's what drove the margin collapsing behavior illustrated by Figure 4.
Alternatively, you could kill the top margin on the h1
and the bottom margin on the p
element, and create the needed space with padding on the masthead itself, like this:
#masthead {background: #F80; margin: 10px; padding: 20px 5px;} #masthead h1 {margin: 0 10px 20px;} #masthead p {margin: 5px 10px 0; font-style: italic;}
This would create the precise distances that were originally intended. With the one-pixel padding on the masthead shown in Figure 6, the padding "wraps around" all of the content inside the masthead. Thus there would be 21 pixels of space between the top of the h1
's content area and the top of the masthead's background, not 20 pixels. If that's a real problem, then the one-pixel approach won't work. You'll have to use masthead padding and remove the margins instead, as shown in the most recent example.
Another way to keep the margins contained is to set a border on the masthead. This border can be the same color as the page background, thus making it effectively invisible. For example:
#masthead {background: #F80; margin: 10px; border: 1px solid #FFF;} #masthead h1 {margin: 20px 10px;} #masthead p {margin: 5px 10px; font-style: italic;}
In this case, the separation between the visible top of the masthead and the top of the h1
's content area will be exactly 20 pixels, since the border sits outside that margin but blends into the page background—assuming that background is a solid color, of course. If you set the masthead's border to another color, like gray, then you'll find a distance of 20 pixels from the top of the h1
's content area to the inner edge of the top border.
Summary
In the majority of cases, margin collapsing delivers the types of layout results we want. It's only in those cases where we want margins to be "contained" by other elements that things can go a bit awry. Thanks to the way CSS is written, however, there are ways to overcome the unwanted collapsing. By associating either padding or a border with an element, it will "contain" the margins of descendant elements.
- [1] The most relevant portion of this section is the last sentence of the first bullet point, which states: "Note. Adjoining boxes may be generated by elements that are not related as siblings or ancestors."