A lot of frustration in creating web page layouts results from a misunderstanding of the CSS box model. To be fair, this isn’t entirely the fault of web developers or designers; blame can be equally apportioned to the oddities of the CSS spec itself.
Very simply, the width and height of an HTML element is not what most people assume it is. By default, the width
of an element is the width of its content, not necessarily the width of the containing box: padding
, border
and outline
are added to the width
you set for any element. This leads to situations like the following:\
Let’s say we have the opening two paragraphs from Mary Shelly’s Frankenstein, each provided with a different id
:
<p id="first">I am by birth a Genevese; and my family is
one of the most distinguished of that republic…
<p id="second">As the circumstances of his marriage illustrate
his character…
We want to have both paragraphs to have a width
of 50%, floated side-by-side on the page:
p { width: 50%; }
p#first { float: left; }
p#second { float: right; }

All looks fair and well to this point, as you can see to the in Figure 1. Trouble occurs when we add a border
to the paragraphs. As you will see, adding just a few pixels is enough to throw the paragraphs out of alignment:
p {
width: 50%;
border: 1px solid #000;
}

This gives the result you see in Figure 2. Historically, this behavour is one of the reasons I have preferred to use flexbox and its related properties to create grid layouts. Without going that far, simply adding the box-sizing
property with a new value can fix this issue:
p {
width: 50%;
border: 1px solid #000;
box-sizing: border-box;
}
border-box
calculates each element’s width to the outside of its border. This means we can add padding
to the paragraphs and still retain their alignment:
p {
width: 50%;
border: 1px solid #000;
box-sizing: border-box;
padding: 3em;
}
Note, however, that adding
margin
to the paragraphs will throw them out again, as the box-sizing
model does not accommodate space outside the element.
box-sizing
can also be set to padding-box
, which takes into account padding
but not border
. The default box-sizing
value is content-box
.
As the traditional box-model can be so counter-intuitive and problematic, and this new setting so useful, it is suggested that setting box-sizing
to border-box
should be part of an opening CSS reset, with vendor prefixes added to support older versions of browsers:
* {
box-sizing: border-box;
}
The only problem is that this approach can be overly aggressive, making it difficult to change the box model for elements later in our code. A more adaptive approach has been suggested by Jonathan Neal:
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
This provides the opportunity to set an element to another box-sizing model later in your page, and for its children to inherit and respect the change. For example, to set a <div>
back to the standard box-model, due to its interaction with a third-party plugin that has the same behaviour, you could now use:
div#special {
box-sizing: content-box;
}
Photograph by Michelle, used under a Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Generic license
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.