Module 3: Box Model & Spacing
Box Sizing: content-box vs border-box
Why the default width math is awkward, how border-box fixes it, and how width, auto and 100% behave differently with padding.
The previous lesson showed that a 300px box with padding and a border ends up wider than 300px. The box-sizing property decides whether width and height include that extra space. It is one line of CSS, and it removes a whole category of layout arithmetic.
The two models#
.a { box-sizing: content-box; } /* the default */
.b { box-sizing: border-box; }
content-box: width: 300px; padding: 20px; border: 5px
┌─ border ──────────────────────────────────────┐
│ padding │ content 300px │ padding │
└───────────────────────────────────────────────┘
total = 5 + 20 + 300 + 20 + 5 = 350px
border-box: width: 300px; padding: 20px; border: 5px
┌─ border ──────────────────────────────┐
│ padding │ content 250px │ padding │
└───────────────────────────────────────┘
total = 300px (the content shrinks to fit)
With border-box, the width you write is the width you get. Padding and border eat into the content area instead of expanding the box. Margin is never included in either model.
Why that matters#
Imagine two columns that each should take half the container:
.col {
width: 50%;
padding: 1rem;
border: 1px solid #ccc;
}
With content-box, each column is 50% plus padding plus border, so the sum is more than 100%, and the second column wraps down. You then start subtracting, width: calc(50% - 2rem - 2px), which is the kind of arithmetic nobody should do.
With border-box, 50% plus 50% is exactly 100%, regardless of padding or border.
The universal reset#
Almost every modern project begins with this:
*,
*::before,
*::after {
box-sizing: border-box;
}
It covers every element and the generated content from ::before and ::after, which the plain * selector does not match.
A more flexible variant lets a component opt out when it needs to:
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
Here everything inherits from html, so changing one element's box-sizing changes its whole subtree. This was designed for third-party widgets that expect content-box. For a new project, the first version is simpler and good enough.
What border-box does not do#
It does not include margin. A box with margin: 20px is still pushed 20px away from neighbors, beyond its stated width.
It also cannot make the content area negative. If your padding and border add up to more than the width, the content area collapses to zero and the box is as wide as its padding plus border.
.tiny { box-sizing: border-box; width: 10px; padding: 20px; }
/* actual width: 40px, because padding alone exceeds 10px */
width: auto vs width: 100%#
A block-level element with width: auto (the default) fills the available space, and its padding, border and margin fit inside that space. The browser works backwards: it takes the container width, subtracts the margin, border and padding, and gives the rest to the content.
width: 100% is different. It sets the width to exactly the container's width, and then padding and border are added on top in content-box.
.container { width: 400px; }
.auto { padding: 20px; } /* total 400px, fits nicely */
.full { width: 100%; padding: 20px; } /* content-box: 440px, overflows */
Even with border-box, width: 100% plus a horizontal margin overflows, because margin sits outside. The simplest rule: let blocks stay on width: auto unless you have a reason to set a width.
min-width, max-width and friends#
The sizing properties work together:
.article {
width: 100%;
max-width: 65ch; /* never wider than this */
min-height: 50vh; /* at least this tall, can grow */
margin-inline: auto;
}
max-width is more robust than width for responsive layouts, because the box shrinks on small screens instead of overflowing. Similarly, min-height is almost always better than a fixed height for content, because text grows when users enlarge fonts or translate the page, and fixed heights make it overflow.
Common mistakes#
- Applying
border-boxonly to some elements, then mixing both models in a grid and getting uneven columns. - Using
width: 100%plus margin or padding (incontent-box) and getting a horizontal scrollbar. - Fixed heights on text containers. Text overflows when the font is larger or the copy longer.
- Expecting
border-boxto include margin. - Forgetting
::beforeand::afterin the reset.
Practice#
- Make two
.colelements atwidth: 50%with padding and a border, side by side in a container usingfloatorinline-blockor flex withoutgap. Show that they wrap undercontent-box. Fix it with the reset. - Put a
divwithwidth: 100%; padding: 2reminside a 400px container. Compare its rendered width under both models. - Set
width: 10pxandpadding: 20pxunderborder-boxand measure the box in DevTools. Explain the number. - Convert a fixed-height card with long text into a
min-heightcard and verify the text no longer overflows when you zoom the browser.