Here's how floats work in CSS
Tags: css, tutorial, beginners, webdev
Published: 2019-07-20 21:36:00

*Cover photo by J A N U P R A S A D via Unsplash.*

Does anyone else remember the old days of using floats for layout? We used to have to do our CSS uphill in the snow, barefoot, both ways. Whippersnappers these days with their flexbox and their grids...

In any case, floats are still useful for things that aren't large-scale layout. And even if you don't end up wanting to use them in your newest app, you may end up maintaining an old legacy application that uses a heavy-handed approach to floats. Having a basic knowledge of how they work can come in handy in a variety of situations.

Plus, floats are one of the big CSS fears. Applying a float and having everything on the page suddenly shift (and not knowing why) is something many of us have done. Let's get over that stumbling block today.

So, let's jump right in and examine the mystery of the float.

## The basics

{% codepen https://codepen.io/tchaflich/pen/orEodZ %}

This is what floats are best at: **Flowing text around other things.** Floats are the absolute best way to mimic print layouts when it comes to this.

What is happening is, very simply, this:

1. The floating element is placed where it would otherwise be
2. It's removed from the normal document flow and shoved as far left or right as it can go
3. Elements that come after it will arrange themselves *around* the floating element

#### Can I float center?

Nope. I know it would be cool, but it does not work.

I guess imagine how you would implement the text-and-cat-picture version of this. How would you go about breaking the text up neatly? There's no great way to do it. That's probably why it was never implemented.

#### Floats aren't in the normal document flow? So it's like "position: absolute"?

Not quite. Elements with `position: absolute` (or a similar property) are removed *entirely* from the document flow. An absolutely positioned element can often end up on top of or underneath other nearby elements, since they simply ignore each other.

When an element is floated, it's not exactly *in* the normal document flow, but it does interact with other elements; they are subject to special positioning rules, mostly that they will arrange themselves *around* it.

## Floats don't want to be large

A floating element that doesn't have an explicit size set will ["shrink to fit"](https://www.w3.org/TR/CSS2/visudet.html#float-width) its content - that is, it will only take up as much width as it needs to. If there's not much content inside of it, it won't be very large; if there's a lot, it gets bigger. You may have seen similar behavior before in `position: absolute` and `position: fixed` elements.

{% codepen https://codepen.io/tchaflich/pen/EqjXjY %}

## Predicting where floats will go

While you were playing around with the Jabberwocky example, did you happen to find something like this?

![Floats floating around in a rather confusing and distressing manner.](https://thepracticaldev.s3.amazonaws.com/i/ks6sfk9hbpeyfh5bdgfg.png)

One of the reasons people tend to avoid floats is that they can seem unpredictable. What happens if a series of floats of different heights overflows its line?

Let's take a look at the specification for [how floating elements are positioned](https://developer.mozilla.org/en-US/docs/Web/CSS/float#How_floated_elements_are_positioned):

> [...] when an element is floated, it is taken out of the normal flow of the document (though still remaining part of it). It is shifted to the left, or right, until it touches the edge of its containing box, *or another floated element*.

So when we have something that would fit within our width, all fine and dandy. When it doesn't, it goes downward and floats some more until it hits an edge that it would like to stop at - the edge of its parent container, or another float.

![An explanation, in GIF form, of the aforementioned distressing float ordering.](https://thepracticaldev.s3.amazonaws.com/i/w2faalo13deu3s8j5toa.gif)

#### Why didn't the last one move upward to fill in that terrible whitespace?

A float will never position itself above something that comes before it in the HTML order.

It will, however, try to "float" as high as it can, keeping that restriction in mind.

## Margins, the box model, and HTML order

In the basic example, were you bothered by this?

![A snipped image of the above pen, with the hard left edge highlighted.](https://thepracticaldev.s3.amazonaws.com/i/ig4wfeugarbm8wu37v2r.png)

What we need here is a *margin*. You have about a 1/3 chance of guessing which element to apply the margin to if you choose randomly. As much as it *feels* like random guessing is the essence of CSS, though, let's try to understand what the floats are doing here.

![A drawing on paper of the effect of adding margins to all three elements.](https://thepracticaldev.s3.amazonaws.com/i/lrut8ihyqubiq9ez45k0.png)


**\#1** - This is what happens if you add margin-left to the outer container `#container` - it just shoves *everything* to the right. This doesn't really fix the problem of the text coming to a hard edge with the floating box. Probably nobody has tried to fix floats this way, except at 3am, crying with desperation. (Who *hasn't* been there?)

**\#2** - This is something you might actually have tried - applying a `margin-left` to the text itself. The text's margin, however, is based on the text's "box" - where the text *would* be if the float wasn't there. (You can picture it as having a margin "under" the floating element.)

The text *itself* is still flowing around the float as it was before, but it has a margin left relative to its parent element. The margin property cannot add a "zigzag" margin to the text, and it must have a separation from its parent element represented by the margin, so the separation from the float just won't do.

**\#3** - The margin is added to the actual floating element, expanding its box. The text will flow around the *entire* floating box, margin included. So adding `margin-right` to our left-floating cats and `margin-left` to our right-floating cats will give the text a bit of space.

In summary - putting the margin **on the floating element** causes other stuff to flow around the combined float's box, including any margins.

The idea that we're getting at here is this - other stuff on the page moves around the float, rather than the float moving around other stuff on the page.

{% codepen https://codepen.io/tchaflich/pen/mZXKWy %}

We can also stack floats next to each other! Floats will sit nicely next to other floats, and the text will keep flowing around them all.

Just note that it's important where in the HTML order the floats are placed. **When in doubt, floats come first.** Pretend you're the browser, and you're reading the HTML "in order" - you'd want to know where your floating things are first, so you don't have to redraw all the stuff that has to flow around it afterward.

## Clearing floats

An element with the [`clear`](https://developer.mozilla.org/en-US/docs/Web/CSS/clear) property on it describes how that element will interact with nearby floating elements. You can put a clear property on both floating things *and* non-floating things.

{% codepen https://codepen.io/tchaflich/pen/RzQJgN %}

### The clear property means: I don't want any floats near me, in this direction!

Since it's rude to push other things around the page, the element that has the clear property on it will move downward if there's a conflicting float nearby. Think of a clearing element as being socially awkward and conflict-avoidant.

## Floats don't take up space the way you might expect

If you've used floats before, you might have found yourself in this position:

{% codepen https://codepen.io/tchaflich/pen/WqMyMO %}

The `#container` that holds all the floats (and *only* floats) has collapsed. This is because floating an element removes it from [normal document flow](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flow_Layout/In_Flow_and_Out_of_Flow), and the surrounding container does not create a new [block formatting context](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context).

Remember when I said above:

> It's removed from the normal document flow and shoved as far left or right as it can go

"Removed from document flow" has a meaning and technical consequences here!

Due to the rules that our CSS overlords have specified, the height of a container is determined by all of its contents that are "within normal document flow". However, we can also make the container be a "block formatting context" - kind of like a mini-layout within a page - to tell the container that it needs to treat itself as if it's a mini-document, in that it stretches to fit all its contents, even the out-of-flow ones.

We will need to do some convincing to get the container to stretch to fit our floats, but know that we have options.

{% codepen https://codepen.io/tchaflich/pen/wLyXjJ %}

A couple possible solutions:

### 1. Create an element that is in-flow, but after the floats

This is the `clearfix-type-one` solution in the pen above. It's also the "old school" clearfix.

We know now that the container will stretch to fit all elements within its flow, and floats are not in the flow. So we need a non-floating element that's placed *just* after our floating elements. We can do this using the `clear` property:

```html
<div id="container">
  <img class="floaty-left" src="https://placekitten.com/100/200">
  <img class="floaty-left" src="https://placekitten.com/100/100">
  <img class="floaty-left" src="https://placekitten.com/160/200">
  <img class="floaty-left" src="https://placekitten.com/100/120">
  <div style="clear: both;"> </div>
</div>
```

This will cause the container to stretch down to our cleared div. 

However, that element will have a bit of its own height (because it's supposed to have some text inside), so we can add something like `height: 0` to make sure it doesn't affect the height of the container while continuing to apply the clearfix. Over the years I've seen other ways to do this, such as using `max-height` and even `font-size: 0`. ¯\\\_(ツ)_/¯
 
Since this is only for display and isn't semantic, we can also move it to an `::after` [pseudo-element](https://developer.mozilla.org/en-US/docs/Web/CSS/::after) applied to the container.

That's how we end up with something like the following:

```scss
  // the "basic" clearfix with after
  &.clearfix-type-one {
    background-color: #644;
    &::after {
      content: " ";
      display: block;
      height: 0;
      clear: both;
      visibility: hidden;
    }
  }
```

This can be refined even further; there are many similar solutions online. For example, [Nicholas Gallagher's micro clearfix](http://nicolasgallagher.com/micro-clearfix-hack/) from 2011. Clearfix solutions of this type tend to have amazing cross-browser support since they've been around for nearly a decade. 

### 2. Use `overflow: hidden` to create a new block formatting context

*(Technically you can use any overflow value that isn't visible, but hidden is the one that tends to work best. Give me a second to explain...)*

This is the `clearfix-type-two` solution in the pen above. From the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context):

> A block formatting context contains everything inside of the element creating it.

Thus, forcing the parent container to form a new block formatting context will force it to expand, since it *must* fully contain all its children. And it's done with just one property, so **why isn't this the single best solution?** Why is it only number two of three on a list?

Well... you're using `overflow: hidden`. This isn't consequence-free; you have to deal with having a container that doesn't show anything at all outside of itself. What if we also want to have some rainbow drop shadows on some of our elements that would be clipped, or absolutely positioned glitter outside of our wrapper? It would be madness to get rid of those just to clear our floats.

{% codepen https://codepen.io/tchaflich/pen/YmzEXE %}

You can see what a travesty this would be.

And remember how I said earlier that you could use any overflow value that wasn't visible? Well, imagine how crappy that would look with `overflow: scroll` creating scrollbars on the sides. Big yikes.

### 3. Use `display: flow-root` to create a new block formatting context

This is the `clearfix-type-three` solution in the pen above.

Unlike the solutions above, this is *meant* to work on an inherent level, and isn't a "hack". From the [spec](https://drafts.csswg.org/css-display/#valdef-display-flow-root):

> The element generates a block container box, and lays out its contents using flow layout. It always establishes a new block formatting context for its contents.

So this basically does the same thing as `overflow: hidden`, but with no side effects. ✨

That sounds like exactly what we want! The reason it isn't used is [cross-browser support](https://caniuse.com/#feat=flow-root) - it's not currently supported on almost 30% of devices, with Safari and IE/Edge being the problem children as always.

# What else do you want to know about floats?

![A series of hot-air balloons "floating" (lmao) above a desert/plains landscape.](https://thepracticaldev.s3.amazonaws.com/i/00d71a4wiitq0vihc33d.jpg)
*Footer photo by Cristina Gottardi on Unsplash.*