--- 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; } > :first-child { margin-block-start: 0; } > :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 borders 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 approach in this case. 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.