Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Flexing your HTML Layout Muscles with Flexbox


This article was originally published in CODE Magazine Issue 2016 Mar/April and has been updated on November 11th, 2017 with additional information


CSS hasn’t changed much since CSS 3 has been released, but the relatively new Flexbox module provides some much needed relief when it comes to creating complex structured layouts in HTML. Flexbox can be considered a much more flexible successor to HTML tables. In this article I’ll introduce the basic concepts of Flexbox and a few practical examples that demonstrate how to put Flexbox to use in your own HTML applications today.

Let’s face it – most of us doing Web development have struggled with multi-column layouts at sometime in our Web development. Any developer new to HTML coming from a desktop based development environment is very likely to comment immediately on the fact how difficult it is to properly create a structured layout in HTML. While it’s always been possible to make layouts behave using various tricks and specialized workarounds, the process has never been easy.

HTML has always been very good for document style layout – that is, creating flowing documents where content stacks on top of the previous content. HTML thrives on flowing content that takes up as much space as is available and reflowing content around embedded floating elements automatically.

On the other hand, creation of structured layouts that display content in confined areas that sit side by side or need to stack in an exact order and location have been much more elusive to achieve with HTML. Trying to create paneled layouts that can stay fixed and/or dynamically resize relative to each other are not trivial to create in HTML.

Technologies to deal with structured layouts have come and gone over the years: Developers have used HTML frames, HTML tables, float based layouts, and more recently various CSS tricks and grid systems popularized by a number of CSS frameworks like Bootstrap, Foundation and Google’s Material Design. While there have always been solutions and hacks to beat HTML into submission, it has never been an easy road as each of these technologies has limitations and requires compromises. Even today, for many Web developers tables are the first and only tool they reach for when a structured layout is required.

It seems we’ve been struggling long enough with various hacks to create complex, structured HTML layouts.

Flexbox to the Rescue

But that’s finally changing. With the advent of the HTML Flexbox model, HTML gains a rich box formatting engine that addresses complex layouts. You can think of Flexbox as HTML tables on steroids, without the styling limitations of tables and the ability to use any HTML semantic tag to create a Flexbox container and child elements. You can apply Flexbox styling to any HTML element which means you can easily restyle and reflow elements independently of each other as there is no fixed container hierarchy as there is in tables.

Flexbox makes it much easier to lay out documents that require multiple columns or need to flow complex layouts around pages by providing many declarative attributes that describe exactly how each container and their children should size themselves, how content should align and wrap, how much and where spacing should be applied to fill space and so on. For example, it’s easy with Flexbox to create a full page layout that stay fixed to the height of the browser window and adjust all panels within the layout when the browser resizes. Likewise, it’s pretty easy to specify that content should be centered either horizontally and vertically or both. Content inside a container can be made to stack on top of the previous element or to flow side by side and you can easily control the wrapping behavior, which makes it much easier to create responsive mobile friendly layouts that can adjust according to screen size.

Much of this was possible before with HTML without Flexbox, but it required a lot of disjointed CSS attributes to find the right invocations. Flexbox creates a new, comprehensive model using a few clearly defined attributes specific to layout tasks that can interact with each other more easily than the old HTML attributes.

Flexbox History

As we are building more and more interactive applications inside of the browser, it is increasingly important to control layout more easily to enable the complex interfaces we now build with HTML. User interface design still is one of the most time intensive operations in Web application development, and anything that makes this process easier is a welcome change. Flexbox provides some much needed relief in this area as it facilitates creating complex layouts more easily using standard semantic HTML element tags.

The HTML Flexbox model is not exactly new. The initial, now outdated spec for Flexbox was released in 2009 followed by another now outdated update and was finally was replaced in 2012 by the current Flexbox specification that is now available in most browsers. It’s still a working draft in the final recommendation stage (see sidebar), but the draft has been fairly stable for a few years now. All evergreen browsers and most reasonably recent mobile browsers now support Flexbox. The big glaring exception is Internet Explorer 9 and older which have no support and Internet Explorer 10 which has partial (but reasonably good and usable) support for Flexbox.

Although decent Flexbox browser support has been available since 2014, it seems that Flexbox really hasn’t hit the developer mainstream yet. It still feels like bleeding edge technology that hasn’t garnered wide adoption yet. This is because it’s relatively new and because Flexbox has a learning curve. The Flexbox CSS syntax is different and to use it you have to understand the basics of how the model and its relationships works. It’s not as simple as slapping a new attribute on a single HTML element and see behavior change. Flexbox deals with layouts and acts on many elements simultaneously and therefore requires some Flexbox styling on each participating element. It’s not rocket science, but there are quite a few CSS attributes to choose from. The terminology for some these attributes and values is somewhat overlapping, so it can be a little overwhelming at first to figure out which attributes to use. But as is often the case with new technology, you end up using a few features over and over again and the few attributes you need to master are easy to remember once you get the basic hang of how they relate to each other.

