CSS's box model is ...interesting
Frontend
CSS's box model is simple on the surface but has some nuances worth understanding. It's better to understand how it works to control the design rather than fighting it. Every HTML element is seen by CSS as a rectangle. Each rectangle has to find a space to co-exist with other rectangles (its parent, siblings or children). One aspect I'm trying to get hang of is how margins work. Here I will explain what I understand so far. A good starting point is to look at the default behavior and explain where the extra spaces around the elements are coming from. I created a page with three content divs - a header, a section with one paragraph and another section with two paragraphs:
We see that there is some space to the left and right of the content and some space at the top. There is also space between <h>
and <p>
tags. It is not easy to visualize the white space so I added a border to the body and background color for the three divs and headings:
This default representation is a combination of two things:
- Browser default style - every browser apply a default style for it to be able to display the content that is readable - at the very least it needs some font style to display the content.
- CSS rules - how much space should be between each block-level element.
Let's deconstruct some of the browser and CSS rules:
Where is the extra margin around the document coming from?
- body gets a default margin - this is the space we see outside of the red rectangle (body's border). While the top, left and right margin appear to be same, the bottom margin is automatically adjusted to accomodate the content. Let's remove the margin from body:
body {
margin: 0;
}
Where is the vertical space between the h1 and body border coming from?
Well, this one is tricky. There are two rules at play:
- Every text element (paragraphs, headings) gets default margin equal to their font size.
- If the margin (top/bottom) of a parent and child touch, they collapse - child's margin is stolen by parent's margin (selfish parent!).
This is why, although I have a different background set for the header's div, it's not showing up because h1 has no margin anymore while the header div has gotten that margin. Let's separate the two margins by adding a border to header div:
.header {
border: 1px solid yellow;
}
This means I can remove that space by setting the h1 margin to 0, so no one steals the margin and instead add a padding to the header. Here I also used text-align
to center
to put text in the middle:
.header {
padding: 25px;
}
h1 {
margin: 0;
text-align: center;
}
What about the space between blue header and first section?
Similar argument as above applies here. h2 has a default margin but it touches its parent section div. That section gets the h2's margin. Let's remove h2's margin:
h2 {
margin: 0;
}
Why is the space above second h2 still there?
That is a result of section one's paragraph's bottom margin touching the bottom margin of section one's div. Same argument applies to the space between bottom of section two's second paragraph and the bottom body border. Let's remove the paragraph's bottom margin and see:
p {
margin-bottom: 0;
}
Now we are left with only the margin at the top of each paragraph, which we can remove by removing all paragraph margins:
p {
margin: 0;
}
It's worth noting at this point that when paragraph's top and bottom margin's were active, the margin between section two's first and second paragraph was not doubled (bottom margin of p1 + top margin of p2).
What happens if siblings margins touch?
They collapse too. The final margin between the siblings is the maximum of the two margins that are touching. This helps in creating uniform spaces between block elements:
section {
margin: 50px 0;
}
Note the margin between header and section one, section one and section two, section two and body- they are all 50px.
Phew!!
What does this all mean for creating good layouts?
- Set the body margin to zero (almost always)
- Set text elements top margin to zero (for better control)
- Set last child's bottom margin to zero (for better control)
- Control spaces between text elements using bottom margins
- Control spaces between div and its children using div's padding