In CSS, why does height: 100% on a child element so often do nothing, while width: 100% works as expected?
answer
- percentage of what, exactly
- parent height depends on the child
- definite versus indefinite size
- auto is the fallback value
- viewport or stretch breaks the cycle
basics
~20 sA percentage height resolves against the containing block's height, and a normal-flow parent with height: auto has no definite height to resolve against, so the percentage behaves as auto. Widths work because a block's available inline size is always known.
solid answer
~50 sPercentages need something definite to be a percentage *of*. In the inline direction a block-level box always has a known available width handed down from its containing block, so `width: 100%` resolves immediately. In the block direction the usual flow is the opposite: a parent with `height: auto` sizes itself *from* its children, so asking the child for 100% of the parent while the parent waits on the child is circular. CSS breaks the cycle by treating the percentage as `auto`. The fixes all make the parent's height definite: give the whole ancestor chain a height (`html, body { height: 100% }`), use a viewport unit like `100dvh`, let the parent be a grid or flex container so the item stretches into a definite line, or give the child an `aspect-ratio` so its height comes from its width instead.
code
css · 7 lines/* Fails: .card has no definite height to be a percentage of */
.card { padding: 1rem; }
.card > .fill { height: 100%; background: #eee; }
/* Works: the parent's block size is definite, so the item stretches */
.card { display: grid; block-size: 12rem; }
.card > .fill { background: #eee; }go deeper
Know that a percentage height needs the parent to have a real height, and that setting height on html, body and every wrapper in between is the traditional fix. Recognise the symptom when a full-height panel collapses.
Explain the circular dependency: a normal-flow parent with auto height is sized by its children, so the percentage has nothing definite to resolve against and computes to auto. State the absolutely positioned exception.
Choose deliberately between the ancestor height chain, viewport units including the dynamic dvh family, and letting a flex or grid parent stretch the item, and say why the ancestor chain is the most brittle of the three.
Treat full-height layout as an architectural decision: pick one app-shell strategy so panels do not each invent their own, and be explicit about mobile viewport instability and how it interacts with scroll containers.
## Definite versus indefinite sizes CSS distinguishes a **definite** size — one the layout engine can know without laying out the contents — from an **indefinite** one, which is only discovered by doing the layout. A percentage can only resolve against a definite size in the relevant axis. Normal flow is deeply asymmetric about this. Sizing goes *down* the tree in the inline (horizontal, in a left-to-right writing mode) direction: a block-level box is handed an available width by its containing block and fills it. Sizing goes *up* the tree in the block (vertical) direction: a box with `height: auto` is as tall as the boxes it contains. That asymmetry is the whole answer. `width: 100%` asks for 100% of something already decided. `height: 100%` asks for 100% of something that is *waiting on this very element* to be decided. ## The rule as written CSS 2.1 states it directly: if the height of the containing block depends on the element's own content — that is, the containing block's height is not specified explicitly — and the element is not absolutely positioned, the percentage height computes to `auto`. Modern specs phrase the same thing as a percentage against an indefinite size behaving as `auto`. Note the exception hiding in that sentence: an **absolutely positioned** element does resolve percentage heights against its containing block's padding box even when that block's height came out auto. By the time an out-of-flow box is laid out, the ancestor's used height is already known, and the abspos box has no say in it, so there is no cycle to break. ```css /* does nothing: .page has height: auto */ .page > .panel { height: 100%; } /* works: the containing block's used height is already resolved */ .page { position: relative; } .page > .overlay { position: absolute; inset: 0; } ``` ## The classic fixes **Make the chain definite.** The oldest fix propagates a definite height from the viewport down: ```css html, body { height: 100%; } #app { height: 100%; } .panel { height: 100%; } ``` Every link must be present. Skip one ancestor and the chain breaks at that point, which is why "I set height: 100% and nothing happened" is nearly always a missing intermediate rule. **Use a viewport unit.** `height: 100vh` sidesteps the ancestors entirely because the viewport is definite by construction. On mobile the dynamic variants `100dvh`, `100svh` and `100lvh` exist because the visible viewport changes as browser chrome collapses. **Let the parent lay out its children.** If the parent is a flex or grid container with a definite size in the block axis, its items stretch to fill it by default, which makes the item's own height definite and lets *its* children's percentages resolve. This is why converting a stubborn wrapper to a grid container so often makes a nested `height: 100%` suddenly start working. **Derive the height from the width.** `aspect-ratio` computes the block size from the inline size, which is definite. That is the cleanest way to get a proportional box without touching any ancestor. ## Related traps in the same family - **Percentage `top`/`bottom` on a positioned box** resolve against the containing block's height and hit the same indefiniteness. - **Percentage vertical padding and margin resolve against the containing block's *width***, not its height. That surprises people, but it is deliberate: it makes them resolvable in a single pass. It is also the trick behind the pre-`aspect-ratio` "padding-top: 56.25%" ratio box. - **`min-height: 100%`** fails for exactly the same reason `height: 100%` does; it is not a workaround. ## What a good answer sounds like Name the cycle, name the rule (percentage against an indefinite height behaves as auto), name the abspos exception, and then give two or three fixes with the tradeoff — the ancestor chain is fragile, viewport units ignore the layout, flex/grid stretch is usually the most robust, and `aspect-ratio` is right when you actually want proportion rather than fill.
- Why does percentage padding-top resolve to something even when height: 100% does not?Percentage padding and margin resolve against the *inline* size of the containing block in both axes — vertical padding included. The inline size is definite before layout of the contents begins, so there is no cycle. That quirk is exactly what made `padding-top: 56.25%` the classic pre-`aspect-ratio` trick for a 16:9 box.
- Does the same failure hit min-height: 100%?Yes. `min-height` takes percentages against the same containing-block height, so if that height is indefinite the percentage behaves as `auto` and the minimum is effectively zero. Swapping `height` for `min-height` is not a workaround; you still have to make the ancestor definite, use a viewport unit, or let a flex or grid parent stretch the item.
- Why does an absolutely positioned child with height: 100% work inside an auto-height parent?Out-of-flow boxes do not contribute to their containing block's height, so there is no circular dependency. CSS 2.1 exempts absolutely positioned elements from the rule and resolves the percentage against the containing block's padding-box height, which is already known by the time the out-of-flow box is laid out.
Asking for 100% of a parent that sizes itself from its children is like asking to take half of whatever is left after you have taken your share — the answer depends on itself, so the browser refuses to play and treats it as auto.
saying these in an interview costs you the question
- Says height: 100% needs the parent to have position: relative
- Claims the browser is buggy or that the property is unsupported
- Suggests min-height: 100% as the workaround
- Thinks percentage vertical padding resolves against height
- Reaches for a JavaScript resize handler as the first fix