Skip to content
academia.sh

Lesson 13 / 26

Floating Elements

Floating shortening line boxes so text flows around a box; the containing box's height problem, and the clearing and formatting-root solutions.

Contents

Normal flow laid boxes out in two patterns: blocks stacked, inline boxes side by side. In the station page’s location section there is a request that fits neither pattern — the location image should sit to the left of the text, and the caption text should flow around the image.

The mechanism defined for this behavior is called float. Floating pushes a box to one edge of the inline axis and has the line boxes in flow pass around it. This lesson takes up what floating does, what it does not do, and the height problem it brings with it.

Floating Shortens Line Boxes

A floated box is taken out of normal flow but keeps affecting flow. Its effect is in one place only: line boxes that share its vertical range get shortened.

// float.mjs — computes how a floated box shortens line boxes
function lines({ containerWidth, floatWidth, floatHeight, lineHeight, lineCount }) {
  const result = [];
  for (let i = 0; i < lineCount; i++) {
    const top = i * lineHeight;
    const insideFloat = top < floatHeight;
    const available = insideFloat ? containerWidth - floatWidth : containerWidth;
    result.push({ line: i + 1, top, available, start: insideFloat ? floatWidth : 0 });
  }
  return result;
}

for (const s of lines({ containerWidth: 640, floatWidth: 200, floatHeight: 120, lineHeight: 24, lineCount: 7 })) {
  console.log(`line ${s.line}: top=${String(s.top).padStart(3)}  x=${String(s.start).padStart(3)}..640  available width=${s.available}`);
}

console.log("\n--- the container's height ---");
function containerHeight({ flowContentHeight, floatHeight, isFormattingContext }) {
  return isFormattingContext ? Math.max(flowContentHeight, floatHeight) : flowContentHeight;
}
for (const isFormattingContext of [false, true]) {
  const h = containerHeight({ flowContentHeight: 48, floatHeight: 120, isFormattingContext });
  console.log(`formatting context=${String(isFormattingContext).padEnd(5)} -> container height=${h}`);
}
line 1: top=  0  x=200..640  available width=440
line 2: top= 24  x=200..640  available width=440
line 3: top= 48  x=200..640  available width=440
line 4: top= 72  x=200..640  available width=440
line 5: top= 96  x=200..640  available width=440
line 6: top=120  x=  0..640  available width=640
line 7: top=144  x=  0..640  available width=640

--- the container's height ---
formatting context=false -> container height=48
formatting context=true  -> container height=120

The first five line boxes are 440 units wide instead of 640, and start from 200: that is where the floated image sits over that range. The sixth line, since it passes the image’s bottom edge, returns to full width. This is what it means for text to flow around the image — not some mysterious wrap behavior, but line boxes being shortened.

Two conclusions follow from this.

Floating affects only line boxes, not block boxes. A block box next to a floated box does not narrow; it keeps filling its container and passes underneath the floated box. Only the text lines inside that block box get shortened. This is why using float to build column layout is fragile.

Floating has a direction. float: inline-start pushes the box to the start of the inline axis, float: inline-end to the end. Their physical counterparts are left and right; logical values adapt to writing direction.

The Container’s Height Collapses

The output’s second block gives the actual problem with floating. Since a floated box is taken out of flow, it takes no part in its container’s height. If the container has 48 units of flow content and a 120-unit floated image, the container’s height comes out 48; the image spills outside the box.

The visible result is that a background or border given to the container does not cover the image. This is not a bug, it is a direct consequence of the definition: a box out of flow contributes nothing to flow’s height.

There are two solutions.

Solution 1: Clearing

The clear property tells a box to drop below any floated boxes that precede it.

.clear { clear: both; }

clear: inline-start drops below only start-floated boxes, inline-end below only end-floated ones, both below either.

Clearing is used to fix the container’s height indirectly: if a clearing box is placed as the container’s last child, that box drops below the float and the container stretches to contain it. This can be done without cluttering the document with a pseudo-element:

