Charles Harries

| Posts

Thoughts on margins

Some thoughts on how to use margins in CSS. Needlessly opinionated.

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/-leftand 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 and margin-block-start. In fact, you should probably be using these ones (margin-inline-start and margin-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 and margin-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:

Search buttons
Search buttons

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.

Web