NOTE: This post is only an introduction to debugging CSS with the box model. It's not a definitive guide but it can help you get started if you're learning CSS.
Everything displayed by CSS is a box.
Have you seen those art drawings made using only HTML and CSS?
(Go give Pratham a follow on Twitter if you're into web dev.)
Yes, every element in that drawing is a box. They may look like different shapes, but as far as the web page is concerned, they are all boxes. Understanding this principle is important to learning the CSS box model.
In this blog post, I am going to do three things. First, I am going to explain why you should learn the box model. Second, I'm going to describe the properties related to the box model. And third, I am going to cover two "weird" behaviors you may encounter in CSS and we'll explore how to understand and resolve them with the CSS box model.
Why learn the CSS Box Model?
Programming languages have rules built into them. When you break a rule, the text editor or IDE will (usually) tell you when something went wrong. It will even tell you where the bug originated from and possible fixes.
CSS is not like that. It has rules, but it won't try to fight you when you use it. At best, you may see squiggly lines in your text editor if you leave out a colon, a semi-colon, a curly bracket, or when you try to use a property that doesn't exist.
However, if you write CSS that breaks your site layout, CSS will not help you identify the buggy code. Unlike programming languages, it will not tell you why your page layout broke. Similarly, if an element collapses on itself or if content overflows, CSS will not tell you why that happened or how to fix it.
Why is this the case? Because CSS assumes you know what you're doing.
There are several tools that web developers can use to debug CSS. One of those tools is the CSS box model.
What is the CSS Box Model?
Everything displayed by CSS is a box. The CSS box model describes how the boxes work and their four properties: content, padding, border, and margin.
The image below shows how these four properties work together to make up a box that says "Hello World."
Margin Box
The orange area represents the margin box. The margin is the space surrounding the box. Increasing the size of the margin will push the box in certain directions.
For example, adding margin to the left of the box will push the box to the right.
p {
margin-top: 15px;
}
Border Box
The black outline between the green and orange areas is the border box. It can defined like this:
p {
border: 5px solid black;
}
Padding Box
The padding box is the area shown in green. It's the space between the content box and the border. When you set a background to an element, the padding box's background changes along with the content box. Padding is useful because it lets the content box breathe with whitespace.
p {
padding: 0;
}
Content Box
Finally, there's the content box shown in blue. This is the inner most box where your content lives. When you set the width and height of your element, you are actually setting the width and height of your content box.
p {
width: 5px;
height: 2px;
}
Default values
Some browsers give certain elements default styles. For example, Google Chrome gives <p>
elements a default margin top and bottom of 16px. It's usually a good idea to use libraries that reset the default browser styles. This will ensure that your webpage looks the same across browsers.
Debugging CSS with the box model
People sometimes get frustrated with CSS because it doesn't do what they want it to do. But CSS has rules. Understanding these rules will help you enjoy CSS a bit more.
Let's examine two weird behaviors that people usually encounter in CSS. We'll go over how to use the box model to investigate these weird behaviors, and we'll learn the CSS rules that cause these behaviors.
The alternative box model
Let's start with a simple HTML document:
<div class="container>
<div class="box-1">Box 1</div>
<div class="box-2">Box 2</div>
</div>
To distinguish the elements, we'll give them different background colors and change the text color to white so make it readable. We'll also give them widths and heights.
.container {
background-color: pink;
width: 500px;
height: 500px;
margin: 25px;
color: white;
}
.box-1, .box-2 {
width: 50%;
height: 50%;
}
.box-1 {
background-color: blue;
}
.box-2 {
background-color: red;
}
The web page should look like this:
You know what? It drives me nuts when text is right up against the edge of its element, so let's add padding to the two boxes. Let's add 15px of padding all around:
.box-1, .box-2 {
width: 50%;
height: 50%;
padding: 15px;
}
That should be better:
What happened?? The boxes are overflowing out of their container. That shouldn't happen. We set both boxes to have a height of 50% of their parent container. That means that, together, the height of the two child elements should add up to 100% of the parent's height, not more or less.
This is where we can look under the hood to see what's going on. Right-click somewhere on the web page and select "Inspect" from the options. The dev tools will appear on the side of the page.
Here, I am going to use the Google Chrome dev tools since Chrome is the browser I use. From the Chrome dev tools, locate the menu bar with the following tabs: "Styles," "Computed," Layout," and so on. Click the "Computed" tab to see the box model.
The values in the box model update when you select an element from the Elements window. For example, here I clicked the container <div>
and the box model updated to show that it's content box is 500x500. It also shows that it has a margin of 25px on each side, exactly what we defined earlier. Looks good.
Additionally, when you hover over an element in the elements window, the element is highlighted in the webpage and you can see some other information about it. Here, the window tells us the element is 500 x 500, exactly as it appears in the box model.
This means that if we click on Box 1 or Box 2, the box model should say the elements are each 250x250 with a padding of 15px all around. And that's exactly what we see when we click Box 1.
But if we hover over Box 1 in the Elements window and look at the webpage, we'll see that it says the element is 280 x 280. Hover over Box 2 shows the same thing.
How is that possible?
Well, when you set the width and height of an element, you're applying those properties to the content box only. In order to get the full height of the box, we need to add up the borders, padding, and content heights.
In our case, we have 0px for the top and bottom margins, 15px for both top and bottom paddings, and 250px for the element height:
0 + 15px + 250px + 150px + 0 = 280px total height
The same logic applies to the height. Notice that the margin does not count in our calculation of the total width and height of our box. This is because margin lies outside of our box.
So this is why Box 2 is overflowing. Box boxes have a combined height of 560px which is taller than the parent container which has a height of 500px.
What if there was way to tell CSS to apply a specific width and height to the entire box (border, padding, and content) instead of just the content box?
Well, there is!
There is a CSS property called box-sizing
. The default value of box-sizing
is content-box
, which tells CSS to apply the width and height to the content box only.
We can set box-sizing
to border-box
to tell CSS to apply the width and height to the content box, padding box, and border box combined. When we change the property to border-box
, we are using the alternate box model. In my opinion, it's much easier to work with the alternate box model.
Applying this small change to our CSS fixes the two boxes and makes them take up 50% of the parent container as we expected earlier.
.box-1, .box-2 {
width: 50%;
height: 50%;
padding: 15px
box-sizing: border-box;
}
The box model in the dev tool now says the content box of Box 1 is 220 x 220. CSS automatically adjusted the length and width of the content box so that everything inside could add up to 50% of the width of the parent container:
border top + padding top + content height + padding bottom + border bottom = total box height.
So, 0 + 15px + 220px + 15px + 0 = 250px total height (which is 50% of the parent container's height)
Collapsing margin
The second strange behavior is one that I've encountered a few times.
Let's start with a new, simple HTML document:
<div class="container">
<h1>Hello dev.to</h1>
<p>Lorem ipsum...</p>
</div>
We add some styles to the container:
.container {
background-color: #333333;
max-width: 500px;
margin: 35px auto;
color: white;
}
And the result should look like this:
I don't like how the title is right up against the edge of its element, so let's add some margin to push it down a bit.
h1 {
margin-top: 25px;
}
Hmm. Nothing seemed to change.
Time to check the trusty box model in the dev tools.
The box model shows that the top margin of 25px we add is there. So what's going on? Well, when I hover over the <h1>
element in the Elements window, I can see that the top margin is actually spilling out of the container box.
If I was working on a project, my next step would be to go to Google and search something like "CSS h1 element margin is outside the container box". That search lead me to this stackoverflow question, which let me to this sitepoint article that talks about collapsing margins.
In short, the article explains that when two elements are touching margins, only the larger margin is applied. The smaller margin collapses into the larger margin. "Touching margins" means there is no border, padding, or content between the two margins.
For example, imagine there are two sibling <div>
elements in a page. You add margin-bottom of 25px to the top <div>
, and margin-top of 50px to the bottom <div>
. If the two margins touch, they will collapse into each other. So instead of the elements being 75px (25px + 50px) apart, they are only 50px apart.
This behavior also happens with margins of parent and child elements, like in our example. Here, the margin of the <h1>
header is collapsing with the margin of the container <div>
because they are touching. So how do we stop them from touching?
We can add a border or padding to the parent container:
.container {
background-color: #333333;
max-width: 500px;
margin: 35px auto;
color: white;
border-top: 1px solid #333333;
}
Since the container <div>
now has a border, the margins will no longer touch and collapse. The margin-top of the <h1>
now appears inside the container:
Now what?
In this blog post, we talked about the box model. We talked about what it is and how to use it to investigate weird CSS behaviors.
In fact, these 'weird' behaviors are part of how CSS works. As you search for solutions to these behaviors, you'll learn that they were intentionally setup that way. CSS will make more sense to you as you encounter these behaviors and learn why they do what they do.
When you encounter a 'weird' behavior in your element layouts, try using the box model tool first to investigate the problem. It'll help you arrive at solutions faster.
Keep me honest
See something that is incorrect? Let me know in the comments and I'll fix it. Thanks!
If you made it all the way down here, you might as well hit that Follow button and check me out on Twitter.
Top comments (0)