You’ll be hearing a lot more about Flexbox in the near future. Many of the updated big CSS frameworks coming out soon like Bootstrap 4.0 and Material Design from Google rely heavily on Flexbox to provide their layout features. It’s a good time to get up to speed on how Flexbox works now. Even if you’re not using Flexbox directly some of the frameworks you might use in the future will leverage Flexbox under the covers and understanding how it works will make it easier to interact with these frameworks.

You can think of Flexbox as HTML tables on steroids, without the limitations of table styling and the ability to use any semantic HTML tags.

Getting Started with Flexbox

Flexbox is a CSS based container layout module. One of the big benefits of Flexbox over HTML tables is its ability to use any HTML element to define its containers and elements. You can use div, nav, article or any other built-in or dynamic semantic HTML tag to define your containers and child items.

The key thing to understand about Flexbox is that it’s a container manipulation tool. It defines the overall behavior on the container and then allows element functionality to override some behavior traits of the elements that are under control of the container. Using a handful of flexbox specific attributes, you get a ton of control how the container alignment and flow is managed.

Figure 1 – Flexbox works through containership using a flex container and flex items. Items can control their own sizing and flow characteristics.

Figure 1 demonstrates the containership and some of the more common CSS attributes you’re going to see in a Flexbox layout. The container controls the overall flow of the layout, using attributes like flex-wrap, align-items and justify-content. The layout in Figure 1 is horizontal using flex-direction: row (along the main axis) but Flexbox also supports flex-direction: column layouts that formats a layout vertically (along the cross axis). Using these attributes, you can control many aspects including element sizing, wrapping and fill mode with very little CSS styling that clearly defines the container behavior.

Individual items can control how they fit into the layout and manage their own internal formatting using standard CSS styling. Child items primarily use the flex attribute to determine their Flexbox behavior. The flex attribute is a combination of the flex-grow, flex-shrink and flex-basis attributes. These three attributes determine whether the element can grow or shrink if the value is non-zero. If the value is non-zero and the element can grow or shrink and the value is used as a ration in relation to the other other elements in the container. The flex-basis determines the initial size or auto to use whatever the elements actual size is initially. Using Flexbox it’s trivial to create relatively sized elements that are recalculated on resizing and even as new elements are added to the container. Other item attributes include align-self which overrides the parent’s align-items setting and order which lets you explicitly control the location of an element in the container’s child collection. These are very useful if you programmatically insert and manage elements dynamically.

Flex containers can be nested so you can create a top level container that vertically manages elements, then use a child container to set up a horizontal flex container inside one of the child containers. A common example of this is a fixed window layout where you have a top level container that manages the full height of the browser window with header, content, footer. The content area can then have a horizontal Flexbox layout that contains multiple panes for a nav sidebar, content and ad sidebar. We’ll look at an example of this later in this article.

Unlike HTML tables, Flexbox can use any semantic HTML element for the container or child elements

The Flex Container

The flex container starts off the Flexbox hierarchy using display:flex attribute. This changes the display mode and tells the container and immediate child elements that they are using Flexbox layout formatting. The flex-direction attribute controls the horizontal (row) or vertical (column) flow of the child elements. The default is row and can be omitted for horizontal layouts. You can also specify the fill characteristics via the align-item and align-content which define the positioning via fill or spacing respectively, the flex-wrap attribute that controls wrapping behavior or elements. In combination these attributes give you a lot of control over how the container renders its child elements.

To demonstrate here’s a simple example of a full-width horizontal container using some very simple HTML:

<div class="panel-container">
    <nav class="panel-left">
        left
    </nav>

    <div class="splitter">
         splitter
    </div>

    <article class="panel-right">
        right
    </article>
</div>

To define a top level container you can do something like this (all but the first attribute are optional):

.panel-container {
    display: flex;
    flex-direction: row;
  
    justify-content: space-around;
    flex-wrap: nowrap;
    align-items: stretch;
}

Flex Elements

Once you have a container you can customize how the contained elements behave inside of that container primarily using the flex attribute.

To demonstrate I’m using three horizontal panels that have two fixed width panels and one auto-resizing panel:

.panel-left {
    flex: none;  /* manual resize */
    width: 300px;
}
.splitter {
    flex: none; /* manual resize */
    width: 18px;
}
.panel-right {
    flex: 1; /* resizable */
}

