skip to content

In CSS normal flow, which conditions stop vertical margins from collapsing, and which of those would you choose to keep a child's margin inside its parent?

level: seniorimportance: should knowfreq 52%

answer

  1. separation versus opting out of flow
  2. something between the two border edges
  3. BFC stops children, not siblings
  4. flow-root buys nothing but the context
  5. clip clips but makes no BFC

basics

~20 s

Margins stop collapsing when something separates them (padding, border, content, clearance) or when the box is not a plain in-flow block: floats, absolutely positioned boxes, flex and grid items, and boxes establishing a block formatting context. For a leaking child margin, display: flow-root is the surgical fix.

solid answer

~50 s

Two things break a collapse: separation and opt-out. Separation means something lies between the two margins — `padding`, a `border`, intervening content, or clearance from `clear`. Opt-out means the box is not participating in ordinary block flow: floated boxes, absolutely positioned boxes, flex items and grid items never collapse their margins at all, the root element's margins never collapse, and a box that establishes a block formatting context does not collapse with its **in-flow children** — note that last one says nothing about its siblings. To keep a child's margin inside its parent I reach for `display: flow-root`: it establishes the formatting context and nothing else. `overflow: hidden` also works but makes the box a scroll container that clips shadows and popovers, and `overflow: clip` does *not* create a formatting context, so it does not help at all.

go deeper

for a junior

Know that a 1px padding or a border on the parent stops a child's margin escaping, and that flex and grid containers do not have the problem.

for a middle

Sort the conditions into two groups — margins separated by something, versus boxes that opt out of block flow — and name display: flow-root as the dedicated fix.

for a senior

Choose between fixes on their side effects: what overflow: hidden clips, what a flex container does to child layout, and why a formatting context only protects children.

for a principal

Argue for removing the problem class rather than the symptom: container-owned spacing or a one-direction margin convention, weighed against the cost of migrating an existing stylesheet.

## Two separate reasons a collapse does not happen It helps to split the list in half rather than memorise nine bullet points. **Reason one: the margins are not adjoining.** Two margins collapse only when nothing sits between the border edges they belong to. So a collapse is broken by: - `padding` on the side in question, - a `border` on that side (even `border-top: 1px solid transparent`), - intervening content that generates a line box, - clearance introduced by `clear` on a box following a float, - for the parent/last-child pair, a resolved non-auto `height` or a non-zero `min-height` on the parent. **Reason two: the box opts out of block-flow collapsing entirely.** - **Floats.** A floated box's margins never collapse with anything. - **Absolutely positioned boxes.** `position: absolute` and `fixed` remove the box from flow and from collapsing. - **Flex and grid items.** The specifications state outright that the margins of flex items and of grid items do not collapse. - **The root element.** Its margins never collapse. - **Block formatting context roots.** A box that establishes a BFC does not collapse its margins with its **in-flow children**. ## The BFC clause is the one people misquote "Establishing a formatting context stops margin collapsing" is only half true, and the missing half is where interview answers fall apart. It stops the box collapsing **with its own children**. It does not stop that box's margins collapsing with its **siblings'** margins. Putting `overflow: hidden` on box A to stop A's bottom margin merging with B's top margin does nothing at all; putting it on A to stop A's *child's* top margin escaping works perfectly. ## Choosing a fix When a child margin is escaping a container, all of these work, but they cost different things: - **`display: flow-root`** — establishes a block formatting context and does nothing else: no size change, no clipping, no effect on how children lay out. This is the fix the property was added for, and it is the default choice. - **`padding-top: 1px`** or a transparent border — separates the margins, but changes the box's metrics and reads as a hack. - **`overflow: hidden` / `auto` / `scroll`** — establishes a BFC as a side effect of making a scroll container. That container clips overflow, which silently breaks box shadows, focus rings and any absolutely positioned menu that needs to escape, and it becomes the scroll container that a `position: sticky` descendant sticks within. Use it when you actually want clipping. - **`display: flex` / `grid`** — reliable, since items never collapse, but it re-lays-out every child to buy one behaviour. - **`overflow: clip`** — a trap. `clip` is the one overflow value that does **not** establish a block formatting context, so it clips without stopping the collapse. ## The structural alternative A fix applied per container is a fix you will apply forever. Two conventions remove the problem class instead: ```css /* container owns the spacing */ .stack { display: flex; flex-direction: column; gap: 1rem; } ``` or a single-direction margin convention, where components only ever declare margin on one block side, so two adjoining margins are never both non-zero and the collapse result is unambiguous either way. Both are cheaper than teaching every contributor the collapsing rules. ## What an interviewer is listening for Not the list. They want to hear that you know *why* each entry is on it — separation versus opting out of block flow — that you keep the BFC clause scoped to children, and that you pick a fix by its side effects rather than reaching for `overflow: hidden` by reflex.

  • Why is overflow: hidden a risky way to stop a margin escaping?
    Because you are asking for clipping in order to get a formatting context. The box becomes a scroll container: shadows, focus rings and absolutely positioned menus that need to leave the box get cut off, and it becomes the scroll container a sticky descendant sticks within. `display: flow-root` gives the formatting context with none of that.
  • Does putting a border on a box stop its margins collapsing with its siblings?
    No. A border on the box's top separates that top margin from its first child's top margin, and a bottom border does the same at the bottom. It does not sit between the box's own bottom margin and the next sibling's top margin, so the sibling collapse still happens exactly as before.
  • You inherit a stylesheet full of overflow: hidden used purely to contain margins. How would you unwind that?
    Identify which declarations exist for clipping and which for containment — usually visible from whether anything overflows. Replace the containment-only cases with `display: flow-root`, which is behaviourally identical minus the clipping, and verify shadows, focus outlines and any sticky descendants that the scroll container was suppressing. Then move the underlying spacing to container-owned `gap` where it is cheap to do so.

saying these in an interview costs you the question

  • Says any new formatting context stops sibling margins collapsing
  • Uses overflow: hidden as the default containment fix
  • Thinks overflow: clip behaves like overflow: hidden for margins
  • Claims margins collapse inside a flex container
  • Believes only padding, never a border, breaks the adjacency

context