DEV Community

Cover image for Don't use margin in CSS
Antonio Moruno Gracia
Antonio Moruno Gracia

Posted on • Updated on

Don't use margin in CSS

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?

Layout with one block as child

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*/
}
Enter fullscreen mode Exit fullscreen mode

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.

Layout with many blocks as children

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.

Adding wrapper to the last two children

.parent {
    padding: 24px;
}

.wrapper {
    /*Extra padding for the last two children*/
    padding: 24px; 
}

.child {
    /*No margin needed*/
}
Enter fullscreen mode Exit fullscreen mode

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.

Container with elements positioned using grid layout

.parent {
    display: grid; /*Changes the display layout to grid*/
    gap: 24px; /*Desired gap between elements*/
    padding: 24px;
}

.child {
    /*No margin needed*/
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. This only works for centering elements horizontally, not vertically.
  2. Inline elements (<a/>, <em/>, <span/>, etc) cannot be centered using this declaration. It only works for block elements.
  3. 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.
  4. 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.

Container that overlaps its parent

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)

Collapse
 
madsstoumann profile image
Mads Stoumann

Maybe it’s worth mentioning that gap is short for column-gap and row-gap. Use them individually for finer control. A quick, row-based grid can be as simple as:

.grid {
  display: grid;
  row-gap: 1em;
  &>* { margin-block: unset; }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
theklr profile image
Kevin R

In addition, gap is no longer exclusive to grid, can also be used for flexbox as well

Collapse
 
damian_cyrus profile image
Damian Cyrus

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.

Collapse
 
lebbe profile image
Lars-Erik Bruce

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?

Collapse
 
lixeletto profile image
Camilo Micheletto

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.

Collapse
 
lebbe profile image
Lars-Erik Bruce

I agree that if what you want to achieve is gap, you should just use gap. :)

Collapse
 
moruno21 profile image
Antonio Moruno Gracia

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).

Collapse
 
lixeletto profile image
Camilo Micheletto

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.

Collapse
 
koga73 profile image
AJ

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.

Collapse
 
andrew_roazen profile image
Andrew Roazen

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 and margin-collapse end up calling the same 'first/last element' check function in the renderer. This isn't like deprecating float: because it introduced more complexity to use it than later solutions.

Collapse
 
lixeletto profile image
Camilo Micheletto

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.

Collapse
 
dsaga profile image
Dusan Petkovic

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:

.parent .child {
    padding-bottom: 5px;
}

Enter fullscreen mode Exit fullscreen mode

Would the above still be classified as the child interfering with what should only be on the parent to set.

Thanks!

Collapse
 
moruno21 profile image
Antonio Moruno Gracia • Edited

Hi! I'm not quite sure about the purpose of that padding. I understand it could be for two reasons:

  1. 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 the gap property in the grid layout.

  2. 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:

.child {
    /*Whatever padding you want to add*/
}
Enter fullscreen mode Exit fullscreen mode

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:

const Child = styled.div`
    /*Common styles for all children*/
`

const ChildInsideParent = styled.div`
    ${Child}    /*You add here the common styles via composition*/
    /*Now you can add all the styles that you want without afecting the base Child component*/
    padding: 8px;

`
Enter fullscreen mode Exit fullscreen mode

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 :)

Collapse
 
thejase profile image
Jason Featheringham

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.

Collapse
 
pvivo profile image
Peter Vivo

I used this way to keep document harmonic flow.

<article class="grid gap-2">
  <h1>Why I don't use margin?</h1>
  <p>Because I use grid gap instead - mainly with tailwind</p>
  <section class="grid gap-1 rounded p-1 bg-gray-900  text-gray-300 text-[.7em]">
    <p>--  dense list of</p>
    <p>--  something</p>
    <!-- lot more -->
  </section>
  <p>This solution give well organized gap between different</p>  
  <p>section of doc, desktop publishing way</p>
</article>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
moruno21 profile image
Antonio Moruno Gracia

Just like that!

Collapse
 
pvivo profile image
Peter Vivo
<article class="grid gap-2">
  <h1>Why I don't use margin?</h1>
  <p>Because I use grid gap instead - mainly with tailwind</p>
  <!-- lot of other content -->
</article>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
drew-k profile image
Andrew

Paddings are not always applicable either. It heavily depends on the design and the page structure.

Collapse
 
appdevelopmentcompanyappingine profile image
AppDevelopmentCompanyAppingine

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!

Collapse
 
npeete profile image
Péter Novák

One annoying thing with gap is that it adds around elements with display:none as well. Is there another solution except adding the margins to the children in these cases?

Collapse
 
jaelyn_jdouglas profile image
Jaelyn Tyson

Slay

Some comments have been hidden by the post's author - find out more