CSS Flexbox

Flexbox is a CSS layout system for designing web pages. It is a comparatively new addition to the frontend world, only gaining widespread browser support in the last few years.

The most distinctive aspects of flex layouts are that elements are form-fitting, automatically resizing to take up available space. This attribute allows flexbox to gracefully solve common problems that developers face in an increasingly mobile-first world. As such, flexbox is an invaluable tool for any frontend developer.

In this article, we will cover:
  • The background and history of flexbox
  • Core flex CSS properties
  • Creating a responsive layout using only
    <div></div> s and flex

History of Flexbox

Block Layout (or, the Dark Ages)

Before flexbox, frontend developers largely relied on the block layout to style documents. Other layouts had more specific purposes: inline for text, table for tables, and positioned for elements attached to the page in a specific location.There are several problems which the block layout does not solve well. First, positioning elements was hard. The generally accepted solution for “sticking” elements to certain sides of the page relied on float, a fairly inflexible CSS property. As an example, imagine a standard two-column layout. One could make the left column float left, and the right column float right. However, what if three columns are needed? Because float directions are binary, we are left with an inelegant solution like “left-left-right”.

Secondly, sizing elements was hard. Block elements are sized to fit their content. To fit the page, a two-column layout has to explicitly style its children with rules like width: 50%. If a new column is added, all columns must be changed to be width: 33%. This can be made more concise and declarative with a rule like .parent > .column { width: 33% }, but this is merely a stopgap: the 33% manual calculation is still there. Despite these issues, the block layout was largely considered an adequate method for styling documents. However, everything changed with the advent of smartphones.

The Rise of Mobile

In the 2000s, mobile devices enjoyed a massive uptick in popularity. The world saw a proliferation of various screen sizes, the likes of which developers never had to support before. This presented a problem: the same website had to display correctly on an extremely diverse set of dimensions. Layouts had to not break given drastically different dimensions or even different orientations. Because existing block layouts relied so much on explicit height and width calculations, they were often brittle under changing screen sizes, causing a large amount of developer frustration.

The block layout was lacking; as such, developers turned to alternative tools to create responsive styles. Javascript was identified as a solution, with popular libraries like jQuery and Bootstrap dynamically resizing elements in response to changes in window size. While this granted the flexibility block layouts lacked, Javascript was not perfect: it had its own set of issues. Dynamic resizing required querying the DOM and manually setting attributes, which incurred a performance penalty. In addition, because CSS is handled by the browser itself, layout interactions are optimized and can tap into the device’s GPU for higher performance. Finally, Javascript tends to be slower than native browser code. These issues led to the common complaint of choppy resizing.

Javascript Animation: Then and Now

It’s worth noting that Javascript has been catching up quickly, with many animation-optimized libraries matching or even exceeding CSS performance (Velocity.js is often touted as being virtually indistinguishable from WebGL). However, for general purpose layout-related work, it’s safe to assume that CSS will always be faster than JS.

Enter Flexbox

Flexbox was therefore introduced as a solution that was more dynamic than block layouts, yet still entirely CSS. Because flex elements grow to fit their container, flex-based layouts enjoy several attractive properties:

  • Efficient arrangement and positioning of elements
  • Even distribution and formatting of content across containers
  • Easy handling of “sticky” elements such as footers
  • Decoupling of content and layout

It’s worth noting that all of the above can be achieved with block layouts. However, in addition to providing these properties, flexbox provides them with minimal configuration. This means that flex layouts can gracefully transition between different screen sizes, with very little work needed from the developer.

Imperative vs. declarative CSS

The distinction between imperative and declarative code is common across all aspects of programming. Imperative code explicitly instructs a program how to do a task, while declarative code describes what the outcome should be, and delegates the task of “figuring out how” to the program itself. Block layouts that rely on floats and clears often require the developer to manually calculate dimensions. For example, one way to achieve a “sticky footer” with block layouts is to set the body height to be the height of the page minus the height of the footer (e.g. calc(100vh – footer height)). Thus, they are an example of an imperative way to code layouts, where the browser is explicitly instructed to assign a certain height to an element.

