skip to content

For an in-flow block-level element in CSS, what does `width: 50%` resolve against, and what does a percentage in `padding` resolve against?

level: middleimportance: should knowfreq 52%

answer

  1. percentages need a reference length
  2. the containing block, not the parent's border box
  3. inline size, both axes
  4. padding-top keys off width
  5. avoids a circular height dependency

basics

~20 s

A percentage width resolves against the containing block's width — for an in-flow block element, the content-box width of its nearest block container ancestor. Percentages in padding and margin resolve against that same inline size, even for padding-top and padding-bottom.

solid answer

~40 s

Both resolve against the **containing block**, and for an in-flow block-level element that is the *content box* of its nearest block container ancestor — not the parent's border box, and not the viewport. So `width: 50%` is half of the space inside the parent's padding, and `box-sizing` decides whether that 50% describes the child's content box or its border box. The part that surprises people is padding and margin: **every** percentage there resolves against the containing block's **inline** size — the width in a horizontal writing mode — so `padding-top: 10%` is ten percent of the parent's *width*, not its height. The spec does that deliberately, because resolving block-axis padding against a height that often depends on the content would be circular.

code

css · 9 lines
css
.parent {
  width: 800px;
  padding: 40px;
}

.child {
  width: 50%;      /* 50% of the 720px content box = 360px */
  padding-top: 10%; /* 10% of 720px = 72px, from the WIDTH */
}

go deeper

for a junior

Know that a percentage width is a share of the space inside the parent, after the parent's own padding is subtracted — not a share of the screen. Say the reference is the containing block.

for a middle

Explain that percentages in padding and margin resolve against the containing block's inline size on every side, including top and bottom, and be ready to compute a concrete example from a stated parent width.

for a senior

Demonstrate the debugging instinct: when a percentage row overflows, identify whether the cause is content-box padding, an added margin, or a wrong containing block, and pick between a border-box reset and calc() deliberately.

for a principal

Own the convention question: decide whether layouts express spacing through percentage widths plus a sizing reset or through modern gap-based layout, and make that choice consistently so contributors are not mixing two mental models in one codebase.

## The containing block is the reference A percentage in CSS is meaningless on its own; every percentage names a reference length. For sizing and spacing, that reference is the element's **containing block**. For an ordinary in-flow block-level element — `position: static` or `relative` — the containing block is the **content box** of its nearest block container ancestor. Two details in that sentence do real work: - **Content box, not border box.** If the parent has `padding: 40px`, a child's `width: 100%` fills the space *inside* that padding, not the parent's painted width. This is why nesting a full-width child in a padded parent works out rather than overflowing. - **Nearest block container, not literally `parentNode`.** Inline ancestors and some display types are skipped when the box tree resolves the containing block. (Absolutely positioned and fixed boxes resolve against a different containing block; that is a positioning question rather than a box-model one.) ## Percentage width ```css .parent { width: 800px; padding: 40px; } .child { width: 50%; } /* 50% of 720px = 360px */ ``` The parent's content box is 800 − 80 = 720px, so the child resolves to 360px. `box-sizing` then decides *which* of the child's boxes that 360px describes: under the default `content-box` it is the child's content box, and its own padding and border are added on top; under `border-box` the 360px is the child's painted width with its padding and border carved out of it. That interaction is the source of the classic overflow bug: ```css .col { width: 33.333%; padding: 0 16px; } /* content-box: 100% + 96px → overflows */ .col { box-sizing: border-box; width: 33.333%; padding: 0 16px; } /* fits */ ``` Under `content-box` the three columns claim the full inline size *and then* add 96px of padding, so the row overflows or wraps. Under `border-box` the padding is taken out of each 33.333% share and the row fits exactly. The alternative fix without the reset is arithmetic in the declaration itself, `width: calc(33.333% - 32px)`, which is doing by hand what `border-box` does automatically. ## Percentage padding and margin: always the inline size Here is the rule that catches people: **all** percentage values on `padding-*` and `margin-*` resolve against the containing block's **inline** size — its width in a horizontal writing mode — regardless of which side they apply to. ```css .parent { width: 600px; height: 200px; } .ratio { padding-top: 50%; } /* 300px of padding, from the 600px width */ ``` `padding-top: 50%` is 300px, not 100px. The reasoning is to avoid a circular dependency: an element's height frequently depends on its own content, and its content height would then depend on padding that depends on the height. Anchoring both axes to the inline size breaks the loop. A long-standing consequence is that a percentage in the block-axis padding produces a box whose spacing scales with the container's *width*, which is what the old percentage-padding ratio technique exploited before a dedicated property existed. The same inline-size rule applies to `margin-top` and `margin-bottom` percentages, which is why a `margin-top: 5%` on a wide container is far larger than people expect. ## Percentage heights are a separate story `height: 50%` does **not** follow the inline-size rule — it resolves against the containing block's height, and it only produces a definite length when that height itself is definite. That resolution chain (and what `auto` does to it) is its own topic; the point to carry here is simply that width percentages and padding percentages both key off the inline size, while height percentages do not. ## Things that are not the reference - **Not the viewport.** Viewport-relative sizing has its own units; a percentage never silently means "of the screen" unless the containing block chain happens to reach a viewport-sized ancestor. - **Not the parent's border box.** Parent padding is subtracted first. - **Not the element's own size.** Percentages in `transform: translateX()` do resolve against the element's own border box, which is a genuinely different rule and a good way to check whether a candidate is guessing. ## Answering it in an interview Say "the containing block", define it for the in-flow case as the parent block container's content box, then volunteer the padding surprise — that percentage padding on *any* side resolves against the inline size — and give the circularity reason. Finishing with the three-column overflow example shows you have hit the bug in real code, not just read the spec.

  • Why did the spec make block-axis percentage padding resolve against the inline size?
    To avoid circularity. An element's height often depends on its content; if `padding-top: 10%` resolved against that height, the height would depend on padding that depends on the height. Anchoring every percentage in `padding` and `margin` to the containing block's inline size gives a value that is always resolvable in one pass, at the cost of the counter-intuitive behaviour.
  • Does `box-sizing` change what a percentage width resolves against?
    No — the reference is the containing block either way. `box-sizing` only changes which of the *child's* boxes the resolved length describes. `width: 50%` of a 720px containing block is 360px in both models; under `content-box` that is the child's content box with padding added outside, under `border-box` it is the child's painted width with padding carved inside.
  • If a child's `width: 100%` fills a padded parent exactly, why does adding a margin to the child cause overflow?
    Because the percentage resolved against the parent's content box, which the child now fills completely, and margin sits outside the child's border box. There is no room left to absorb it, so the margin box exceeds the containing block. Either drop the margin in favour of padding on the parent, or use `calc(100% - 2 * margin)`.

saying these in an interview costs you the question

  • Says a percentage width is a share of the viewport
  • Resolves percentage width against the parent's border box
  • Thinks padding-top: 10% is ten percent of the height
  • Assumes box-sizing changes the percentage's reference length
  • Confuses percentage padding with percentage translate

context