Command Palette

Search for a command to run...

Hectal
PHASE 5Beginner ~19 min· topic 2 of 8

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; inherit forces 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.

style.csswhole filecss
.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.

Why: content-box adds padding and border on topdiagram
Rendering diagram…

03The fix: border-box

Switch the input to border-box and the 300px now includes the padding and border. It fits exactly.

style.csswhole filecss
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.

style.csswhole filecss
*,
*::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. 1

    Predict the content width

    In the first example, change the padding to 30px. Predict both lines of output before running (hint: in border-box the content shrinks, in content-box the outside grows).

  2. 2

    Apply the reset

    In the second example, delete the .old and .new rules and add the universal reset at the top of the CSS. Both inputs should now log 300.

  3. 3

    Squeeze a border-box

    In the first example, set .border { box-sizing: border-box; width: 30px; }. The padding and border need 48px. What does offsetWidth print? (It prints 48: the box can't be smaller than its padding plus border.)

Code & diagrams

content-box vs border-box New tab

Same width, padding and border. Only `box-sizing` differs, and so does every number.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

content-box: outer 248px, content 200px
border-box: outer 200px, content 152px
The overflowing input, fixed by the universal reset New tab

The 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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

content-box input: 324
border-box input: 300
The two common resetscss
/* 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.

terminal
$ the page shows
── what you'll see ──
The second column drops onto the next line instead of sitting beside the first

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.

terminal
$ DevTools → Computed (::before)
── what you'll see ──
box-sizing: content-box, width 32px instead of 24px

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-sizing also affects min-width, max-width, min-height, max-height and the flex-basis of flex items: all of them measure the border box when border-box is set. aspect-ratio (Topic 5.5) uses the box chosen by box-sizing too.

  • ▸

    Under border-box the content size is clamped at zero: width: 10px; padding: 20px gives 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 chose content-box for CSS1, and box-sizing was 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. 1

    box-sizing is a CSS property with two values. content-box (the default from the spec) means width and height set the content area; padding and border are added outside it. border-box means width and height set 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. 2

    With width: 200px; padding: 20px; border: 4px solid: in content-box the box is 200 + 40 + 8 = 248px wide. In border-box it is 200px wide, and the content area shrinks to 200 − 40 − 8 = 152px. Margin is never included in either: box-sizing only moves the line between "inside" and "outside" to the border.

  3. 3

    border-box matches how people think about layout. If you say a sidebar is width: 25% and then add padding, you still want it to be a quarter of the page, not a quarter plus 40 pixels. It also makes width: 100% safe: a full-width input with padding stays inside its parent instead of poking out of the right side.

  4. 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/::after are 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. 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-sizing doesn't inherit on its own, but inherit forces it. The benefit: a third-party widget that relies on content-box can switch back by setting it on its own wrapper, and everything inside follows.

  6. 6

    Some elements already use border-box in the browser's default stylesheet. In Chrome, for example, <button>, <select> and some <input> types (like checkboxes, radio buttons and type="button") default to border-box, while text inputs and <textarea> default to content-box. This inconsistency is one more reason to set it yourself for everything.

Explain it without notes

01

What is the difference between content-box and border-box?

02

Why do most stylesheets set box-sizing: border-box on everything, and what does the usual reset look like?

03

Does box-sizing affect margins? Explain with an example.

04

Why might you prefer the inherit version of the reset?

Practice

01

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).

02

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-box makes 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 use content-box for that element.

  • ↔

    The plain * reset is simplest; the inherit version costs one more rule but makes opting out per component easy.

Done when you can

  • I can explain what content-box and border-box measure.

  • I can calculate a box's outer and content width in both modes.

  • I can write the universal border-box reset 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.