Web Platform · CSS Layout
Used Values and the Containing Block
How a declared width becomes a pixel length: which box the percentages resolve against, what box-sizing changes, and how auto margins and min-max constraints are resolved.
A declaration of width: 50% does not specify a length. It specifies a relation, and the browser resolves that relation against a rectangle chosen by the box’s positioning scheme. The choice of rectangle, and the order in which auto values, percentages and min/max constraints are applied, determine the final pixel geometry. Most layout bugs that look like “the box is two pixels too wide” are failures of one of these resolutions rather than of arithmetic.
The box model itself is four nested rectangles: content, padding, border and margin. What a width declaration applies to depends on box-sizing, and which rectangle it is measured against depends on the containing block, which is not the same thing as the parent element.
The width equation for a block box in normal flow
For a block-level, non-replaced box in normal flow, the used inline size is the solution of a constraint equation:
margin-left + border-left + padding-left + width
+ padding-right + border-right + margin-right = containing block inline size
The margins, borders and padding are known once their own percentages and lengths are resolved. One of width, margin-left or margin-right may be auto. The resolution order in CSS 2.1 §10.3.3 is:
- If
widthis notautoand the fixed edges plus the non-automargins already exceed the containing block, anyautomargins are treated as zero for the remaining steps. - If every value is then specified and the sum does not match the containing block, the equation is over-constrained: the inline-end margin is recomputed from it, discarding whatever was declared. In a left-to-right box that is
margin-right. - If exactly one value is
auto, it absorbs the remaining space. - If
widthisauto, theautomargins become zero and the width takes what is left. - If the width is definite and both margins are
auto, they take equal shares, which is the centring case below.
The over-constrained rule is why a block with width: 100% plus horizontal padding overflows its parent when the padding is added outside the declared width. The width is honoured, the padding is honoured, and the margin becomes negative to keep the equation true.
Percentage widths on a block box resolve against the containing block’s content box inline size. Percentage horizontal padding and margins also resolve against that same inline size, a point that becomes strange when the same rule applies in the block direction.
Which rectangle is the containing block
The containing block is the rectangle that percentages and positioning offsets resolve against. Its identity depends on the box’s position:
- Static or relative box. The containing block is the content box of the nearest block container ancestor. It is not necessarily the parent: an inline-level ancestor does not provide one, and the search continues upward past it.
- Absolutely positioned box. The containing block is the padding box of the nearest ancestor with
positionother thanstatic. If there is none, it is the initial containing block, which is the viewport-sized rectangle at the document origin. - Fixed box. The containing block is the viewport, unless an ancestor establishes a containing block for fixed-position descendants through a transform, a filter,
perspective,contain: layout,contain: paint, orwill-changeon one of those properties. This is the mechanism by which a transformed ancestor traps a fixed header inside a scrolling card.
Two rectangles differ by exactly the ancestor’s padding. A child with position: absolute; inset: 0 inside a relatively positioned parent with 20 pixels of padding fills the parent’s padding box, so its own border edges sit on the parent’s padding edge, inside the parent’s border, rather than on its content edge. The same absolute child in a static parent measures against the initial containing block and spans the viewport.
Relative positioning does not change the containing block for the element’s own children beyond making the element a candidate; it offsets the element’s painted position without affecting the space reserved for it in normal flow, as described in Positioning and Stacking Contexts.
What box-sizing changes
The initial value of box-sizing is content-box. A box with width: 300px; padding: 20px; border: 2px solid occupies 344 pixels along the inline axis: 300 of content plus 40 of padding plus 4 of border. box-sizing: border-box reinterprets the declaration so that the 300 pixels cover content, padding and border together, leaving 256 pixels of content.
The useful property of border-box is that padding and border changes no longer alter the outer size, so a grid or flex track sized at 300 pixels holds the box exactly. The cost is that the content area shrinks as padding grows, and enough padding can drive the content size to zero. The content size is clamped at zero rather than going negative; the box then overflows its declared width.
Two details are commonly missed. First, box-sizing is not inherited, so a reset that sets it on * is the usual way to make the choice uniform. Second, min and max constraints are expressed in the same box that box-sizing selects, so min-width: 200px with border-box means the border box is at least 200 pixels wide. The specified constraint and the resolved content size therefore stay separated by the padding and border widths.
Vertical percentages are the other trap. padding-top: 10% resolves against the containing block’s inline size, not its block size. A card with 10% top and bottom padding on a 900-pixel-wide parent gets 90 pixels of padding above and below regardless of its height. The rule avoids a circular dependency in which the padding would depend on a height that depends on the padding, and it is the reason the “percentage padding aspect ratio” trick worked before aspect-ratio existed.
Auto margins and alignment
margin-left: auto; margin-right: auto on a block box with a definite width solves the constraint equation by giving each margin half the remaining space, which centres the box. The margins must be auto and the width must not be auto; with width: auto the width takes the free space and the margins become zero.
A single auto margin absorbs all the free space, which pushes the box to one side. This is the standard way to right-align a fixed-width block: margin-left: auto; margin-right: 0.
In flex and grid layout the same declarations behave differently. Auto margins in a flex container absorb free space along the main axis, and if there is surplus space, they distribute it before justify-content has any effect. A flex item with margin-left: auto is pushed to the end of the line, and two auto margins split the space. Grid items follow an analogous rule in their own formatting context. The flex free-space distribution has its own sequence for handling flexibility, and auto margins short-circuit that sequence.
Applying min and max constraints
The used size is computed from width and the containing block first, then clamped by min-width and max-width, then the margins are resolved again if the clamp changed the size. The order matters:
- A box with
width: 60%andmax-width: 40reminside a 1600-pixel container computes 960 pixels, clamps to 640, and only then distributes the remaining 960 pixels of free space to auto margins. - A box with
width: auto,max-width: 40remand auto margins in a 1600-pixel container fills 640 pixels and centres, because the clamp applies before the margins absorb space. min-widthwins overmax-widthwhen the two conflict; per CSS 2.1, the minimum is applied last.
Clamping happens in the box selected by box-sizing. With border-box, a max-width of 640 pixels includes padding and border, so the content area is correspondingly narrower. Mixing border-box on the element with content-box on a child is a reliable way to produce a child wider than its parent’s content area.
Why the model is split this way
The separation between specified, computed, used and actual values exists so that a declaration can be resolved in stages without re-parsing the cascade. Specified values come from the cascade and inheritance; computed values resolve relative units and keywords where possible; used values resolve layout-dependent quantities such as percentages; actual values are what the device can render, after rounding to device pixels.
The staging has observable consequences. Percentage heights only resolve when the containing block has a definite height, which is why height: 100% on a child of an auto-height parent collapses to the content height rather than filling the viewport. The chain of definite heights has to reach a box whose height is fixed or whose containing block is the viewport.
The same staging explains why percentage margins and padding always resolve against the inline size even in vertical writing modes, where the “inline” and “block” axes are rotated. Logical properties keep the reference rectangle tied to the writing mode rather than to a physical side, which is the subject of Writing Modes and Logical Properties.