skip to content

Floats and Clearing (Legacy)

Floats were the layout system before flex and grid, and they still matter for wrapping text around an image and for reading older codebases. Interviewers ask what clearfix was working around and why floats collapse a parent's height.

part ofCSSoverview, primer and where to startread it →
on this pageshow

questions

5

In CSS, why does a container whose children are all floated collapse to zero height, and what makes it contain them again?

level: middleimportance: must knowfreq 62%

answer

  1. auto height counts in-flow children
  2. floats are out of flow
  3. container must contain its floats
  4. flow-root, or overflow as a side effect
  5. clearfix adds a cleared in-flow child

basics

~20 s

A block container with auto height measures only its in-flow children, and floats are out of flow, so it collapses. Make it contain floats by giving it display: flow-root, or add a cleared element after the floats.

solid answer

~50 s

A block container with `height: auto` computes its height from its in-flow children, and floated children are out of flow, so a row whose children are all floated has nothing to measure and collapses. The fix is to make the container contain its floats. The modern one-liner is `display: flow-root`: it establishes a block formatting context, so the container's auto height stretches to include the floats, with no clipping and no scrollbars. `overflow: hidden` or `overflow: auto` has the same containing side effect, but it also clips or scrolls anything that escapes the box, which quietly breaks dropdowns and sticky children. The old clearfix — an `::after` pseudo-element with `content: ""`, `display: block`, `clear: both` — works differently: it inserts an in-flow child that is pushed below the floats, so the parent's height reaches down to it.

code

css · 14 lines
css
.row-legacy::after {
  content: "";
  display: block;
  clear: both;
}

.row-modern {
  display: flow-root;
}

.col {
  float: left;
  width: 33.333%;
}

go deeper

for a junior

Be able to recognise the symptom — a wrapper that renders as a thin line above its floated children — and name one fix out loud, ideally display: flow-root.

for a middle

Explain the mechanic: auto height measures in-flow children only, floats are out of flow, and a block formatting context root is the exception that measures the floats it contains.

for a senior

Show judgment between the fixes: why overflow: hidden is a trap in a component that has dropdowns or shadows, and why converting the row to flex or grid usually beats containing the floats at all.

for a principal

Own the codebase-wide call — whether to keep a clearfix utility alive for compatibility or delete it in a sweep, and how you stop layout hacks from being copied into new components.

