
-
The Issue
Something is stretching your nice mobile layout beyond your expected 560px width. The layout displays mostly nice, but you can drag the entire site to the left and see extra space. You keep toggling open elements in the inspector trying to find the culprit lurking in its hideout plotting its next caper. You futz about with overflow: hidden. You get tricky, winnowing down widths until it all fits: "Okay, we've got 20px on each side, so you're now, uh, width: 520px." You start floating and clearing and swearing.
A possible reason is that the standard box model calculates dimensions only for elements themselves, excluding padding and borders. You might have assigned a 560px width to a container, but padding and borders push it wider as a result.
Solution
Change your box-sizing. A number of developers prefer changing the box-sizing from content-box to border-box, which includes padding and borders in width calculations. When I have 560px of space, I usually want its contents to be no more than that.
There are a couple ways to do this. The classic method is to set everything to border-box:
*, *::before, *::after { box-sizing: border-box; }
This will work nicely in just about all cases, although when you do want something to behave in the standard content-box way, you'll have to override it and all its children. This is the current standard, which is more inheritance-friendly:
html { box-sizing: border-box; } *, *:before, *:after { box-sizing: inherit; }
Other Notes
• Margin is not included, and we typically don’t want it to be. We expect margins to be "outside" an element. I do, anyway. I'm sorry if this, um, marginalizes them.
• The * selector does not include pseudo-elements by default, so if you use the first method, include them.
• This whole border-box behavior used to be "Quirks Mode" for IE6 and earlier. So, IE won that aesthetic round.
-
The Issue
So you’ve got two elements sitting atop each other, each with vertical margins. You would expect the margins to add together:
.thing { display: block; width: 100%; height: 200px; } .thing-upper { margin-bottom: 50px; } .thing-lower { margin-top: 30px; }
… so the space between them is expected to be 50 + 30 = 80 pixels. Except they’re not.
When two vertical margins meet, they usually collapse; i.e., in most cases, where the margins of two elements meet, only the larger value is preserved (or if equal, only a single value).
This is the normal flow of the box model, and there are specific cases governing this collapse (blocks with margins but no content, adjacent siblings, etc.).
Solution
Maddening before one knows what's going on, but it’s workable.
The simplest method is to stick with either top or bottom. I prefer margin-bottom, because I usually want to be consistent with how much room there is underneath something. I also often have to override a margin-top anyway (header tags, i’m looking at you).
Other Notes
• There is a -webkit-margin-after-collapse property which can be set to "separate", but of course non-Webkit browsers get no love.
• You can include an :after pseudo-element with display:table, but I like reserving my befores and afters for interesting things.
• Floats do not collapse vertical margins, but you must control width and clearance (width: 100%, clear: both).
• Negative margins are funny. If both margins are negative (who does this?), then they act as if positive, preserving the larger absolute value. If one margin is negative, the negative margin is subtracted from the positive.
• First and last child elements tend to have their vertical margins overridden by their parent, or even extend their margin outside the parent. You can muck about with the properties of the child (like its padding), which sort of "wakes it up" and allows the margins to be preserved. See MDN web docs for painstaking but succinct details.