.clearfix::after {
  content: "";
  display: block;
  clear: both;
}

The solution works but is indirect: a content element is generated to solve the height problem.

Solution 2: Independent Formatting Root

The direct solution is to make the container a block formatting context. Such a box includes the floated boxes inside it in its own height; the output’s last line, where the height is 120 in the formatting context=true case, shows this.

The explicit way to make a box a formatting root:

.location-section { display: flow-root; }

There are also indirect ways that give the same result: an overflow value other than visible, the box sitting inside a flexible box or grid layout, being absolutely positioned. These come with side effects — writing overflow: hidden also clips overflowing content. flow-root, on the other hand, does only the job wanted.

A formatting root has three effects, and all three have already come up in earlier lessons:

  • Floated boxes inside it are included in its height.
  • Floated boxes outside it do not spill into it.
  • Margin collapsing stops at its boundary.

All three come from the same definition: the box completes block placement within itself.

The Floated Box’s Own Size

A floated box produces a block box but does not fill its container. When its width is not declared, it shrinks to fit its content: the box pulls in to the narrowest size it can fit into. This is the opposite of a block box’s behavior in flow.

The result is that declaring a width for a floated box is often necessary. A box carrying text that shrinks grows the line count and stretches unpredictably; a box carrying an image settles at the image’s natural size.

A second behavior is that floated boxes push against each other. When more than one box is floated to the same edge, they line up side by side; whatever does not fit on the line drops to a row below. Where it drops is determined by the bottom edges of the preceding floated boxes, and if the boxes are different heights, the arrangement becomes irregular. This is another source of the fragility of building grid-like layout with float.

The Shape of the Float Boundary

The boundary that shortens line boxes is, by default, the floated box’s margin rectangle — a rectangle no matter its shape. This boundary can be changed with the shape-outside property:

.location-image {
  float: inline-start;
  shape-outside: circle(50%);
  shape-margin: 12px;
}

shape-outside only changes the boundary of flowing text; it does not clip the box itself. If the image is also wanted to look circular, clip-path is written separately. The two properties do different jobs: one decides where neighbors pass, the other decides what part of the box is painted.

What Floating Is For

Float has long been used as a tool for building column-based page layouts. This use does not fit float’s definition, and the output’s first block shows why: floating does not place a box into a column, it only shortens line boxes. Equalizing column heights, aligning boxes, and spacing them remain problems that must be solved separately.

The one use that matches float’s own definition is flowing text around a box. The layout systems taken up in the next course take over the job of building columns, and do it with fewer declarations and more predictably than float. Float still has a place in those courses too, because no layout model flows text around a box.

/* station.css — step 12: location image and text flowing around it */
.location-section { display: flow-root; }

.location-image {
  float: inline-start;
  inline-size: 200px;
  margin-inline-end: 16px;
  margin-block-end: 8px;
}

.location-section .source-note { clear: both; }

The display: flow-root declaration makes the section contain the image. The margin given toward the end of the image keeps text from pressing against it — a floated box’s margin edge is the boundary that shortens line boxes, not its border edge.

The last rule drops the source note below the image; the note should be read not to the right of the text but at the end of the section.

Summary

  • A floated box leaves normal flow but shortens line boxes that share its vertical range; this is what it means for text to flow around it.
  • Floating affects line boxes, not block boxes; this is why it is fragile as a tool for building column layout.
  • A floated box takes no part in its container’s height; the container collapses and its background or border does not cover the floated content.
  • clear drops a box below floated ones; used as the container’s last child, it fixes the height indirectly.
  • display: flow-root makes a box an independent formatting root: it includes floats in its height, keeps outside floats from spilling in, and stops margin collapsing at its boundary.

Next Step

Floating took a box out of flow but did not let its destination be chosen; the box was only pushed to one edge. Placing an element at a wanted point in the document — a badge in a card’s corner, a heading held fixed while scrolling — needs a different mechanism. The next lesson defines positioning’s five values and what box each is measured against.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close