Skip to content
academia.sh

Lesson 10 / 26

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.

Contents

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:

content-box:Wborder=w+pleft+pright+bleft+bright\text{content-box:}\quad W_{\text{border}} = w + p_{\text{left}} + p_{\text{right}} + b_{\text{left}} + b_{\text{right}} border-box:Wcontent=wpleftprightbleftbright\text{border-box:}\quad W_{\text{content}} = w - p_{\text{left}} - p_{\text{right}} - b_{\text{left}} - b_{\text{right}}

// 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+116 + 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:

*, *::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,00,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:

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

/* 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.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close