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:
// 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 ().
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 , 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:
width(orauto) is resolved first.- If the result exceeds
max-width, it is brought down tomax-width. - If this result is below
min-width, it is raised tomin-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
widthmeasures is chosen withbox-sizing:content-boxmeasures the content box,border-boxmeasures the border box. - In the
content-boxmodel, padding and border are added outside the declared value; with percentage widths, this is the direct cause of overflow. - In the
border-boxmodel, 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:
maxfirst, thenmin. This order makesminstronger thanmax.
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.