In CSS, how do position: relative and position: absolute differ in their effect on normal flow, and what are their top/left offsets measured from?
answer
- one keeps its space, one does not
- layout first, then shift
- search upward for a positioned ancestor
- padding box of that ancestor
- shrink-to-fit once out of flow
basics
~20 sA relatively positioned box keeps its space in normal flow and is only shifted visually from where it would have been. An absolutely positioned box is removed from flow entirely and offset from its containing block instead.
solid answer
~40 sWith `position: relative` the box still participates in normal flow — the layout is computed as if it had not moved, its original space stays reserved, and `top`/`left` then paint-shift it from that spot, so it can overlap neighbours without pushing them. With `position: absolute` the box is taken out of flow: following siblings close up as though it did not exist, its width shrink-to-fits its content instead of filling the parent, and its offsets are measured from its containing block — the padding box of the nearest ancestor whose `position` is not `static`, or the initial containing block if there is none. That asymmetry is why the classic pattern is `position: relative` on a wrapper purely to become the anchor, with `position: absolute` on the child that needs to sit in a corner.
code
css · 15 lines.card {
position: relative;
padding: 16px;
}
.card .badge {
position: absolute;
top: 0;
right: 0;
}
.nudged {
position: relative;
top: 10px;
}go deeper
Be ready to say plainly that relative keeps its space and shifts from where it was, while absolute leaves the flow and offsets from the nearest positioned ancestor. Naming the relative-wrapper plus absolute-child pattern is expected.
Explain the mechanics: layout runs first and relative offsets are applied afterwards, absolute boxes shrink-to-fit, and the anchor search walks up for a non-static ancestor and uses its padding box.
Show the debugging instinct — when something lands in the page corner, you look for the missing positioned ancestor; when a container collapses, you check whether all its children left the flow.
Own the guidance on when absolute positioning is the wrong reach at all: it takes an element out of the flow that flex and grid manage, so reserve it for genuine overlays and anchors rather than as a general layout tool teams then have to maintain.
## Start with normal flow By default every element is `position: static`, which means it sits in normal flow: block boxes stack top to bottom in their parent's content box, inline boxes run along a line. A static box ignores `top`, `right`, `bottom`, `left` and `inset` completely — setting them changes nothing. The `position` property is what makes those offset properties mean anything. ## position: relative — shifted, but still occupying its space A relatively positioned element is laid out exactly as if it were static. Only after layout is finished are the offsets applied as a visual shift. Two consequences follow directly from that ordering: - **Its original space is preserved.** Neighbours do not move to fill the gap, so a shifted box leaves a hole behind it and may overlap whatever it moves toward. - **Offsets are measured from its own normal-flow position**, not from any ancestor. `top: 10px` means "10px down from where you would have been". Offsets on a relative box are also self-consistent by rule: if you specify both `top` and `bottom`, `bottom` is ignored; if you specify both `left` and `right` in a left-to-right writing mode, `right` is ignored. The box cannot be stretched — it can only be moved. Two side effects matter more in practice than the shifting itself. First, a relatively positioned element becomes a containing block for absolutely positioned descendants. Second, being positioned makes `z-index` apply to it, and a positioned element with a `z-index` other than `auto` establishes a stacking context. This is why `position: relative` with no offsets at all is such a common declaration — it is being used purely as an anchor. ## position: absolute — out of flow, anchored to a containing block An absolutely positioned element is removed from normal flow. Its parent no longer accounts for it when sizing itself, and later siblings lay out as if it were not in the document. Its own sizing changes too: `width: auto` no longer means "fill the parent" but shrink-to-fit, so an out-of-flow box collapses to its content width unless you give it a width or opposing insets. Its offsets resolve against the **containing block**: the padding box of the nearest ancestor whose `position` is anything other than `static` — `relative`, `absolute`, `fixed` or `sticky`. If no ancestor qualifies, the containing block is the initial containing block, a viewport-sized rectangle anchored at the document origin, so the box lands relative to the top of the *document* and scrolls away with the page. ```css .card { position: relative; } /* anchor only, no offsets needed */ .card .badge { position: absolute; top: 0; right: 0; /* corner of .card's padding box */ } ``` If you delete `position: relative` from `.card`, the badge does not fall back to the parent — it jumps to the corner of the page, because the parent was never in the running once it was static. ## The pairing that makes this the most-used pattern in CSS Because absolute positioning searches *upward* for a positioned ancestor, you control the anchor by choosing where to put `position: relative`. Badge on a card, caption over an image, dropdown under a button, custom focus ring around a control — all the same shape: relative wrapper, absolute child. ## Where people get burned A relative shift does not reflow anything, so nudging an element with `top` to "fix" spacing leaves the old space behind and usually should have been a margin. Conversely, an absolutely positioned element contributes nothing to its parent's height, so a container holding only absolute children collapses to zero height unless it is given one. ## The rest of the family, in one line each `position: fixed` is like absolute but its containing block is the viewport, so it does not scroll. `position: sticky` stays in flow and behaves like relative until a scroll threshold you define with an inset is crossed. Both are out of scope for the relative-versus-absolute comparison, but knowing that the difference between all five values is mostly *what they are positioned against* is the point of the family.
- If a container holds only absolutely positioned children, why does it end up with zero height?Out-of-flow boxes do not contribute to their parent's content height. The parent sizes itself from its in-flow children only, and there are none, so it collapses. You have to give the container a height, an `aspect-ratio`, or some in-flow content — the absolute children will never push it open.
- What happens if you set both top and bottom on a relatively positioned element?`bottom` is ignored. A relative box cannot be stretched by opposing offsets the way an absolute one can, because its size was already resolved in normal flow; the offsets only shift it. In a horizontal, left-to-right writing mode `top` wins over `bottom` and `left` wins over `right`.
- Why do people write position: relative with no offset values at all?To make the element a containing block for absolutely positioned descendants, so a child with `inset` values anchors to it rather than to a further ancestor or the page. It also makes `z-index` apply to that element, which is the other common reason.
Relative is sliding a book along a shelf while its slot stays empty; absolute is taking the book off the shelf entirely and pinning it to the frame of the bookcase.
saying these in an interview costs you the question
- Says relative elements are removed from the flow like absolute ones
- Assumes an absolute child anchors to its direct parent automatically
- Thinks absolute offsets are measured from the viewport
- Believes top/left work on a default static element
- Expects an absolutely positioned child to stretch the parent's height