Normal Flow

Understand the default document flow — block and inline formatting contexts, containing blocks, and the frequently misunderstood behavior of margin collapsing.

Before you ever write a line of flexbox or grid, the browser is already laying out your page using what's called 'normal flow' — the default algorithm that stacks block elements vertically and flows inline elements horizontally within lines. Nearly every layout technique in CSS is really just a way of opting specific elements OUT of normal flow (floats, positioning) or replacing it locally (flexbox, grid). So understanding normal flow deeply — especially its quirkiest behavior, margin collapsing — is essential groundwork.

Block and inline formatting contexts

A block formatting context (BFC) is the layout mode block-level elements follow: each block box is placed directly below the previous one, stacking vertically from top to bottom, each one taking the full available width unless told otherwise. An inline formatting context (IFC) is the layout mode inline-level content follows within a block: text and inline elements flow left-to-right (in LTR languages), wrapping onto new 'line boxes' automatically when they run out of horizontal space, much like words wrapping in a word processor.

These two contexts nest inside each other constantly: a <div> establishes vertical block stacking for its block-level children, while the text directly inside a <p> forms an inline formatting context that wraps into lines. Certain properties and values (overflow other than visible, display: flow-root, floats, absolute positioning) can create a NEW, self-contained block formatting context, which is what allows flow-root to 'contain' floated children as covered in the previous lesson.

Containing blocks

Every element's size and position are calculated relative to a reference box called its containing block. In normal flow, an element's containing block is generally the content box of its nearest block-level ancestor (its parent, essentially) — this is why percentage widths resolve against the parent's width, and why block elements stack inside their parent's boundaries. Positioned elements calculate their containing block differently, which the next lesson covers in detail.

Margin collapsing

This is one of the most confusing behaviors in CSS for newcomers, so it deserves a careful explanation. When two block elements are stacked vertically in normal flow, and the bottom margin of the first element touches the top margin of the second, the two margins do NOT add together. Instead, they COLLAPSE into a single margin equal to the LARGER of the two values.

Concretely: if paragraph A has margin-bottom: 20px and paragraph B directly below it has margin-top: 30px, the actual visible gap between them is 30px — not 50px as you might expect from simply adding the two. The browser takes the larger of the two adjacent margins and discards the smaller one.

Margin collapsing only happens for VERTICAL margins between elements in normal block flow — horizontal margins never collapse, and margins never collapse across elements that establish a new block formatting context (like flex/grid containers or their children), or when padding/border sits between the two margins, or for elements with overflow other than visible. Margins can even collapse through an empty parent: if a parent element has no top border, no top padding, and no content separating its margin from its first child's margin, the parent's top margin and the child's top margin collapse together and can visually 'escape' outside the parent entirely — a very surprising behavior when first encountered.

• Adjacent vertical margins between block siblings collapse to the LARGER value, not the sum • Horizontal margins never collapse • Margins don't collapse across elements that establish a new formatting context • A parent with no border/padding separating it from its first/last child can have its margin collapse THROUGH to that child, potentially escaping the parent's own box

Margin Collapsing Example

.card-a { margin-bottom: 20px; }
.card-b { margin-top: 30px; }
/* actual gap between .card-a and .card-b = 30px (the larger value), not 50px */

Code Examples

Since every paragraph has the SAME 16px margin on both top and bottom, adjacent paragraphs collapse their touching margins down to a single 16px gap between each pair — not 32px. This is actually convenient: it's why stacking many identical paragraphs still looks evenly and predictably spaced. Expected result: Consistent 16px gaps between every paragraph, not 32px.

Normally .child's top margin would collapse through .container and 'escape' upward if .container has no border/padding on top. Giving .container overflow: hidden (or the more purposeful display: flow-root) creates a new block formatting context, which stops the margin from collapsing through — the 40px now correctly pushes .child down from inside .container.

Even though the two declared values are different (48px and 24px), the rendered gap is only 48px — the smaller 24px value is entirely discarded, not added. This trips up developers who expect CSS margins to behave additively like most other stacked measurements.

p {
  margin-top: 16px;
  margin-bottom: 16px;
}
.container {
  overflow: hidden; /* or display: flow-root */
}
.container .child {
  margin-top: 40px;
}
.hero { margin-bottom: 48px; }
.section { margin-top: 24px; }
/* gap between .hero and .section = 48px, the LARGER value */

Margin Collapsing

Adjacent vertical margins between block siblings merge into ONE gap equal to the larger of the two values — they are never summed.

Element A
  margin-bottom: 20px  --+
                        | collapses to 30px (the larger)
  margin-top: 30px     --+
Element B

Important Rules

Adjacent vertical margins between block-level siblings in normal flow collapse to the larger single value, never the sum.

Only vertical margins collapse; horizontal margins never do.

A new block formatting context (overflow ≠ visible, flow-root, flex/grid containers, floats, absolutely positioned elements) stops margin collapsing across its boundary.

A parent's margin can collapse through to its first or last child if no border, padding, or content separates them — this can make the child's margin appear to 'escape' the parent.

Why this matters on real projects

Margin collapsing is one of the top sources of 'why isn't my spacing what I expect' bugs reported by developers new to CSS. Modern layout techniques like flexbox and grid became popular partly BECAUSE they don't have margin collapsing between their flex/grid items — spacing via gap in those contexts is fully predictable and additive, which is one reason experienced developers often reach for flex/grid even for simple vertical stacks.

/* Expecting 20px + 30px = 50px gap between stacked divs */ .a { margin-bottom: 20px; } .b { margin-top: 30px; }

/* Understand the actual gap is 30px (the larger value), and design spacing around that */ .a { margin-bottom: 20px; } .b { margin-top: 30px; }

The CSS itself isn't 'wrong' here — the mistake is a developer's mental model. Many developers waste time debugging a 'missing' 20px of space, not realizing margin collapsing is working exactly as specified; the fix is understanding the behavior, not necessarily changing the code (though using padding or a flex/grid container instead of relying on sibling margins sidesteps collapsing if summed spacing is truly needed).

[Predict the Output] Two sibling <div>s are stacked in normal flow with no border, padding, or formatting-context boundary between them. Div 1 has margin-bottom: 15px; Div 2 has margin-top: 25px. What is the actual rendered gap between them? .div1 { margin-bottom: 15px; } .div2 { margin-top: 25px; } A. 15px B. 25px C. 40px D. 10px

B. 25px — Adjacent vertical margins between block siblings collapse into a single gap equal to the LARGER of the two values — 25px here — not their sum (40px) and not their difference (10px). The smaller 15px value is simply discarded.

Explain, in the comment, what the actual vertical gap will be between two sibling <div> elements where the first has margin-bottom: 12px and the second has margin-top: 40px, assuming normal block flow with no formatting-context boundary between them.

/* The gap is 40px — the larger of the two margins — not 52px. */

Recall that adjacent vertical margins collapse to the larger single value, not the sum.