Skip to main content

CSS box-sizing property

box-sizing defines whether an element's width and height include only content, or content plus padding and border.

Theory

TL;DR

  • content-box (browser default): width: 200px + padding: 20px = 240px actual. Padding adds outside.
  • border-box: width: 200px is the final size. Padding carves space from inside.
  • Analogy: content-box is ordering a 200px pizza where toppings spill beyond the edge; border-box is a box where everything fits inside exactly 200px.
  • Decision rule: use border-box everywhere via * { box-sizing: border-box; }. That is what Bootstrap, Tailwind, and React resets all do.

Quick example

css
.content-box { box-sizing: content-box; /* browser default */ width: 200px; padding: 20px; border: 5px solid; /* actual total width: 200 + 40 + 10 = 250px */ } .border-box { box-sizing: border-box; width: 200px; padding: 20px; border: 5px solid; /* actual total width: 200px, content area = 150px */ }

The first box overflows to 250px. The second stays at exactly 200px. Same property values, different sizing model.

Key difference

Browsers default to content-box per the CSS2.1 spec. So width: 200px; padding: 20px produces an element that actually takes up 240px. Most developers expect the opposite. That mismatch is why horizontal scroll on mobile has been the number one CSS bug on StackOverflow for years. border-box fixes it: the number you write becomes the number you get.

When to use

  • Fixed-width UI components (cards, modals, buttons) - border-box, so padding does not break the layout.
  • Flex or grid children - border-box, prevents items from overflowing their containers.
  • Text-heavy content blocks where content drives the size - content-box works here, though border-box does not hurt either.
  • Universal reset - border-box on * is the industry standard. Paul Irish's 2012 article showed it fixes most padding-related layout bugs instantly.

Comparison table

content-boxborder-box
What width meanscontent area onlytotal outer size
Browser defaultYes (CSS2.1)No
Content area sizeequals widthwidth - padding - border
When to usepure content sizing (rare)99% of layouts

How browsers calculate this

CSS engines like Blink (Chrome) and Gecko (Firefox) read box-sizing during style resolution. For border-box, the engine subtracts padding + border from the specified width before assigning the content width. For content-box, it skips that step. This happens in the layout phase, before paint, per CSS2.1 section 10.2.

Common mistakes

Forgetting the browser default. You set width: 300px; padding: 24px and the card overflows its container. The total is 348px, not 300px. Fix: add the universal reset to your global styles.

css
/* Standard global reset */ *, *::before, *::after { box-sizing: border-box; }

Mixing box-sizing inside flexbox. One child uses content-box, another border-box. The layout breaks because flex tracks outer sizes. Set box-sizing on the container and let children inherit.

css
.flex-container { display: flex; box-sizing: border-box; /* children inherit */ } .flex-child { flex: 1; padding: 20px; /* stays within flex track, no overflow */ }

Using border-box on img elements. Images rarely have padding or border by default. The value does not cause bugs, but it will silently shrink content space if you later add padding. Better to skip it for images or set content-box explicitly.

Forgetting pseudo-elements. ::before and ::after do not automatically inherit box-sizing in older resets. That is why the modern pattern explicitly includes *::before, *::after.

Real-world usage

  • React (create-react-app, Vite) - * { box-sizing: border-box; } in index.css by default.
  • Tailwind CSS - applies border-box on html and body via preflight reset.
  • Bootstrap 5 - universal border-box selector for grid consistency.
  • Next.js - global reset includes it to prevent hydration mismatches.
  • Material UI - cards and buttons use border-box to match design tokens.

Follow-up questions

Q: What is the browser default for box-sizing and why does it matter?
A: content-box, inherited from CSS2.1. Without a reset, every element with padding grows larger than its stated width. That breaks layouts in ways that are surprisingly hard to trace.

Q: How does box-sizing interact with min-width and max-width?
A: Same rules apply. With border-box, min-width: 200px means the total outer size is at least 200px. Padding and border are still subtracted from the content area inside.

Q: Does box-sizing affect ::before and ::after?
A: Yes. They inherit from the parent, but older reset stylesheets only targeted *. The modern pattern is *, *::before, *::after { box-sizing: border-box; } to cover pseudo-elements explicitly.

Q: In a component library, how do you handle box-sizing conflicts when users override it with their own CSS?
A: Document that the library assumes border-box and instruct users to include the universal reset. For fully isolated components, Shadow DOM encapsulation prevents external overrides entirely. Both approaches are used in production libraries.

Examples

content-box vs border-box side by side

html
<!DOCTYPE html> <html> <style> .content-box { box-sizing: content-box; width: 200px; padding: 20px; border: 5px solid navy; background: lightblue; margin-bottom: 12px; } .border-box { box-sizing: border-box; width: 200px; padding: 20px; border: 5px solid darkred; background: lightcoral; } </style> <div class="content-box">Total width: 250px (content 200 + padding 40 + border 10)</div> <div class="border-box">Total width: 200px (padding and border fit inside)</div> </html>

The blue box is 250px wide despite width: 200px. The red box is exactly 200px. Open DevTools, hover both elements, and check the box model panel to see the difference visually.

React card component

A typical pattern in component libraries. Without border-box, adding padding causes the card to exceed its container width and push other elements out of alignment.

jsx
function RepoCard({ name, stars }) { return ( <div style={{ boxSizing: 'border-box', /* 300px stays 300px even with padding */ width: '300px', padding: '16px', border: '1px solid #ddd', borderRadius: '6px' }}> <h3>{name}</h3> <span>{stars}</span> </div> ); } // Output: card always occupies exactly 300px, no parent overflow

Hover state padding changes do not shift the layout. I saw this cause a visible layout jump in production once, when someone removed the global reset and every card on the page widened by 32px overnight.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?