Topic 5.2
box-sizing
In one line
box-sizing decides what width and height measure: only the content (content-box, the default) or the whole box up to the border (border-box), which is why almost every site sets border-box everywhere.
Think of it like this
Buying a suitcase that must fit in an airline's 55 cm cabin limit. One shop measures only the inside of the case (content-box): add thick padded walls and the case ends up 60 cm and gets rejected. Another shop measures the outside (border-box): whatever the walls, the case is 55 cm, and the inside space is what's left. The airline only cares about the outside.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- box-sizing
- The CSS property that says whether width and height include the padding and border.
- content-box
- The default: width and height size the content only. Padding and border are added on top.
- border-box
- Width and height size the whole box up to the border. Padding and border fit inside that size.
- Universal selector
*: a selector that matches every element.- Pseudo-element
- An extra box CSS creates for part of an element, like
::before(before its content). - Inherit
- Take the value the parent element has. Some properties do it automatically;
inheritforces it. - Reset
- A few rules at the top of a stylesheet that smooth out browser differences before your own styles.
Step by step
01The problem: a full-width input that overflows
You want an input as wide as its card, so you write width: 100%. Then you add padding so the text doesn't touch the edge. Suddenly the input sticks out of the card on the right.
.card { width: 300px; }
input {
width: 100%; /* 300px of CONTENT */
padding: 10px; /* +20px */
border: 2px solid; /* +4px = 324px */
}02Why: content-box adds padding and border on top
100% became 300px of content. Padding (10 + 10) and border (2 + 2) were added outside, so the input is 324px in a 300px card. It overflows by 24px.
03The fix: border-box
Switch the input to border-box and the 300px now includes the padding and border. It fits exactly.
input {
box-sizing: border-box;
width: 100%;
padding: 10px;
border: 2px solid;
}04Do it for every element, once
Instead of remembering per element, put the universal reset at the top of every stylesheet. From then on, every width you write is the width you see.
*,
*::before,
*::after {
box-sizing: border-box;
}05Know the edge case: padding bigger than the width
With border-box, the content area can't go below zero. If padding + border is more than the width (say width: 20px; padding: 20px), the box grows to 40px anyway: the width is treated as a minimum for the padding and border.
Try it yourself
- 1
Predict the content width
In the first example, change the padding to
30px. Predict both lines of output before running (hint: inborder-boxthe content shrinks, incontent-boxthe outside grows). - 2
Apply the reset
In the second example, delete the
.oldand.newrules and add the universal reset at the top of the CSS. Both inputs should now log300. - 3
Squeeze a border-box
In the first example, set
.border { box-sizing: border-box; width: 30px; }. The padding and border need 48px. What doesoffsetWidthprint? (It prints 48: the box can't be smaller than its padding plus border.)
Code & diagrams
Same width, padding and border. Only `box-sizing` differs, and so does every number.
Preview
Console
Nothing logged yet.Expected console output
content-box: outer 248px, content 200px
border-box: outer 200px, content 152pxThe first input pokes 24px past the card's content edge. Delete `.old`'s rule and add the universal reset at the top to fix both.
Preview
Console
Nothing logged yet.Expected console output
content-box input: 324
border-box input: 300/* 1. Everything is border-box */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* 2. Inherit from the root, so one wrapper can opt back out */
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
.legacy-widget {
box-sizing: content-box; /* everything inside follows */
}Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Two 50% columns that don't fit
Make two inline-block columns with width: 50%; padding: 16px; side by side, without border-box.
Break #2
Reset that forgets the pseudo-elements
Write the reset as * { box-sizing: border-box; } and give a ::before icon width: 24px; padding: 4px.
Myth vs fact
Myth
border-box makes the margin part of the width too.
Fact
No box-sizing value includes the margin. border-box stops at the outer edge of the border; margin is always added outside.
Myth
box-sizing is inherited, so setting it on html is enough.
Fact
It is not inherited by default. Setting it on html only affects the html element, unless the other elements use box-sizing: inherit.
Myth
Using border-box is a hack.
Fact
It's a standard property that every browser supports, and the CSS Working Group has said content-box being the default was a mistake. Bootstrap, Tailwind and most resets set border-box.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Tailwind's Preflight and Bootstrap's Reboot both start with
*, ::before, ::after { box-sizing: border-box; }. If you drop a component from such a site into a plain page, its sizes will change unless you add the reset too. - ▸
box-sizingalso affectsmin-width,max-width,min-height,max-heightand theflex-basisof flex items: all of them measure the border box whenborder-boxis set.aspect-ratio(Topic 5.5) uses the box chosen bybox-sizingtoo. - ▸
Under
border-boxthe content size is clamped at zero:width: 10px; padding: 20pxgives a 40px box. Browsers handle this per the CSS Box Sizing spec, so it's consistent everywhere. - ▸
The legacy Internet Explorer 5 "quirks" box model was effectively
border-box. The standard chosecontent-boxfor CSS1, andbox-sizingwas added later (CSS3 UI) to let authors choose. Pages in quirks mode still size some elements the old way, one more reason to keep the doctype (HTML course, Topic 0.4).
Remember this
- 1
box-sizingis a CSS property with two values.content-box(the default from the spec) meanswidthandheightset the content area; padding and border are added outside it.border-boxmeanswidthandheightset the box up to the outer edge of the border; padding and border are taken out of that size, and the content gets what remains. - 2
With
width: 200px; padding: 20px; border: 4px solid: incontent-boxthe box is 200 + 40 + 8 = 248px wide. Inborder-boxit is 200px wide, and the content area shrinks to 200 − 40 − 8 = 152px. Margin is never included in either:box-sizingonly moves the line between "inside" and "outside" to the border. - 3
border-boxmatches how people think about layout. If you say a sidebar iswidth: 25%and then add padding, you still want it to be a quarter of the page, not a quarter plus 40 pixels. It also makeswidth: 100%safe: a full-width input with padding stays inside its parent instead of poking out of the right side. - 4
That's why nearly every modern stylesheet starts with the universal reset:
*, *::before, *::after { box-sizing: border-box; }. The*is the universal selector (it matches every element), and::before/::afterare pseudo-elements (extra boxes CSS can add before or after an element's content, see the selectors phase); they aren't matched by*, so they're listed separately. - 5
Another popular version sets it once on the root and lets every element inherit:
html { box-sizing: border-box; } *, *::before, *::after { box-sizing: inherit; }.box-sizingdoesn't inherit on its own, butinheritforces it. The benefit: a third-party widget that relies oncontent-boxcan switch back by setting it on its own wrapper, and everything inside follows. - 6
Some elements already use
border-boxin the browser's default stylesheet. In Chrome, for example,<button>,<select>and some<input>types (like checkboxes, radio buttons andtype="button") default toborder-box, while text inputs and<textarea>default tocontent-box. This inconsistency is one more reason to set it yourself for everything.
Explain it without notes
What is the difference between content-box and border-box?
Why do most stylesheets set box-sizing: border-box on everything, and what does the usual reset look like?
Does box-sizing affect margins? Explain with an example.
Why might you prefer the inherit version of the reset?
Practice
Make a .panel that is exactly 280px wide on screen with 24px padding and a 3px border, using border-box. Log its offsetWidth and its content width (clientWidth minus the padding).
Write a reset so that every element inherits box-sizing from the root, then make one .legacy wrapper use content-box for itself and all its children. Log the box-sizing of a <p> inside .legacy.
Trade-offs
- ↔
border-boxmakes outer sizes predictable but the content size becomes "whatever is left"; when the content size must be exact (an image slot of exactly 300px), you either size the content or usecontent-boxfor that element. - ↔
The plain
*reset is simplest; theinheritversion costs one more rule but makes opting out per component easy.
Done when you can
I can explain what
content-boxandborder-boxmeasure.I can calculate a box's outer and content width in both modes.
I can write the universal
border-boxreset from memory, including pseudo-elements.I know margins are never included in
box-sizing.I can explain why a
width: 100%input with padding overflows and fix it.