Because flex layouts automatically resize to fit available space, they require less configuration from the developer. For example, to achieve a sticky footer, a developer simply has to set the flex container to flex-direction: column and flex-grow: 1, and the footer element will ALWAYS be right at the bottom of the page. Declarative code tends to be more concise, readable, and portable. Imperative code can also be more fragile: because a height is manually set in the block example, if the footer height ever changes, the body height must be changed as well. This is an example of undesired coupling of content and layout: layouts should be resistant to changes in content.

What Flexbox Is Not

Before we proceed, several misconceptions about flexbox should be resolved. Given that flex is still relatively new, there remains some confusion about what flexbox is, exactly. This is exacerbated by the fact that several popular libraries are built on top of flexbox, with libraries such as Flexbox Grid and FlexboxLayout adding to the confusion.

Flexbox is NOT a library. It is a standard that browsers implement in their rendering engines. This means that NO installs are needed (unless you use an ancient version of Internet Explorer, in which case you need a polyfill).

Flex Syntax

As previously mentioned, flexbox provides attractive properties with very little configuration needed. Flexbox achieves all it does with only a handful of properties, making it incredibly easy to learn. Within the flexbox system, elements can be flex containers and/or flex items. An element is made a flex container by setting display: flex, and all children become flex items. (Note that a flex child can also be a flex container if its display type is also set to flex.) Without any further configuration, the main change we can observe is that flex items stretch to fill the height of their flex container, as opposed to fitting their content.

Note that this article will only cover the most basic flex properties, with which one can solve the majority of layout issues. For information on all available flex properties, see the MDN docs.

In the following examples, we will work off of the following HTML snippet:

HTML Snippet

Flex container properties

Flex container properties are used to arrange children. A flex container has two axes, the main axis and the cross axis. The main axis lies on the direction which the content flows, while the cross axis is the perpendicular axis. By default, the main axis is the horizontal axis, while the cross axis is the vertical axis. The three most commonly used flex container properties are flex-direction, justify-content, and align-items. Many common layouts can be implemented using just these three properties.

Flex-Direction

flex-direction sets which axis is the main axis, and which is the cross axis. The default is row, for which the horizontal axis is the main axis, and the vertical axis is the cross axis. When setting the value to column, the vertical axis becomes the main axis, and the horizontal becomes the cross axis.

Flex row
flex-direction: row (default)

Flex column
flex-direction: column

Justify-Content

justify-content distributes items along the main axis. When using a row layout, this horizontally aligns children, and when using a column layout, this vertically aligns them. The default is flex-start. flex-center and flex-end position children at the center and end, respectively, while space-around and space-between center the children and automatically assign whitespace to fill the container. If you are experimenting with these properties in the browser, try resizing the window. Note that when using space-around or space-between, content remains evenly spaced as you adjust the window size – no configuration needed!

Flex start
justify-content: flex-start (default)

Flex center
justify-content: flex-center

Flex end
justify-content: flex-end

Flex around
justify-content: space-around

Flex between
justify-content: space-between

Align-Items

align-items positions items along the cross axis. Older developers will be well aware of the frustrations involved in vertically centering elements; flexbox allows dead simple centering with a single property. While there are several possible values, we will focus on stretch, flex-start, center, and flex-end in this article for simplicity. Note that when setting align-items to anything other than stretch, flex items no longer grow to fit the container.

Stretch
align-items: stretch (default)

Flex start
align-items: flex-start

Flex center
align-items: flex-center

Flex end
align-items: flex-end

Flex Axes

While it is easy to think of align-items as horizontal and justify-content as vertical, this can present confusion when working with both flex rows and flex columns. Try to associate align-items with the cross axis and justify-content with the main axis, and flex-direction with whether the horizontal or vertical axis is the main axis.

Flex item properties

