# The case for .space When CSS was introduced in 1996, one of the first features was the concept of margins, borders and padding. Over the years a lot of useful stuff got added. But margins, borders and padding are still here, mostly as initially designed. However, for years webdesigners have been struggling with margins, and white space in general. The problem in short is, how do you create re-usable components with predictable white space, regardless of the contents of such components? Let me show you a concrete example: ```htmlembedded= <div class="ds-box"> Contents of a box </div> ``` Which looks like this: <style> :root { --ds-shadow-light: rgba(0,0,0,0.07); --ds-shadow-middle: rgba(0,0,0,0.09); --ds-shadow-dark: rgba(0,0,0,0.11); --ds-shadow-small: 0 1px 1px var(--ds-shadow-dark), 0 2px 2px var(--ds-shadow-middle), 0 4px 4px var(--ds-shadow-light) ; --ds-shadow-medium: 0 1px 1px var(--ds-shadow-middle), 0 2px 2px var(--ds-shadow-middle), 0 4px 4px var(--ds-shadow-middle), 0 6px 8px var(--ds-shadow-middle), 0 8px 16px var(--ds-shadow-middle) ; --ds-box-radius: 3px; --ds-box-shadow: var(--ds-shadow-medium); } .ds-box { border-radius: var(--ds-box-radius); box-shadow: var(--ds-box-shadow); position: relative; break-inside: avoid; margin-bottom: 1em; display: inline-block; } .ds-box h2 { border: 0; } </style> <div class="ds-box"> Contents of a box </div> The contents of the box are too close to its border. So I'll add some padding: <style> .ds-box-padding { padding: 1em; } </style> <div class="ds-box ds-box-padding"> Contents of a box </div> This works well. Now let me change the content a bit: <div class="ds-box ds-box-padding"> <h2>A heading</h2> <p>with some content</p> </div> As you see, a lot of extra white space has been added at the top and bottom. For normal text this is fine and expected. Extra white space above a heading adds more distance to the text above. This makes it clear that the heading is part of a new section of content. But inside the box you don't need this extra space. It is usually better for all of these boxes to have a similar white space, regardless of the content. ## Enter the lobotomized owl. One trick designers have used is called the [`lobotomized owl`](https://alistapart.com/article/axiomatic-css-and-lobotomized-owls/). It looks like this: ```css= * { margin-top: 0; } * + * { margin-top: 1em; } ``` This set of CSS rules removes the top margin of all elements, and re-introduces a standard top margin for all elements that have a previous sibling. So in the case of our box, the results are: <style> .ds-box.ds-box-lobotomized > * { margin-top: 0; padding: 0; } .ds-box.ds-box-lobotomized > * + * { margin-top: 1em; } </style> <div class="ds-box ds-box-padding ds-box-lobotomized"> <h2>A heading</h2> <p>with some content</p> </div> This works for the heading, but the paragraph still adds a bottom margin. Since we've standardized the margin-top, lets remove the bottom margin for all elements: <style> .ds-box.ds-box-lobotomized2 > * { margin: 0; padding: 0; } .ds-box.ds-box-lobotomized2 > * + * { margin-top: 1em; } </style> <div class="ds-box ds-box-padding ds-box-lobotomized2"> <h2>A heading</h2> <p>with some content</p> </div> And voila. Uniform white space around the content of the box. But now the problems start. What happens if we add an image inside the box, before the heading and float it left? <div class="ds-box ds-box-padding ds-box-lobotomized2"> <img src="https://placekitten.com/20/30" style="float: left; margin-right: 0.5em;"> <h2>A heading</h2> <p>with some more content</p> </div> The image is the first child element of the box, so its margin is set to 0. But now the heading has a previous sibling, the image, and its margin-top is set to `1em`. You can work around this by moving the image inside the heading. But there are many other cases where the lobotomized owl (`* + *`) selector won't help you. I've been using it for quite some time and I've always had to add extra exceptions to the CSS of projects that used it. The main problem is, I think, that this trick disables a feature of the webbrowser that isn't well understood, but is vital for correctly rendering text: margin collapse. ## Margin collapse Below is an example of a margin collapse in action - or well, emulated here. The yellow background is the margin of the heading, the blue background shows the margin of the paragraph. Between the heading and the paragraph, the colors overlap, because the margins overlap. This is referred to as the margins collapsing. <style> .clean p { margin: 1em 0; background: white; } .clean h2 { border: 0; padding: 0; margin: 1em 0; background: white; } .clean .margin-up { position: relative; top: -1em; } .clean .yellow { background: rgba(255,255,0,0.5); display: flow-root; } .clean .blue { background: rgba(137, 196, 244, 0.5); display: flow-root; } </style> <div class="clean"> <div class="yellow"> <h2>A heading</h2> </div> <div class="blue margin-up"> <p>A paragraph</p> </div> </div> The bottom margin of the heading is slightly larger than the top margin of the paragraph. So the top margin of the paragraph is fully collapsed. The collapsed margin between the elements becomes the size of whichever margin is bigger. This isn't just the case for siblings. It also works when an element with a margin is contained inside another element, like this: ```htmlembedded= <h2>A heading</h2> <div> <p>A paragraph</p> </div> ``` As long as the `<div>` doesn't have padding or a border. There are some more exceptions, but these two are the most common. The top margin of the paragraph is automatically applied to the `<div>`, where it is again collapsed with the bottom margin of the heading. Below is a visual example: <style> .clean .outline { border: 1px solid purple; } </style> <div class="clean"> <div class="yellow"> <h2>A heading</h2> </div> <div class="blue margin-up"> <p class="outline">A paragraph</p> </div> </div> The purple line is the edge of the `div` element. The margin of the paragraph extends outside this edge. Note however, that this effect is gone whenever you add a border or padding to the `div`. The example shows the top and bottom margin collapsing, but not the left or right. That is because those margins never collapse. Only the top and bottom margins have this behaviour. This makes sense, since it was designed for the normal flow of text in a webpage. ## Enter .space So lets use collapsing margins to fix the space issues we have with our box. ```css= .space { margin: 2rem; } ``` ```htmlembedded= <div class="ds-box"> <div class="space"> <h2>A heading</h2> <p>A paragraph</p> </div> </div> ``` <style> .clean .space { margin: 2rem; } </style> <div class="clean ds-box"> <div class="space"> <h2>A heading</h2> <p>A paragraph</p> </div> </div> I've added an extra wrapper with the class `space`. This adds a margin around the content, but within the `ds-box` component. The margin of `space` is larger than both the top margin of the heading and the bottom margin of the paragraph. Because of this, both those margins collapse over the `space` margin and disappear. And let's add that floating image back again: <div class="clean ds-box"> <div class="space"> <img src="https://placekitten.com/20/30" style="float: left; margin-right: 0.5em;"> <h2>A heading</h2> <p>with some more content</p> </div> </div> And now the white space is still consistent! So at the cost of one extra HTML element, the `ds-box` component is much more robust. You don't need to add extra CSS exceptions when you change the content of the box component. And you can re-use the `space` element in any other component you design. This has the added benefit of having consistent spacing in all your components. There are some things to consider though. For one, the space margins must be larger than the largest margin of any piece of content you add inside it. So if you really need just a tiny bit of white space, you'll have to go back to the lobotomized owl again. The modern design trend of large amounts of white space helps, however. Another problem is that some modern display modes, e.g. flex and grid, don't use collapsing margins. If you need to use those _and_ a consistent white space, you should add a space container inside each area inside a grid or flexbox container. Or you can lobotomize an owl again. The last two years I've been using a `space` container in most of my CSS work. Only very rarely have I needed to fix white space issues. In general the problem has just disappeared. I hope this trick can help you get more done with less CSS as well! Auke van Slooten auke@muze.nl