Where to apply spacing (such as margins) in CSS

An important part of CSS principles is knowing where to apply spacing in CSS. This article focused on situations where you’d apply spacing like margins, rather than situations where you’d apply padding.

In this article, we’ll look at:

  1. A brief description of the rule
  2. Some examples
  3. Why you should follow this rule. Some advantages and disadvantages.

Brief description of the rule

Where do you apply spacing like margins in CSS?

CSS for spacing should be added to space apart siblings.

For example, if you want to add space between two section elements of the website, there are two options:

  • Add a margin-bottom to the first section or a margin-top to the first section, or a combination of margin-top and margin-bottom on both section elements. That way, you’re adding spacing to the sibling elements directly.
  • Or, alternatively, the immediate parent of those two elements can space them apart. For example, add display: flex; row-gap: 1rem; to the parent of the two elements.

Important: The spacing must be added either on the siblings you want to space apart (the section elements), or their immediate parent. You must avoid adding spacing anywhere else, such as a child or descendant of the section elements.

Another way of thinking about it is to "move styling up". Apply margins to the highest level elements, or the earliest ancestors, possible. Avoid adding margins to descendant, low-level elements if you can add them to higher level elements.

Examples

Consider the following code example. It’s a website with 3 sections:

See the Pen
CSS spacing example
by Spyros Argalias (@sargalias)
on CodePen.

Consider that you want to add more vertical space between the final product and the "Get in touch" heading. Which element should you add margin to?

The rule is that you must always add space between siblings.

Which two elements are the sibling elements that you can space apart to achieve the desired effect?

In this case, it’s the two section elements.

The two section elements are siblings and adding a margin to either of them creates the desired effect. So, you must add margin-top to the second section element, or margin-bottom on the first section element.

You must avoid adding this margin to space apart elements which are not siblings. In this case, avoid adding a margin-top to the "Get in touch" h2 heading. Yes, it will create the desired effect, but it doesn’t have a sibling above it. The only element that appears before it in the code is the section element, which is its parent, not its sibling.

Similarly, you must also avoid adding margin-bottom to the product elements. Again, the final product element doesn’t have a sibling that comes after it. For the product elements before it, you should only add margin to them to create space between them and other products. That’s not the goal here. The goal is to create space between the products and the "Get in touch" heading.

So, one of the two section elements is the only option. You add a larger margin to one of them, so it creates more space between its sibling (the other section element), which achieves the desired effect of creating the whitespace you want.

Example 2:

Consider the contact form.

At the moment, there is a line with the label "Name:" and the text input field next to it. Under it, there is a line with the label "Email:" and an email input field next to it. Finally, there is a label for "Message:", with a textarea element below it.

What should you do to increase the space between the line with the "Name" field and the line with the "Email" field?

There are two sibling div with the class of form-control. Adding a margin to one of them achieves the desired effect. That satisfies all the conditions.

There are label elements. Adding a margin to them would also achieve the desired effect. However, they don’t have siblings, so, that’s not the answer. But, even if the input elements were their siblings instead of their descendants, we’re not interested in spacing apart the label and the input. That wouldn’t be the answer either.

Similarly with the inputs. Adding margin to them would work. However, they don’t have siblings.

The answer is to add margin to the div with the class of form-control.

Example 3:

Within the product component, what if you want to increase the spacing between the product heading and the product description?

Answer: You add margin to one of them. The product heading and product description are siblings. Adding margin to one of them would have the desired effect. So, that’s the answer.

Example 4:

What if you want to increase the space between the product description and the bottom border of the product?

Answer: You add padding to the div with class product. The product description doesn’t have a sibling that comes after it to add space between them, so that’s not the answer.

Alternative solutions to the examples

Along with adding margin between siblings, you can also have their immediate parent control the spacing between siblings.

For example, to increase spacing between the products and the "Get in touch" heading, we concluded that you must add margin to one of the section elements. Alternatively, you can make their parent control the spacing between them. For example, you can give the body element display: grid;, and use properties like grid-template-rows or gap to add enough spacing between the two section elements.

The rule

To add spacing between things:

  • Add spacing / margins between siblings
  • Let the parent of the two siblings add spacing between them