The most important attribute is the flex attribute is a shorthand for flex-grow, flex-shrink and flex-basis. The first two are numeric values that represent relative sizes of all the child elements. 0 means the size is fixed and shouldn’t shrink or grow respectively. Any number greater than 0 means the element can grow or shrink relative to the other elements’ grow and shrink values. The most common values are 0 0 auto which specifies to keep the size fixed, or 1 1 auto (which is also the default if not provided) which specifies the element will auto size itself to fill available space. When using auto for the flex-basis, the element uses the existing size as its basis, so if you have a fixed width for the element that is used as the base value.

If you want to express a relationship without explicit width (or height) you can also use relative numbers that establish a ratio relationship between the elements:

.panel-left {
    flex: none;  /* manual resize */
    width: 300px;
}
.splitter {
    flex: none; /* manual resize */
    width: 18px;
}
.panel-right {
    flex: 1; /* resizable */
}

This makes the left panel (2) half the size of the right panel (4), and the splitter panel half the size of the left panel (1). As you resize the container that relationship stays intact. This is similar to using percentages in table layouts except that Flexbox can recalculate the ratios even if new elements are added to the layout. Figure 2 shows how this relationship renders in the browser when another .splitter and .panel-right elements are added to the initial layout. You can check it out online at: http://codepen.io/rstrahl/pen/adJPeQ.

Figure 2 – Using relative sizing in the flex property keeps elements sized relatively to each other as you resize the browser window and even if you add new elements dynamically.

Although relative sizing like this is very powerful, you can also use specifically sized layouts where some elements stay fixed and others automatically stretch or shrink to the accommodate the new size of the container. Using flex: 0 0 auto specifies that the element should stay fixed in size while flex: 1 1 auto will resize. This is very common for layouts that have a single expanding element or that are using things like splitters that let a user resize panels.

Vertical Layouts

One of the most difficult layout issues to deal with in HTML consistently, even in modern browsers, is to build a UI that automatically stretches to fill the entire browser window and stays properly sized as you resize the document. The age old problem with vertical sizing in the browser is that HTML has to know the actual size of the window from the top level container (body/html) all the way down into the child elements. Flexbox makes this process much easier.

To create a vertical layout you can use the flex-direction: column on the container, and then layer the elements to fill the height of the container. Using the exact same layout as in Figure 2 and simply switching the flex-direction:column results in a stacked layout displayed in Figure 3. You can also check out the Codepen at: http://codepen.io/rstrahl/pen/NxjxRW.

Figure 3 – Creating a vertical layout that fills a container is as simple as using flex-direction:column.

As in the horizontal layout, the vertical panels now fill the height of the container to its maximum size using the ratios specified by in the flex attribute.

To create full window height layouts that fill the entire browser window and resize to fill the entire content area, you have to ensure that the html,body elements as well as the top level flex container that wraps the entire page, have a height:100% set in their CSS:

html,body {
    height: 100%;
    overflow: hidden;
}
.flex-container {
    display: flex;
    flex-direction: column;
    height: 100%;          
    overflow: auto;          
}

This ensures the content of the items fills the entire window. Note the overflow:auto in the .flex-container, which works around an odd gotcha I ran into with FireFox. FireFox requires overflow:auto in order to keep the panels constrained to the panel height, even though overflow:auto should be the default setting. All other browsers seem to work without this. I’ve found in a few instances where explicit overflow settings are required so I’ve taken to being very explicit with my overflow settings in flex containers.

Vertical and Horizontal Content Centering

Another common difficulty in HTML is vertical centering of elements in a container. While there is a vertical-align property in CSS, this property applies only to table-cells and inline elements. It has no effect on any other container elements.

Flexbox provides a simple solution using align-items. There are several ways to express this:

.panel-right {
    display: flex;
    align-items: center;
}

This implies flex-direction:row as that’s the default. If all you’re after is centering this is the least CSS code you can use**.** This might be counter intuitive because the flex-direction is horizontal while align-items works on the vertical direction. This is exactly the behavior of align-items, which aligns items on the perpendicular axis.

Another slightly more verbose but maybe more obvious way to express this same vertical centering is to use justify-content which aligns content on the active flex-direction:

.panel-right {
    display: flex; 
    flex-direction: column;                    
    justify-content: center;          
}

Both have the same effect of vertical centering the content but it’s easier to see that centering is occurring on the column axis. To summarize: justify-content works on the current flex-direction axis, align-items works on the perpendicular/alternate axis. Hooray for bad naming conventions.

The reason both of these exist is so that you can combine them to center both vertically and horizontally. The following does just that:

.panel-right {
    display: flex; 
    flex-direction: column;                    
    justify-content: center;          
}

Applied to the .panel-right style in the previous example Figure 4 shows what the centered content looks like.

Figure 4 – Centering content inside of an element can be easily done with justify-content and align-items on any flex container.

Posted in HTML  

 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2018