CSS The box-sizing Property

Every element on a web page is a rectangular box, and that box has a width, a height, some padding, and a border. The question box-sizing answers is deceptively simple but incredibly important: when you set width: 200px on an element, does that 200px include the padding and border, or not? Getting this wrong is one of the most common sources of layout bugs in CSS, and understanding it deeply will save you hours of confused debugging.

The box-sizing property changes how the browser calculates the total rendered size of an element by changing which parts of the box model count toward the width and height you specify.

Overview / How it works

To understand box-sizing, you first need to understand the CSS box model. Every element consists of four nested layers, from the inside out: the content box (where text and child elements live), the padding (space inside the border, around the content), the border (a visible or invisible edge), and the margin (space outside the border, separating the element from its neighbors). Margin never affects the element’s own rendered size — it only affects the space around it — so box-sizing never touches margin.

The default value, content-box, tells the browser that width and height describe the size of the content box only. Padding and border are then added on top of that size, so the element’s total visible footprint grows larger than the width you declared. This is how CSS has worked since the earliest days of the web, and it’s mandated by the original W3C box model specification.

The alternative value, border-box, tells the browser that width and height describe the size of the box including padding and border. The content area shrinks to make room for them, but the element’s total rendered footprint stays exactly at the width and height you declared. This matches how most designers and developers intuitively think about sizing — “I want this box to be exactly 200px wide, period” — which is why border-box has become the de facto standard reset applied at the top of nearly every modern stylesheet.

Under the hood, box-sizing doesn’t change the box model itself — content, padding, border, and margin are still four distinct layers, and the browser still paints them in that order. What changes is purely the arithmetic the layout algorithm performs when it resolves your specified width/height into the content box’s actual size during the layout (reflow) phase.

Syntax

selector {
  box-sizing: content-box | border-box | inherit | initial | unset;
}
Value Meaning
content-box Default. width/height apply to the content only; padding and border are added on top, increasing the total rendered size.
border-box width/height apply to content + padding + border combined; the content area shrinks to fit. Total rendered size equals the declared width/height.
inherit Takes the computed value of the parent element instead of the property’s own default.
initial Resets to the property’s default, which is content-box — not border-box, a common misconception.
unset Acts as inherit if the property is naturally inherited, or initial otherwise. box-sizing is not inherited by default, so unset behaves like initial here unless you’ve explicitly set inheritance.

Note that box-sizing is not inherited automatically, which is precisely why the universal-selector reset pattern shown later exists: to propagate border-box to every element in the document deliberately.

Examples

Example 1: The default content-box behavior

.box {
  width: 200px;
  padding: 20px;
  border: 5px solid #333;
  background-color: lightblue;
}

Result: The blue content area is 200px wide as declared, but the padding (20px on each side) and border (5px on each side) are added outside it. The element’s total rendered width becomes 200 + 20 + 20 + 5 + 5 = 250px. If this box sits inside a 200px-wide container, it will visibly overflow that container by 50px.

This is the trap that catches beginners constantly: you set a width that matches your grid column, add some padding for breathing room, and suddenly the element is wider than the column and breaks the layout.

Example 2: Switching to border-box

.box {
  box-sizing: border-box;
  width: 200px;
  padding: 20px;
  border: 5px solid #333;
  background-color: lightblue;
}

Result: The element’s total rendered width is exactly 200px, matching the declared value. The browser subtracts the border (10px total) and padding (40px total) from 200px, leaving a content area of only 150px wide. Visually the box looks identical in overall footprint to a plain 200px box with no padding or border — the padding and border are simply carved out of the space, not added to it.

Example 3: A realistic card component with the universal reset

*,
*::before,
*::after {
  box-sizing: border-box;
}

.card {
  width: 320px;
  padding: 24px;
  border: 1px solid #d0d0d0;
  border-radius: 8px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}

.card__title {
  margin: 0 0 12px;
  font-size: 1.25rem;
}

.card__button {
  width: 100%;
  padding: 10px 16px;
  border: 2px solid #2b6cb0;
  background-color: #2b6cb0;
  color: #fff;
  border-radius: 4px;
}

