Floats demystified

I remember learning about floats, and by "learning" I mean that I gradually fumbled through their use, often randomly inserting clearing elements throughout my documents to bludgeon them into shape, until one day I read some introductory material and it finally clicked for me. This is not, I think, an uncommon experience. You can do better, with a little help from the following chapter.

First, it's important to understand how the browser normally lays out its elements. Typically, we can think of an element as being either block-level, or inline. Inline elements are placed within page text, while block elements contain that text and are stacked vertically in a single column running down the page. Blocks default to full-width, while inline elements are sized horizontally by their content. You can think of this as the difference between div (block level) and span (inline), since those are our generic, non-semantic layout tags.

Pages with a single vertical column of content may be back in vogue due to mobile screens, but we still often need to place two or more elements next to each other, say for a pair of photos or a sidebar. One way to do that is to create a block element, then set its children to be inline-block. A lot of grid systems in the early 2000s worked this way, like YUI's grid classes. But using inline elements for positioning has some serious flaws:

To solve these problems, we turn to floats.

We all float down here

A floated element doesn't play by the standard rules. It keeps the same vertical position that it would have had, but it leaves normal document flow, meaning that it's ignored by other block level elements: effectively, they stack up behind it. Inline elements do know about floats, and will wrap around them. Floats also don't use the full width of the browser, but are sized to their content (or to any width and max-width styles that are set). If two or more floats are less than 100% of their container's width, they also line up side by side, instead of stacking.

This behavior—a kind of weird mix of inline alignment and absolute position—can be frustrating at first. For example, since floats don't count for layout, you may find that sidebars extend past the bottom of their section, leaving you with a broken layout. For example, in the illustration, the purple sidebar goes with the yellow section, but has escaped.

The intended solution to this is to "clear" the float, by adding the clear property matching the float direction. For example, if your sidebar is floated right, then adding clear: right to the footer causes it to notice right-floated elements (like your sidebar) for layout, dropping it underneath them. It's a way of saying "at this point, floats should influence the stack of document flow again." Here's our example, but with the following section set to clear floats:

Floated elements can be set to clear float as well, which causes them to stack instead of lining up horizontally. If two floated-and-clearing elements are close to each other, the first one will "push" the second one down the page, instead of causing a large blank space in the document flow (remember, neither of them technically effects layout for non-floated elements). When adding lots of sidebars, or combining sidebars with ads and other supplemental content, this behavior is exactly what you want.

However, there's another way to deal with floats that addresses a different problem: floats not only don't count for layout of external elements, they also don't adjust the size of their container. In the illustration above, the purple sidebar extends outside of the yellow container.

When your page is all white text on a black background, that's fine. But any time that something has a background, or visible borders (say, an embedded graphic), we want the container to adjust its size to include the float. When fixed, our page should look more like this:

There are two ways to make a container float-aware. The first, and most obvious, is to make the last item in the container an element that clears floats. Traditionally, this is usually done with a pseudo-element, so you don't need to actually write the markup. You've probably seen "clearfix" used as a class before:

/* put this on the container */
.clearfix::after {
  content: "";
  display: block;
  clear: both;
}

There's also a counter-intuitive trick that affects containers: their overflow property causes them to pay attention to floats. Specifically, if you set overflow: hidden on the container, it will extend to contain any floated elements inside. Of course, this also means that you can't take advantage of visible overflow, for scenarios like absolutely-positioned design elements. But if you have a common class that's used with floats, such as the sections of your article, setting overflow on the class that you're already using can help clean up markup.

Well, maybe not all of us.

Floats are a great tool for their intended purpose: creating variable-length boxes that can flexibly sit alongside—but not directly against—the main document content. And like inline-block elements, they have long been abused to create other layouts like grid systems. For years, they were the best option developers had, but they're still not great:

These days, if you want to set up a grid, you should be using flexbox. It's well-supported in browsers starting with IE10, which was released five years ago. It's specifically designed for this purpose. And it addresses all the problems of floated grids: responsiveness, matching dimensions, and intuitive sizing. They're like tables, but actually meant for layout!

Using flexbox is straightforward: the container is set as display: flex, which will act like a block from the outside. Inside, the child elements are distributed horizontally (by default) or vertically (using flex-direction: column on the container). Their size is controlled by the flex style, which has three parts: grow, shrink, and basis.

For any given flex container, the browser uses a three step process to decide the sizes of children. First, all of the basis values are added up and compared to the size of the container. Then, if the basis is smaller than the container, the remaining space is distributed to children proportionally to the grow value (meaning, a child with a grow of 2 will get 2 pixels for each pixel given to a child with a value of 1). If it's smaller, pixels are taken away using the same ratio.

Imagine a container that's 100 pixels across, with three children. The children's basis values add up to 75px, meaning that there's 25px left over. From that 25px, we can divide that by the total amount of grow value, and then distribute accordingly. Here's how that works out:

Where this gets really interesting is when you combine it with flex-wrap, which will deal with overflow by creating a new row. It's great to create grid systems with variable numbers of rows, because the row isn't defined in markup, it's just created via the natural sizing of flex. This CSS will create a grid that's 5 across on big screens, 3 across on tablets, and stacked on mobile (by simply disabling flex):

.grid-container {
  display: flex;
  flex-wrap: wrap;
}

/* default to 5 across */
.grid-item {
  /* no grow or shrink, just 20%  wrapped */
  flex: 0 0 20%;
}

/* fewer row items on tablet */
@media (max-width: 800px) {
  .grid-item {
    flex: 0 0 33%;
  }
}

/* turn off flex on mobile */
@media (max-width: 480px) {
  .grid-container {
    display: block;
  }
}

Within a flex row, you can also organize your elements vertically and horizontally, using the "main" and "cross" axis. The main axis is horizontal for a row, vertical for a column, and is controlled with the justify-content property. The cross axis goes the other way, controlled by align-items. The easy way to remember this is that for flex rows (which is the default), justification is the same for items as it is for text.

justify-content is useful and important, but align-items is the real game-changer. If it's set to "stretch" for a container, elements will automatically have equal heights—no more ragged rows. And if it's set to "center" it will actually center row items along the row's central axis. That's right: vertical centering is now finally quick, easy, and reliable.

If flexbox did nothing else, this alone would make it worthwhile. But as we'll see in the next chapter, you can actually use flexbox to create easy, simple stacked bar and column charts.