Flex item properties are used to position the item within its container. In this article, we will cover align-self, flex-grow, and flex-shrink. There are several more, but these cover the most common use cases.

Align-Self

align-self is used to align flex items on a case-by-case basis. It has the same possible values as align-items.

Align-self
First item flex-start, second item flex-end

Flex-Grow and Flex-Shrink

flex-grow and flex-shrink control the flex factor of a flex item. Simply put, it is a number that represents how much an element should grow or shrink proportionally to its sibling items. The initial values are 0 and 1, respectively. When flex-grow is set to 1 or greater, the flex item will grow on the main axis to fit its container.

Flex factor calculates the relative size of elements via ratios. If an element has flex-grow: 3 and its sibling has flex-grow: 1, the first element will be three times the width of the second. flex-shrink works the same way. flex-grow and flex-shrink interact with each other, and can sometimes lead to unintuitive results. They are fairly complex properties, and I encourage readers to experiment with them, because there are too many combinations of values to properly outline here.
The property flex can be used as a shorthand, and accepts three values, with the first representing the grow factor and the second representing the shrink factor. The third represents a property called flex-basis that represents a default size; we will not cover it in this article. Example: flex 1 0.

flex-grow
First item flex-grow: 3, second item flex-grow: 1

flex-shrink
First item flex-shrink: 3, second item flex-shrink: 1

Building Responsive Layouts with Flex

With the properties described above, we’re now ready to tackle creating responsive layouts. We will create a lightweight replication of the layout of meetup.com, which is a fairly standard web layout.

Throwback: Jezzball!

Before diving into page layouts, let’s take a trip down memory lane. Older readers will likely be familiar with Jezzball, an arcade-style game that was released for Windows 3.2.

Jezzball

The premise of Jezzball is that the player must build horizontal or vertical “walls” to subdivide levels into smaller and smaller rectangles, in order to segregate an ever-increasing number of bouncing red-and-white balls. As the available space decreases, the balls bounce more and more frenetically, and the player must carefully continue to build walls without making contact with the balls, which kills the player.

When visualizing web layouts, I like to think of Jezzball. Its concept of subdividing a rectangle into smaller and smaller rectangles is basically how designing basic layouts with HTML and CSS works, except less frantic and without the risk of death (frontend developers may dispute this last point).

Splitting the Page

Meetup’s frontpage features several sections: there is a header, a nav bar, several body sections, and a sticky footer (not visible in the screenshot).

Meetup.com

When thinking about layouts, try to mentally split the page up into separate sections, each with their own purpose. Taking inspiration from Jezzball, let’s build some walls to subdivide the page:

Meetup.com split

Great! Just by drawing three lines, we now have something to go off of. Let’s think about our strategy first, and describe what we know:

  • The page follows a standard vertical layout. Therefore, we want the flex container to have flex-direction: column.
  • For the most part, children have their contents centered both horizontally and vertically. We can achieve this by making the flex items also flex containers (keep in mind that text nodes are considered child DOM nodes). We avoid using align-items: center on the parent because then the child will no longer stretch to fit its parent.
  • The search bar is an oddly offset rectangle that doesn’t cleanly fit in any subdivision. This will likely need position: absolute.
  • The body is split horizontally into two columns, and each column follows a vertical content flow. Thus, the body flex container will be a row, and the children themselves will also be flex containers in column layout.

With this, let’s get started with our meetup competitor, meetdown.com.

Semantic HTML

In this example, we will mostly use <div/>s and inline styles to keep things simple (and also to show how powerful flexbox is with minimal code). In an actual site, semantic HTML tags such as <header/> and <section/> should be used, and styles should be applied via class names representative of the role of the styled element.

Scaffolding

Let’s start with an empty HTML snippet and add what we know we’ll need. As a shortcut, let’s define some shortcut class names (in a real app, your classnames should be defined based on content, not based on styling). The classes will be left out in subsequent snippets to save space, but I will give each of them recognizable names. Also note the border styling on <div/> for visibility.

 

