Thoughts on margins
the tl;dr
I only use margin-left
and margin-top
because elements should be responsible for positioning themselves relative to their siblings, rather than making their siblings responsible for positioning them. Downsides only really make themselves apparent in non-RTL languages—and that's solved by margin-inline-start
and margin-block-start
.
Think in single directions
Trying to think in multiple directions, especially when it comes to margins, is probably one of the canonical CSS footguns. Trying to think your way through your layout with both margin-right/-left
and margin-top/-bottom
is hard enough without also having to consider margins collapsing on themselves. Better to think in single directions.
And I think that the single directions to think in are margin-left
and margin-top
. Don't use margin-right
or margin-bottom
. For any use case you can come up with for using -right
or -bottom
, I can (probably) come up with a better, more declarative, and more maintainable solution using -top
or -left
.
Same goes for
margin-inline-start
andmargin-block-start
. In fact, you should probably be using these ones (margin-inline-start
andmargin-block-start
) instead of the 'traditional' margin directions anyway, since they take into account the user's preferred text orientation. They're supported by all browsers except for Internet Explorer, but even Microsoft has started dropping support for IE.
CSS reads left-to-right, top-to-bottom
We read top to bottom, and left to right, so whatever comes next will always be to the right of, or after, the thing that comes before it, if that makes sense. For that reason, I prefer to have the thing that comes aftertake responsibility for positioning itself relative to its siblings.
CSS agrees with me: that's what the +
and ~
sibling combinators are for. These things can only work on following elements—that is, elements that come after other elements.
So for example, p + img
will work on any <img />
tags directly following a <p>
tag. CSS won't let you style parents or previous siblings based on later siblings.
An exception to this is using a
:first-child
selector, but that's still following-element-agnostic, so it's not really an exception.
Using these sibling combinators gets you out of a lot of situations where you'd wind up reaching for :first-child
or :last-of-type
or something to remove margin that you've placed elsewhere, on e.g. inline nav items:
/* Instead of this... */
nav > ul > li {
margin-left: 10px;
}
nav > ul > li:first-child {
margin-left: 0;
}
/* ... try this! */
nav > ul > li + li {
margin-left: 10px;
}
Exceptions
A major exception is, of course, languages that aren't read left-to-right, like Arabic, or East Asian languages in certain settings. If you're reading right-to-left, you'll probably get a bit more luck out of using margin-right
a lot more than margin-left
.
The point above is moot if you're using
margin-inline-start
andmargin-block-start
, like I told you to.
Another common use of margin-right
is when building bits of interface with optional preceding elements, like a button with an optional preceding icon:
Two buttons reading "Search", one with a preceding magnifying glass, the other without.
A bad solution would be to always add margin-left
to the button text; if the icon didn't display, it would position the text awkwardly to the right of center.
.button__text {
margin-left: 10px;
}
A slightly better solution would be to add some margin right to the icon; if it doesn't show up, no margin is applied and the button text sits nicely in the center.
.icon {
margin-right: 10px;
}
The issue here is that any other icons on the site will now have the same margin-right
applied to them. You could maybe use the descendent selector like .button > .icon
, but what happens if you have a button without a .button__text
? You could also create a new class .button__icon
, but this has the same pitfalls as before, unless you don't apply the .button__icon
class to buttons without text? And now you're down a rabbit hole.
The best solution would be to tell CSS exactly what you want: margin-right
on any .button__text
text coming after an .icon
:
.icon + .button__text {
margin-left: 10px;
}
This allows you to continue thinking in single directions, is probably the most declarative solution, and allows you to continue using .button > .icon
and .button > .button__text
throughout the rest of your application without worrying about weird alignment issues.
All to say...
Drop margin-right
and margin-bottom
. They're weird, they encourage weird spacing behaviour, they lead to wonky edge cases, and the tools that CSS provide solve most of the problems you'll be solving with margin-right
and -bottom
a whole lot better.
Next
I'm extremely tired of the powerful in tech holding the rest of us hostage in the name of innovation.
Previous
Some meandering thoughts on what it means to be an effective person in a tech career.