Would you like to buy me a ☕️ instead?
Years of writing and maintaining CSS code and remarks by Mark Dalgleish and Adam Wathan, along the same line, lead me to conclude that the spacing between HTML elements should be determined by their parent element in almost all cases.
<ul class="nav">
<li class="nav-item"><!-- ... --></li>
<li class="nav-item"><!-- ... --></li>
<!-- ... -->
<ul>
<style>
/* ❌ Bad */
.nav-item:not(:first-child) {
margin-top: 1em;
}
/* 🆗 Better */
.nav > * + * {
margin-top: 1em;
}
/* ✅ Best */
.nav {
display: flex;
flex-direction: column;
gap: 1em;
}
</style>
I think the example above is probably the least controversial one. In the case of repeated elements, it seems like a no-brainer to put the information, about spacing between repeated elements, on the parent element.
But I’d even go one step further and say that putting the spacing information on a shared parent element in almost all situations should be preferred over putting it on the element itself.
<body class="the-page">
<header class="the-header">
<!-- ... -->
</header>
<main class="the-main-area">
<!-- ... -->
</main>
<footer class="the-footer">
<!-- ... -->
</footer>
</body>
<style>
/* ❌ */
.the-main-area,
.the-footer {
margin-top: 3em;
}
/* ✅ */
.the-page {
display: flex;
flex-direction: column;
gap: 3em;
}
</style>
But this is a perfect world scenario: what if the spacing between the-header
and the-main-area
must be different than the spacing between the-main-area
and the-footer
?
Layout Components
We can make use of generic layout components to help us dealing with more complex situations more easily.
<body class="the-page stack stack--xl">
<header class="the-header">
<!-- ... -->
</header>
<div class="stack stack--l">
<main class="the-main-area">
<!-- ... -->
</main>
<footer class="the-footer">
<!-- ... -->
</footer>
</div>
</body>
<style>
.stack {
display: flex;
flex-direction: column;
gap: 1em;
}
.stack--l {
gap: 2em;
}
.stack--xl {
gap: 3em;
}
</style>
Although this makes our markup more complex, I think it can be worth it in certain situations to make this trade-off.
What Are the Advantages of Putting the Spacing on the Parent Element?
But why should we? Why should we add unnecessary wrapper <div>
s to our HTML code? Because code written that way is much more resilient! Spacing is contextual. It depends on the larger context but also on whether a particular sibling element is currently displayed or not.
Consider the following example (written in a pseudo HTML templating language syntax):
<div class="stack">
{#if shouldShowIntro}
<p>{intro}</p>
{#endif}
{#if shouldShowAd}
<div class="ad">
<!-- ... -->
</div>
{#endif}
<div class="content">
<!-- ... -->
</div>
</div>
Thanks to using the stack
layout component from before, our layout works regardless of which combination of shouldShowIntro
and shouldShowAd
is currently at work. If we’d put the spacing onto the ad
component, it would break if shouldShowIntro
is false because then ad
would be the first element.
Exceptions
My rule of thumb is to default to using layout components like stack
to determine the spacing between elements, but I’m not dogmatic about it. If I have the feeling that doing so makes the implementation significantly worse without making the system more resilient, I might choose to diverge from this rule. In the the-page
example, I might decide to use the stack
component as long as the spacing between all children is equal. Still, if the spacing is different between certain elements, I might choose to break the rule in favor of simpler markup.
<body class="the-page">
<header class="the-header">
<!-- ... -->
</header>
<main class="the-main-area">
<!-- ... -->
</main>
<footer class="the-footer">
<!-- ... -->
</footer>
</body>
<style>
/* ✅ This is probably fine too. */
.the-main-area {
margin-top: 3em;
}
.the-footer {
margin-top: 2em;
}
</style>