---
title: 'Box Sizing'
source: 'https://academia.sh/en/courses/css-fundamentals/box-sizing'
course: 'Visual Presentation with CSS'
language: en
updated: '2026-08-17T18:09:15+00:00'
license: 'CC BY-SA 4.0'
---

# Box Sizing

Which region a declared width measures; comparing the content-box and border-box models with the same numbers, and the effect of minimum and maximum bounds.

The previous lesson computed a box's regions starting from the content size and working
outward. Writing styles is usually done the other way around: a `width` is declared, and the
remaining dimensions are derived from it. This raises a question — which region does the
declared width measure?

There are two answers to this question, and both are defined. Which one applies is chosen with
the `box-sizing` property. This lesson compares the two models with the same numbers.

## Two Models

`box-sizing: content-box` — the declared width measures the **content box**. Padding and
border are added outside this value; the space the box occupies is larger than the declared
value. This is the property's initial value.

`box-sizing: border-box` — the declared width measures the **border box**. Padding and border
are taken out of this value; the content box is whatever remains.

The difference between the two is not a matter of preference, it is different arithmetic:

$$\text{content-box:}\quad W_{\text{border}} = w + p_{\text{left}} + p_{\text{right}} + b_{\text{left}} + b_{\text{right}}$$
$$\text{border-box:}\quad W_{\text{content}} = w - p_{\text{left}} - p_{\text{right}} - b_{\text{left}} - b_{\text{right}}$$

```js
// box-sizing.mjs — gives the content-box vs border-box difference as a number
function resolve({ width, padding, border, boxSizing }) {
  const [pl, pr] = padding, [bl, br] = border;
  const extra = pl + pr + bl + br;
  if (boxSizing === "content-box") {
    return { content: width, borderBox: width + extra };
  }
  // border-box: the declared value is the border box; content is what remains
  return { content: Math.max(0, width - extra), borderBox: width };
}

const cases = [
  { name: "card",       width: 300, padding: [16, 16], border: [1, 1] },
  { name: "narrow box", width: 40,  padding: [16, 16], border: [1, 1] },
];

for (const c of cases) {
  for (const model of ["content-box", "border-box"]) {
    const r = resolve({ ...c, boxSizing: model });
    console.log(`${c.name.padEnd(11)} ${model.padEnd(11)} width=${c.width}  -> content=${r.content}  borderBox=${r.borderBox}`);
  }
}

console.log("--- three columns, each 33.333%, padding 16, border 1 ---");
const container = 960;
const percent = 100 / 3;
for (const model of ["content-box", "border-box"]) {
  const w = container * percent / 100;
  const r = resolve({ width: w, padding: [16, 16], border: [1, 1], boxSizing: model });
  const total = r.borderBox * 3;
  console.log(`${model.padEnd(11)} column border box=${r.borderBox.toFixed(2)}  sum of three=${total.toFixed(2)}  overflow=${(total - container).toFixed(2)}`);
}
```

```
card        content-box width=300  -> content=300  borderBox=334
card        border-box  width=300  -> content=266  borderBox=300
narrow box  content-box width=40  -> content=40  borderBox=74
narrow box  border-box  width=40  -> content=6  borderBox=40
--- three columns, each 33.333%, padding 16, border 1 ---
content-box column border box=354.00  sum of three=1062.00  overflow=102.00
border-box  column border box=320.00  sum of three=960.00  overflow=0.00
```

## What the Numbers Say

In the first two lines, the same declaration produced two different boxes: a card declaring
`width: 300px` occupied 334 units in the `content-box` model, and exactly 300 units in the
`border-box` model. The 34 units in between are the sum of padding and border ($16 + 16 + 1 +
1$).

The narrow box line shows an edge case. In the `border-box` model, for a box given
`width: 40px`, only 6 units are left for the content once the 34-unit addition is subtracted.
If the sum of the addition had exceeded the declared width, the content size would be clamped
to zero; it never goes negative. This means the box does not become invisible — the border box
is still 40 units, but nothing fits inside it.

