---
title: What is the best approach for margins in body text?
date: 2024-04-24
tags: [css]
description: By default, browsers add margins both at the top and bottom of elements, which results in excessive whitespace in padded boxes. Is there a better way?
---

By default, browsers add margins both at the top and bottom of elements. That
is not a problem because [adjacent borders
collapse](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing).

However, as we can see in the screenshot below, margins do not collapse when
there is a border or padding, which results in excessive whitespace in padded
boxes.

```css
h2 {
  margin-block: 1.5em 1rem;
}
p, ul {
  margin-block: 1em;
}
```

![Screenshot: browser defaults](01-defaults-combined.png)

## Margins on the bottom

In 2012, [Harry
Roberts](https://csswizardry.com/2012/06/single-direction-margin-declarations/)
proposed that you should only apply margins in one direction, preferably the
bottom. The argument was that margin collapsing is a bit of complexity we just
don't need.

This is the general rule I currently use and that is also employed in popular
frameworks such as bootstrap. Of course there are exceptions: Headings need a
top margin.

For the problem with the padding, Harry recommends to use the `:first-child`
and `:last-child` selectors. [Chris
Coyier](https://css-tricks.com/spacing-the-bottom-of-modules/) added that there
might also be a margin on the last child of the last child (and so on), so you
have to do a little more work then that.

All put together, you end up with code like this:

```css
h2 {
  margin-block: 1.5em 1rem;
}
p, ul {
  margin-block: 0 1em;
}
.box > :first-child {
  margin-block-start: 0;
}
.box > :last-child {
  margin-block-end: 0;
}
```

![Screenshot: Margin on the bottom](02-bottom-combined.png)

## Margins between elements

In 2014, [Heydon
Pickering](https://alistapart.com/article/axiomatic-css-and-lobotomized-owls/)
proposed that margins should not be defined on elements, but between elements.
This can be done by using the adjacent sibling combinator and top margins. The
code could look something like this:

```css
h2, p, ul {
  margin-block: 0;
}
* + h2 {
  margin-block-start: 1.5em;
}
* + p,
* + ul {
  margin-block-start: 1em;
}
```

![Screenshot: Margins between elements](03-top-combined.png)

One benefit of this approach is that it keeps very low specificity, which is an important property for base styles.

On the other hand, I am not convinced that the basic premise is even correct.
Note how there is no margin between the list items in the second list (the one
that contains paragraphs). The visual hierarchy is off here because the margin
inside of the list items creates a bigger separation than the list items
themselves.

You could argue that the list should be in charge of adding margins here. But
the list doesn't know whether it contains paragraphs or not. So maybe just
looking at siblings isn't enough.

I really like this approach. But because of its limitations I currently only
use it in some special cases, e.g. top margins on headings.

## flex gap

The previous approaches are both 10 years old. A lot has happened in the
meantime, so more options have become available. Specifically, I sometimes hear
people say that we should use the `gap` property instead of margins. That could
look something like this:

```css
.container {
  display: flex;
  flex-direction: column;
  gap: 1em;
}

h2 {
  margin-block: calc(1.5em - 1rem) 0;
}
p, ul {
  margin-block: 0;
}
```

![Screenshot: flex gap](05-flex-combined.png)

While I love `gap`, I am not 100% convinced it is the right tool for the job.
Since it only adds space between elements, it has some of the same issues
as the previous approach. Also note how the paragraphs inside list elements do
not have any margins because the flex styling is only applied to the box
itself.

## margin-trim

In 2023,
[Apple](https://developer.apple.com/videos/play/wwdc2023/10121/?time=240)
proposed a new CSS attribute called `margin-trim` that is supposed to finally
fix the problem for good. This would indeed be nice, but so far there seems to
be little interest from other vendors.

## Conclusion

I had already done all the screenshots when I realized I should have included a
nested list. That is another case that frequently produces unexpected results.
At least I was able to include it in the
[codepen](https://codepen.io/xi-the-bashful/pen/qBwgrjB) I used for all the
examples.

It is still hard (impossible?) to produce base styles that just work out of the
box for every kind of content and at the same time don't make it extremely
complicated to overwrite them for custom components.

If we have to live with some edge cases, I feel like excessive margins at the
bottom are more acceptable than excessive margins at the top or missing
margins. So I will stick with the bottom margin approach for now.
