CSS box model and box-sizing — content-box vs border-box
Box = content + padding + border + margin. Default `box-sizing: content-box` excludes padding/border from width — your 200px width becomes 220px on screen with 10px padding. `border-box` includes them: 200px is 200px. Set border-box globally.
The box model is what every CSS layout sits on. The interview question is short but exposes whether you've ever debugged a "but I set width: 200px why is it 240px?" bug.
Every element is a box composed of four areas:
- Content — the text/image area.
widthandheightapply here (with content-box). - Padding — space between content and border. Background extends through padding.
- Border — drawn on the border edge.
- Margin — space outside the border. Transparent.
+------------------- margin -----------------+
| +------------- border -----------------+ |
| | +--------- padding ------------+ | |
| | | content | | |
| | +-------------------------------+ | |
| +--------------------------------------+ |
+--------------------------------------------+box-sizing: content-box (CSS default).
width = content width. Total rendered width = width + padding-left + padding-right + border-left + border-right.
.card {
box-sizing: content-box;
width: 200px;
padding: 10px;
border: 2px solid;
}
/* Renders 224px wide. */box-sizing: border-box.
width includes content + padding + border. Margin is still outside.
.card {
box-sizing: border-box;
width: 200px;
padding: 10px;
border: 2px solid;
}
/* Renders 200px wide. Content shrinks to 176px. */The standard reset. Almost every modern CSS reset sets:
*, *::before, *::after {
box-sizing: border-box;
}Tailwind's preflight, normalize.css, and most design systems do this. Reasons:
- Math is intuitive: declared width = rendered width.
- Padding doesn't blow up your grid.
- Setting
width: 100%on a padded element actually fits its container.
Margin collapsing — the unrelated trap. Adjacent vertical margins on block-level elements collapse to the larger of the two. Two stacked <p>s with margin-bottom: 16px and margin-top: 24px produce 24px between them, not 40. Caveats:
- Horizontal margins don't collapse.
- Margins separated by border, padding, content, or a flex/grid container don't collapse.
- Floats and absolutely-positioned elements don't collapse.
This is why flex/grid layouts feel more predictable: margin collapsing doesn't apply.
width: auto vs explicit. Block elements default to width: auto — they fill the parent's content area. Setting an explicit width makes them ignore parent width.
Sizing keywords.
min-content— narrowest the content can be without overflow.max-content— widest the content prefers.fit-content— clamps between min and max-content given available space.auto— depends on context (block: fill parent; inline: shrink to content).
The outline non-box. outline draws around the border but doesn't take space — useful for focus rings that don't shift layout.
Why this still matters in 2026. Even with Flex and Grid handling most layouts, individual cards, buttons, and form inputs all rely on the box model for their internal sizing. Get box-sizing wrong and your design system has off-by-Npx bugs everywhere.
Code
Follow-up questions
- •Why is border-box the modern default?
- •When do margins collapse?
- •What does outline do that border doesn't?
- •How do min-content and max-content differ?
Common mistakes
- •Forgetting border-box reset and chasing 'why is my 200px element 224px wide?'
- •Stacking margin-top + margin-bottom expecting them to add (they collapse).
- •Using border for focus ring → causes layout shift on focus.
- •Setting width: 100% on a padded element with content-box → overflows parent.
Performance considerations
- •box-sizing has no perf cost; it's purely about computed sizing semantics.
- •Avoid changing border-width during layout transitions — recalc layout.
Edge cases
- •<input>, <select>, <button> historically default to content-box even with universal reset — set explicitly.
- •Tables (display: table) ignore box-sizing in some browsers — use border-collapse.
- •Margin collapses through empty elements — surprising in nested wrappers.
Real-world examples
- •Tailwind's preflight forces border-box globally; design system primitives all assume it.