The third block shows the actual practical problem. To place three columns in a 960-unit
container, when each is given `width: 33.333%`, the `content-box` model comes to a total of
1062 units — a 102-unit overflow. The cause is that the percentage measures the **content
box**, and padding and border are added on top of that. The same declaration fits exactly in
the `border-box` model.

This explains why `border-box` is a common baseline choice: it makes the space a box occupies
independent of that box's own padding. When padding is raised from 16 to 24, the layout does
not break; only the space left for the content shrinks.

The model is set uniformly across the whole document:

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

The universal selector does not cover pseudo-elements, so all three are written together.
Since its specificity is $0,0,0$, any later rule can override it.

## Height Behaves Differently

`width` and `height` look symmetric, but their behaviors diverge.

A block box's width fills the containing box when it is not declared. Its height, when not
declared, is determined **by its content**. This asymmetry is a consequence of normal flow and
will be taken up in later lessons.

One result of this is that a `height` percentage often does not work. A `height: 50%`
declaration cannot be resolved unless the containing box's height is known; if the containing
box's height also depends on its own content, a circular definition forms. In this case, the
percentage is treated like `auto`.

Giving a fixed height is also fragile: when text is longer than expected, it overflows the
box. Height is not declared unless it is considered together with overflow management.

## Minimum and Maximum Bounds

The `min-width`, `max-width`, `min-height`, and `max-height` properties bound the computed
size. The order of application is defined, and it matters:

1. `width` (or `auto`) is resolved first.
2. If the result exceeds `max-width`, it is brought down to `max-width`.
3. If this result is below `min-width`, it is raised to `min-width`.

The order means `min-width` beats `max-width`. If `min-width: 400px` and `max-width: 300px`
are written together, the result is 400.

The most common use of this trio is building a readable column of text:

```css
main {
  max-width: 70ch;
  margin-inline: auto;
}
```

`max-width` sets an upper bound; the box does not overflow just because the container is
narrow, and when the container is wide, the text does not spread into lines too long to read.
`margin-inline: auto` distributes the remaining space evenly to both sides. Together, these
two give a column that is both bounded and adaptive, without declaring a fixed width.

The `ch` unit will be defined in the next topic; for now it can be read as roughly a
character's width.

## Sizing the Station Page

```css
/* station.css — step 9: sizing model and bounds */
*, *::before, *::after { box-sizing: border-box; }

main {
  max-width: 70ch;
  margin-inline: auto;
  padding-inline: 16px;
}

.measurement-table { width: 100%; }

.measurement-table .name { width: 40%; }

.location-image img {
  max-width: 100%;
  height: auto;
}
```

The last rule is a frequently needed pair. Giving an image `max-width: 100%` keeps an image
wider than its container from overflowing. `height: auto` preserves the aspect ratio: if only
the width is bounded and the height stays declared in the document, the image gets squashed.

Giving the `.name` column a percentage width acts as a suggestion in table layout; the table
algorithm weighs column widths together with content. A table's sizing behavior is subject to
its own rules and does not follow every detail of the model here.

## Summary

- Which region a declared `width` measures is chosen with `box-sizing`: `content-box`
  measures the content box, `border-box` measures the border box.
- In the `content-box` model, padding and border are added outside the declared value; with
  percentage widths, this is the direct cause of overflow.
- In the `border-box` model, the content size is whatever remains and never goes negative; if
  the sum of the addition exceeds the declared width, it is clamped to zero.
- Width fills the container when not declared, height is determined by content when not
  declared; this asymmetry is why percentage heights often fail to resolve.
- Bounds apply in order: `max` first, then `min`. This order makes `min` stronger than `max`.

## Next Step

These two lessons computed a box's dimensions, but what **kind** of box a box is was never
asked. Why does a paragraph occupy its own line while an emphasis element stays inside the
text? Why, when both are given a `width`, is only one affected? The next lesson defines
display types and shows where this difference comes from.
