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; }
Figure 1: Paragraphs floated side-by-side

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; 
}
FFigure 2: floated paragraphs thrown out of alignment by added padding

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

Paragraphs border-boxNote, 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.