Nowadays, CSS has a wide range of tools and properties that allow designs that, until a few years ago, seemed impossible to implement. Many developers focus on learning this style language to the smallest detail, which can be quite challenging for some.
Despite not being considered a programming language by most, it's important to note that it's also a part of many applications. As developers, we should be responsible, making the code we develop as easy to understand, modify, and maintain as possible. CSS is no exception.
The goal of this post is to shed some light on a well-known property in CSS: margin. We will discuss its sometimes incorrect usage and propose alternative approaches that are much more maintainable and clean.
โ When not to use
The main reason for not using margin lies in a matter of responsibility. When designing the layout of an application or an individual component, we should consider who is responsible for what.
(For the sake of the post, we will omit border styles and other details).
Between parent and children
Let's imagine that we want to implement the following layout. The goal is to position the blocks as shown in the image. Who is responsible for placing the child at a certain distance from the parent: the child or the parent?
It's the parent who should define the distance at which their children are positioned. This is the key. The responsibility lies within the parent block. If we wanted to change the content of the parent, we would probably want to preserve the distance that their children have from it, so these styles should belong to the parent.
.parent {
padding: 24px;
}
.child {
/*No margin needed*/
}
Of course, there are exceptions. It's possible that we may need to create a layout where the children are not at the same distance to the parent, as shown in the following image.
In this case, it's possible to approach the design as follows: the parent of the last two elements is not the same as the parent of the first element. We can create another element to serve as a wrapper for the last two elements and apply the "extra margin" they have as padding.
.parent {
padding: 24px;
}
.wrapper {
/*Extra padding for the last two children*/
padding: 24px;
}
.child {
/*No margin needed*/
}
Note: The borders are shown simply to illustrate the blocks we would have at code level. The presence of an additional parent block would be invisible to the application end-user.
Between siblings
You may have noticed a detail in the previous layout: the child elements are also separated from each other.
This separation can be achieved by applying top or bottom margin to all of them. However, we come back to the same question: who should be responsible for separating the children from each other? Well, surprisingly, it should be the parent.
But, from the parent, how can we separate children from each other? The best option is to harness the power of the Grid Layout.
This post doesn't cover the use of this type of layout in detail. In summary, the Grid Layout allows us to treat the children of an element as if they were in a grid, organizing how they are positioned relative to each other. Among its options, it's possible to determine the exact distance at which elements are positioned from each other. How? Well, by using the gap
property.
.parent {
display: grid; /*Changes the display layout to grid*/
gap: 24px; /*Desired gap between elements*/
padding: 24px;
}
.child {
/*No margin needed*/
}
The major advantage of using this alternative is that the gap
property adds space only between the elements, neither at the beginning nor at the end. If we were to use margins for each element, we would need to ensure that these margins are not applied incorrectly to the first or last element to avoid unnecessary spacing.
Amazing! Isn't it?
โ When to use
There are some occasions when using margin is not a bad alternative.
Margin: auto
If an element uses margin: auto
, it will be centered horizontally with respect to its parent. However, there are several considerations to keep in mind:
- This only works for centering elements horizontally, not vertically.
- Inline elements (
<a/>
,<em/>
,<span/>
, etc) cannot be centered using this declaration. It only works for block elements. - We come back to the same question again: who is responsible for centering the element? In this case, the element would be centered by itself, not by its parent.
- There are other properties within the flex and grid layouts that allow both vertical and horizontal centering (
justify-content
,align-items
, etc), which could be more appropiate.
Despite these disadvantages, sometimes using margin: auto
is simpler and quicker than any of the alternatives, so it's not a bad choice.
Overlapping
When it comes to overlaying elements, using margins can be very handy.
To achieve the layout from the previous image, we can apply a negative margin to the child element (such as margin-left: -120px
), causing it to be displayed overlapped with its parent element.
There are other alternatives, such as the position
property, which allows to position elements relative to another element. However, using this property may, among other things, cause the child element not to respect the parent's padding.
In this case, using negative margins is much simpler and more straightforward.
I hope you found this post interesting and that it helps you in your programming projects. The proposed alternatives are just one of the many options that CSS offers. In programming, there is not a single way to do things. In CSS, neither is there.
If you have any questions or want to share any ideas, please leave a comment below.
Thanks for your time!
Top comments (21)
Maybe itโs worth mentioning that
gap
is short forcolumn-gap
androw-gap
. Use them individually for finer control. A quick, row-based grid can be as simple as:In addition, gap is no longer exclusive to grid, can also be used for flexbox as well
It's a good post, and something for consideration indeed. It somehow goes against my intuition in, for instance, the flow of paragraphs in a document, though. It feels natural to define distance between paragraphs with margin.
You claim that "The responsibility lies within the parent block." but I fail to see a good justification for this. Why should the responsibility rest on the parent element? I guess this is a rule you have made following practical observations?
If you just apply margins to child elements, you'll end up with an extra margin in the last element. To avoid that, you may use the lobotomized owl selector to select only those that have siblings, eg:
* + *
or use the:not
selector, eg:.element > *:not(:last-child)
, or even worse - redefine the last element's margin with an class or!important
further in the code.This seems like added complexity to me, if you want to achieve a similar result to
gap
.I agree that if what you want to achieve is
gap
, you should just usegap
. :)If you were to replace a paragraph with a new one or another, the margin separating it from the rest should still be there, right?
If that's the case, the margin will remain independent of the paragraphs it separates, so, from my point of view, it should be the parent that separates them.
The paragraph spacing exists due to the layout that displays them (the parent).
Flexbox supports gap, too. It is not too complex and works most of the time.
CSS Grid is better for complex layouts. But that doesn't mean you could use it for simple layouts, too.
Margin: auto; works fine when the container has a limited width. This can be archived now with almost the same lines of CSS code with grid. And then you get that additional superpower layout tool.
About gap: you can use different gaps for the children. There are now less reasons to use margin for layouts.
About the negative margin, Bootstrap has a use case!
In the
row
element, Bootstrap uses negative margins to adjust the outer elements additional horizontal spacing due to the.container
class horizontal padding.Thanks for the great post, its definitely something that comes to mind often when creating layouts, back in the day it was quite a bit more difficult without flexbox and grid.
How would you classify a case where we set all children to have a certain margin but from within the parent selector like so:
Would the above still be classified as the child interfering with what should only be on the parent to set.
Thanks!
Hi! I'm not quite sure about the purpose of that padding. I understand it could be for two reasons:
If that
padding-bottom
is meant to separate the children from each other, you should remove the padding from the last child so that it doesn't add extra space after it. In this case, I would opt for using thegap
property in the grid layout.If you simply want to add padding to the children (whether it's bottom, top, right, or left), I would suggest adding that padding directly to the children, like this:
If you are using the
.child
class for other elements and only want the ones inside the parent to have this padding, you can separate the styles into two different classes: one for those inside the parent and another for those that aren't.You may think that, with this approach, you would have duplicated styles. That's why I love using styled-components, because it allows you to create styles through composition. Here's an example:
Personally, I'm not a big fan of nesting styles. It's just an opinion.
I hope I've answered your question. Thanks a lot for the feedback :)
For your sibling example I would argue that grid is overkill and margin is a better approach. Especially if you need different spacing between the siblings, margin collapse is a benefit.
grid is a wonderful thing for actually creating grid layouts, and in that context the above solution makes sense. But for simpler/smaller content
margin-collapse
is a more compact and saner approach, especially since both grid layouts andmargin-collapse
end up calling the same 'first/last element' check function in the renderer. This isn't like deprecatingfloat:
because it introduced more complexity to use it than later solutions.You can still use
gap
and add spacing where needed, the element will offset normally. Also, can't see grid as an overkill since it's mor extensible, reliable and robust API. Nothing better to avoid creating additional media queries just to redefine absolute margins in mobile viewports.Between parent and child, sure. But what about when some siblings should be spaced differently than others? This is very common, and gap doesn't cut it.
I used this way to keep document harmonic flow.
Just like that!
Paddings are not always applicable either. It heavily depends on the design and the page structure.
Opt for padding over margin in CSS for cleaner, more efficient code. This is crucial advice, especially for app development company striving for seamless UI/UX!
Some comments have been hidden by the post's author - find out more