Another way of thinking about it is to "move styling up". Apply margins to the highest level elements you can. Apply margins to the earliest ancestors possible. Avoid adding margins to descendant, low-level elements if you can add them to higher level elements.

Why

There are many reasons.

Relating to programming principles

One reason is that adding spacing elsewhere breaks many fundamental programming principles.

KISS, separation of concerns, the principle of least knowledge, reusability.

KISS (keep it simple stupid):

If you want to add space somewhere between two siblings:

The simplest place to add it is on the siblings.

Alternatively, you can add it to some descendant. In many cases, where you have a long chain of nested components, it will be a very deep descendant. Later on, when you’re searching either the code or the browser element inspector for where the space is, it will take much longer to find. Further, it will be in an unexpected place. It’s expected that it should be between the siblings you’re spacing apart, not in some descendant.

Separation of concerns: Why was some random descendant modified? It’s not some random descendant’s concern that some random ancestor needs spacing between itself and its sibling.

Principle of least knowledge: This one follows from the other two. You won’t know where the space is because it’s not at the default place it should be. Also, a developer that’s reading the code will see that some random element has a large margin. They won’t immediately understand why. They won’t understand that the reason is that some random ancestor has a sibling element it needs to be spaced away from. In comparison, if you add margin between the two siblings that need it, the reason why is immediately understandable.

Reusability: Some descendant element/component now has a large margin. It makes it more difficult to reuse if it normally needs a smaller margin. You’ll need to create a "modifier" class so that it can have the normal margin most of the time, but have the larger margin when the modifier is present. This is more complicated with component-based development. You’ll need to pass arguments along a long chain of components so that it reaches the descendant which needs the modifier. Adding margin between the two earliest siblings would be much simpler.

Practical reasons

Here are some more practical reasons:

In the code example, we had a section, with 3 descendant product components and a sibling section element. The goal was to add space between the two sections.

One way to do that would be to increase the margin on the product components. However, the downside is that it also affects the spacing between the product components, not just between the two sections.

Another way would be to add a margin-bottom to just the final product component with some CSS like .product:last-child {margin-bottom: 2rem;}. This has other issues.

One issue is that, depending on what flex-order you use, the extra margin might be in the wrong place visually.

Another issue is that, when you look at the CSS for the product component, it’s not obvious why that code is there. That’s because that code isn’t relevant to the product component. The only relevance of the code is to handle spacing for elements that are around it, elements separate from the product component.

Also, depending on where on the product component is used and what elements/components are around it, the increased margin bottom may or may not be needed. Sometimes it will be needed, other times it won’t. To handle this, you’ll need to do something like introduce a modifier class. You’ll need HTML like <li class="product product--larger-margin">. Also, different margin-bottom may be needed each time, so you might need multiple modifiers. Finally, the spacing may relate to a very distant ancestor (many parent elements away in the HTML). If you’re using a component-based library like React, that means that you’ll have to prop drill an argument many components down for the product component to have the modifier class.

Another issue is that sometimes, the product component won’t be the last child. A different HTML element could be the last child. In this case, you’d need to do something like section > *:last-child {margin-bottom: 2rem;} to select any element that’s the last child. That has issues too. Depending on what the last child is, how it looks, and how much margin-bottom it has by default, you might need a different margin-bottom. Or, if the last child is an inline element like a span, vertical margin won’t apply.

All of these issues are bypassed if you put the margin on one of the two section siblings instead of the descendant product component. In particular, there will be no or minimal prop drilling, because the margin is applied on the highest level HTML elements possible.

Final notes

So that’s it for this article. I hope that you found it useful.

As always, if any points were missed, or if you disagree with anything, or have any comments or feedback then please leave a comment below.

Alright, thanks and see you next time.

Glossary

A quick definition of siblings is two HTML elements that have the sama parent element and are located at the same level in the hierarchy. For example, the p elements in this code sample are all siblings:

<div>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
  <p>Paragraph 3</p>
</div>

A quick definition of "component" is a set of elements that, together, make up a thing. In the example code, the "product component" is the set of elements, including the li, img, h3 and p tag that together make up a thing we would call a "product" or a "product card".

Credits

Image credits:

  • Pile of brown wooden blocks: Photo by Volodymyr Hryshchenko on Unsplash
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments