How to completely enclose a floated element in CSS2

Author: Matt Brubeck
Date: 2003-06-24

Introduction

Consider the following CSS and HTML:

.item { border: 1px solid black; padding: 0.5em; }
img { float: left; }

<div class="item">
  <img src="monkey.png"/>
  <p>One time I hired a monkey to
     take notes for me in class.</p>
</div>

Here's how this renders in Mozilla (example 1):

Screenshot 1

If the image is taller than the text, it overruns the border of the enclosing div (except in IE/Win; see browser compatibility below). In some cases this is the desired behavior, but what if we want the border to fully enclose both the image and the text? This document describes the only way I know to do this using completely general, standards-compliant, clean CSS and HTML.

Spacer div hack

One common solution to this problem is known as the "spacer div" hack. We can force the outer div to fully contain both elements by adding an element with clear: both to the end of the div:

img { float: left; padding: 0.5em; }
.item { border: 1px solid black; }
.spacer { clear: both; }

<div class="item">
  <img src="monkey.png"/>
  <p>One time I hired a monkey to
     take notes for me in class.</p>
  <div class="spacer"></div>
</div>

This works, but it requires us to add a new element to our HTML for purely presentational reasons. That means we would have to add markup to every "item" div on our site, costing us time and bandwidth. A CSS-only solution would save effort and bandwith, and avoid polluting our markup with meaningless elements.

CSS3 clear-after property

The CSS3 working draft defines a new property, clear-after, which has the desired effect without any markup changes. We can return to the original markup, and simply add the following rule to our stylesheet:

.item { clear-after: both; }

This rule has the same effect as an element with clear: both at the end of the div, as in the "spacer div" hack above. Unlike the spacer div hack, it achieves this effect without needing the change the markup.

Unfortunately, the clear-after property is not implemented by any current browsers. CSS3 is still in development, and the clear-after property may change before it is standardized.

Solution: CSS2 generated content

Fortunately, we can create the same effect using only CSS2:

.item:after { content: ""; display: block; height: 0; clear: both; }

This uses the same method as the "spacer div" hack: it adds an element at the end of the container div, forcing its bottom border to clear the float. By using the :after pseudo-selector we can generate the extra element from our stylesheet, with no changes to our HTML files.

Here's the new version of our markup:

.item { border: 1px solid black; padding: 0.5em; }
img { float: left; }
.item:after { content: ""; display: block; height: 0; clear: both; }

<div class="item">
  <img src="monkey.png"/>
  <p>One time I hired a monkey to
     take notes for me in class.</p>
</div>

and this what it looks like in Mozilla (example 2):

Screenshot 2

Browser compatibility

The :after pseudo-selector is supported in Mozilla 1.x, Netscape 6 and 7, and Opera 7.

It is not supported in IE 5 or 6, for either Windows or Macintosh (I haven't tested IE/Mac).

(Updated 2003-06-24) Adding height: 100%; causes IE6/Win to expand a containing block around its floated children. Example 3 uses this technique to create a page that works in Mozilla, Netscape 6/7, IE6, and Opera 7 (not tested with other browsers). Note that the containing block must be inside another block-level element. If its immediate parent is the body element, then the height: 100% style will make it fill the entire viewport, at least in IE6.

About this document

The "spacer div" hack has been discussed in many places, including this article on A List Apart.

My CSS2 implementation of clear-after is based on a suggestion by Lydia Lalopolis on the www-style mailing list. My version corrects the problem in Lydia's suggestion pointed out by David Baron.

An article on Mezzoblue discusses this problem and its solutions. Mr. Brownstone's comment points out that adding height: 100%; leads to correct results in IE6.

Please send any comments to mbrubeck@hmc.edu.