CSS's box model is ...interesting

Thursday, Jul 16, 2020
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:

zero

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:

one

This default representation is a combination of two things:

  1. 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.
  2. 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?

  1. 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;
}

two

Where is the vertical space between the h1 and body border coming from?

Well, this one is tricky. There are two rules at play:

  1. Every text element (paragraphs, headings) gets default margin equal to their font size.
  2. 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;
}

three

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;
}

four

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;
}

five

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;
}

six

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;
}

seven

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;
}

eight 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?