Result: Every element on the page, including pseudo-elements, is switched to border-box sizing. The .card renders as a card that is reliably 320px wide no matter how much padding or border you tune later. Inside it, .card__button is set to width: 100%, and because it’s also border-box, that button fills the full 320px minus the card’s own padding, while its own padding and 2px border stay contained within that width instead of pushing it wider than its parent.

How it works step by step

  • Step 1: It reads the computed width (or lets it be determined by the containing block if width is auto).
  • Step 2: It reads the computed padding and border-width values on all four sides.
  • Step 3: If box-sizing is content-box, the content box is set to exactly the specified width, and padding + border are added outward, expanding the total box.
  • Step 4: If box-sizing is border-box, the engine instead computes: content width = specified width − padding-left − padding-right − border-left-width − border-right-width. The same subtraction happens for height using the top/bottom equivalents.
  • Step 5: If that subtraction would produce a negative content size (e.g. padding larger than the declared width), the engine clamps the content box to zero rather than producing a negative size.
  • Step 6: Margin is applied entirely outside this calculation in both modes — it never shrinks or grows the element’s own box, it only affects the gap to neighboring boxes.

Common Mistakes

Mistake 1: Forgetting to reset box-sizing and being surprised by overflow

.column {
  width: 33.333%;
  padding: 16px;
  border: 1px solid #ccc;
}

Three .column elements at 33.333% width plus 16px padding and a 1px border on each side will together exceed 100% of the container’s width, causing the third column to wrap onto a new line unexpectedly. The fix is to add box-sizing: border-box so the padding and border are absorbed inside the declared percentage width instead of added on top:

.column {
  box-sizing: border-box;
  width: 33.333%;
  padding: 16px;
  border: 1px solid #ccc;
}

Mistake 2: Assuming box-sizing: initial means border-box

.reset-me {
  box-sizing: initial;
}

This is syntactically valid CSS, but it’s a logic trap: initial resets a property to its specification default, and the specification default for box-sizing is content-box, not border-box. Developers who expect initial to mean “the modern sensible default” are often surprised when padding starts inflating their element’s size again. If you want border-box, you must write it explicitly:

.reset-me {
  box-sizing: border-box;
}

Best Practices

  • Apply the universal *, *::before, *::after { box-sizing: border-box; } reset near the top of every project’s stylesheet — it is the single most-adopted CSS convention for a reason.
  • Once you’ve applied the reset, always design components by thinking of width/height as the element’s final, total footprint — padding and border become internal details that no longer affect layout math.
  • Remember that box-sizing is never inherited automatically; you must set it on every element (or rely on the universal selector) rather than assuming a parent’s value propagates.
  • Don’t forget ::before and ::after pseudo-elements in your reset — they participate in layout and need the same box-sizing behavior as real elements, especially when used for decorative borders or icons.
  • Margin is always excluded from both content-box and border-box calculations — use the box model’s margin layer for spacing between elements, not box-sizing, to control it.
  • When debugging unexpected overflow or wrapping, check box-sizing first before assuming a flex/grid bug — a missing border-box is one of the most frequent root causes.

Practice Exercises

Exercise 1: Create a .box rule with width: 150px, padding: 30px, and border: 10px solid black, using the default content-box. Calculate by hand what the total rendered width will be, then rewrite the rule using border-box so the total rendered width is exactly 150px.

Exercise 2: Write a universal reset selector that applies border-box sizing to all elements and their ::before/::after pseudo-elements in one rule.

Exercise 3: Given three sibling <div> elements each styled with width: 25%, padding: 12px, and box-sizing: content-box, explain in your own words why they might wrap onto a new row inside a flex container, and write the single-property fix that prevents it.

Summary

  • box-sizing controls whether an element’s declared width/height includes padding and border, or excludes them.
  • content-box (the default) adds padding and border on top of the declared size, inflating the total rendered footprint.
  • border-box includes padding and border inside the declared size, keeping the total rendered footprint exactly equal to width/height.
  • Margin is never affected by box-sizing in either mode.
  • box-sizing is not inherited by default, which is why the *, *::before, *::after universal reset is standard practice.
  • box-sizing: initial resets to content-box, not border-box — a frequent point of confusion.
  • Most real-world overflow and unexpected-wrapping bugs trace back to a missing border-box declaration.