## The symptom You write a row of floated columns, put a border on the wrapper, and the border draws as a thin line across the top while the columns hang out below it: ```css .row { border: 1px solid #333; } .col { float: left; width: 33.333%; } ``` Nothing is broken. This is the specified behaviour of auto height plus floats, and it is the single most-asked float question because every legacy stylesheet carries a workaround for it. ## Why the height ignores floats A float is *out of flow*. When a block-level box with `height: auto` sizes itself, it measures the distance from its top content edge to the bottom edge of its last **in-flow** child (or its last line box). Out-of-flow boxes — floats and absolutely positioned boxes — are simply not in that measurement. If every child is floated, the container has zero in-flow content, so its content height is zero. There is one exception, and it is the whole answer to the question: a box that establishes a **block formatting context** computes its auto height so that it also includes the bottom edge of the lowest float it contains. "Contains" is the operative word — floats are laid out inside the nearest formatting-context root, so a container that is a root both owns its floats and measures them. ## Fix 1: display: flow-root `display: flow-root` exists for exactly this job. The outer display stays block, so the element still lays out as a block in its parent; the inner display becomes an independent flow root, so it contains its floats and its auto height grows to fit them. ```css .row { display: flow-root; } ``` No pseudo-element, no clipping, no scrollbars, no extra markup. It also stops margins from collapsing through the box, which is a bonus rather than the point here. It is supported in every current browser — Chrome and Firefox from 2017, Safari from version 13 in 2019 — so on any modern baseline it is the default answer. ## Fix 2: overflow (the accidental one) `overflow: hidden`, `auto`, or `scroll` on the container also makes it a formatting-context root, so it contains floats as a **side effect**. This was the popular one-line fix for years, and it is still what you will find in old code. Its cost is that overflow is a real, load-bearing property: `hidden` clips any child that deliberately escapes the box (tooltips, drop shadows, absolutely positioned menus), and `auto` can produce a scrollbar you never asked for. It can also defeat a descendant that relies on scrolling from an ancestor. Prefer `flow-root` unless you actually wanted the clipping. ## Fix 3: the clearfix The historical workaround does not create a formatting context at all. It manufactures an in-flow child that sits *below* the floats: ```css .row::after { content: ""; display: block; clear: both; } ``` The generated box is in flow, `clear: both` pushes its top edge below the bottom margin edge of the preceding floats, and because it is the last in-flow child, the parent's auto height now reaches it. Older variants used `display: table` on `::before` and `::after` to also stop margins collapsing through; even older code used a literal empty `<div>` with `clear: both`, which is presentational markup and the reason the pseudo-element version won. A clearfix still works, and you should be able to read one, but it is boilerplate that `flow-root` replaced with a single declaration. ## Fix 4: change the layout model `float` does not apply to flex or grid items — it computes to have no effect on them. So turning the container into `display: flex` or `display: grid` makes the whole problem evaporate, because the children stop being floats and become items that the container sizes itself around. On a modern codebase this is usually the real fix: the floats were only ever there to put boxes side by side. ## What does *not* fix it - Putting `clear: both` on the floats themselves. Clearance moves a box down relative to earlier floats; it does not put it back in flow, so the parent still cannot measure it. - `position: relative` on the container. That creates a containing block for positioned descendants and has nothing to do with float containment. - Setting a fixed `height` on the container. That hides the symptom at one viewport width and breaks at every other one. ## What to say in an interview Name the rule (auto height counts in-flow children only), name the exception (a formatting-context root contains and measures its floats), then rank the fixes: `flow-root` first, `overflow` as the legacy accident with clipping costs, clearfix as the pseudo-element that adds a cleared in-flow child, and "stop using floats for layout" as the real answer in new code.

  • Why did overflow: hidden become the popular one-line fix, and what does it cost you?
    It makes the container a block formatting context root, and containing floats falls out of that for free — one declaration, no pseudo-element. The cost is that overflow is not a no-op: `hidden` clips anything meant to escape the box, such as tooltips, dropdowns or shadows, and `auto` can add an unwanted scrollbar. `display: flow-root` gives you the containment without touching overflow.
  • If I put clear: both on the last floated child, does the parent get its height back?
    No. Clearance only moves that box down past earlier floats; it is still a float and still out of flow, so the parent's auto height ignores it exactly as before. The cleared element in a clearfix works because it is a normal in-flow block, not a float.
  • What happens to float: left on a child if I switch the container to display: flex?
    Nothing — `float` does not apply to flex items, so the declaration is ignored and the child is laid out as a flex item. That is why converting a float row to flex or grid removes the collapsed-height problem entirely rather than needing any containment trick.

Auto height is a ruler that only measures things standing on the floor; floats are hanging from the ceiling, so the ruler reads zero until you tell the room to include them.

saying these in an interview costs you the question

  • Says floats are absolutely positioned, so ancestors never see them
  • Claims content: "" is what creates the formatting context
  • Thinks clear: both on the floats themselves restores parent height
  • Reaches for a fixed height on the wrapper as the fix
  • Cannot name anything newer than the clearfix hack

context

open as a page

In CSS, what does the clear property do, and which floats does it actually respond to?

level: juniorimportance: should knowfreq 48%

basics

~20 s

clear pushes a block box down until its top edge is below the bottom margin edge of earlier floats on the given side. It responds only to floats that precede it in the same block formatting context, and only to the sides you name.

open as a page

In CSS, when you set float: left on an image, what happens to the block boxes that follow it and to the text inside them?

level: middleimportance: should knowfreq 50%

basics

~20 s

A float is only partly removed from flow. Following block boxes are laid out as if it were not there, so their backgrounds and borders slide underneath it, but the line boxes inside them shorten to avoid the float's margin box, which is what makes text wrap.

open as a page

You inherit a stylesheet whose page grid is built from floated columns and clearfix rows. Why did float-based layout lose to flex and grid, and what would you check before converting it?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Float was designed for wrapping text around an image, not for columns, so float grids need width maths, containment hacks, and source-order tricks, and still cannot do equal heights, gaps, or centring. Convert them to grid or flex, keeping float only where content genuinely wraps.

open as a page

In CSS, what does shape-outside do, and what must be true of an element for it to have any effect?

level: middleimportance: nice to knowfreq 18%

basics

~20 s

shape-outside changes the contour that inline content wraps around, so text can follow a circle, polygon, or image alpha instead of a rectangle. It only applies to floated elements, and it changes the wrap, not the element's own painted shape.

open as a page