Nav Bar

The first element we run into is the nav bar. We see that there is a logo on the left, and several links on the right. Developers used to the block layout may reach for float in this case. However, recall that we saw a property earlier for this exact use case: justify-content: space-between. This will make items in the flex container stick to the left and the right sides of the container.

Note that if we make all the items sibling elements, they will be evenly spaced across the page. Instead, we want only the logo to be on the left, while all the others are on the right. If we made all elements in the nav siblings, this would require the left child to be positioned differently than all other children. When making layout decisions, think of how to reduce exceptions as much as possible. Following our Jezzball instincts, let’s build a vertical wall right in the middle to make a left and right child, then build several more walls in the right child for each individual link.

 

Navigation

Not the prettiest, but it’ll do.

 

Header

The header content is centered both horizontally and vertically, and is just some simple text. We need one horizontal line to divide the header text and the description text, and several more vertical lines to divide the description text. I added some colors as well.

 

Header

Search Bar

Note the search bar is “floating” between the header and body. We can achieve this by setting the header to position: relative; and the search bar to position: absolute; with a negative bottom. We also add some padding to the header to space things out properly. The search box container has align-self: center; applied (keep in mind the container is a flex column), as well as a smaller width. The actual search bar we’re imitating is a bit more complex, but you get the idea.

 

Search Bar

Body

Our body is composed of two columns, each their own flex containers. Similar to the search bar, the body container has align-self: center; and a smaller width. Thus, we first use a vertical line to divide the two columns, then split each with horizontal lines. The left column is wider than the right column; we use flex-grow to assign a flex factor, making the left column twice as wide as the right. A brief examination of the children shows that they are just more of the same: the left column’s children are flex rows with time on the left and a flex column on the right, while the right column’s children are just some widgets. To save time, we’ll use placeholders.

 

Body

Footer

Almost done! All that’s left now is the sticky footer. Sticky footers have long been a thorn in the side of frontend developers. When content does not fill the page vertically, the footer should be attached to the bottom of the page. However, if there is more content than does fit, the page should scroll, with the footer still being at the bottom. Before flexbox, developers did all sorts of tricks to achieve a sticky footer. With flex, however, all we need to do is separate the content into one <div/> and apply flex-grow to it. Remember that when flex-grow is 1 or greater, the flex item it is assigned to will expand to fit its container along the main axis.

First, we apply height: 100% to <html/> and <body/> to make them take the full height of the window. We then nest everything we’ve done so far in a new <div/> that is a flex column with flex-grow: 1. Finally, we make our footer the same way we’ve made our other elements so far.

 

And we’re done! Here is the final layout, with the <div/> borders removed. Definitely a worthwhile competitor to meetup.com. When’s the IPO?

Footer

 

Final Code

 

Future of CSS Layouts

As we can see, flexbox is an immensely useful tool for web layouts. However, it may surprise you to know that flex was never intended to be the be-all end-all for layouts! Indeed, browser developers are currently hard at work implementing yet another layout system: the grid layout!

One issue with flexbox is that flex containers are either rows or columns. When attempting to style a two by two square of elements, alignment becomes difficult, since either the main or cross axes will need to be manually lined up with the other. In other words, flex works well for aligning items in a single dimension: horizontal or vertical. On the other hand, grid layout is designed to align items in two dimensions. This allows for a more rigid system of alignment more suitable for large-scale page layouts. However, current adoption is hampered by the relatively recency of widespread browser support: major browser vendors have only begun to officially support Grid in the last year. Today, developers commonly use browser polyfills to patch grid functionality into their websites. However, this has its own host of issues, such as increased bundle size.

In the future, it is likely that frontend developers will design layouts primarily using grid, with Flexbox being used mainly for styling within layout sections, such as form fields.

And as for the block layout? This isn’t Super Mario; let’s leave the blocks behind.

Be the first to comment

Leave a Reply

Your email address will not be published.


*