Flexible Box Model
The CSS3 flexible box model (or "flexbox", as it's commonly known) offers a handy way to control the horizontal and vertical arrangement of elements on a page. As the W3C description states:
In the flex layout model, the children of a flex container can be laid out in any direction, and can "flex" their sizes, either growing to fill unused space or shrinking to avoid overflowing the parent. Both horizontal and vertical alignment of the children can be easily manipulated. Nesting of these boxes (horizontal inside vertical, or vertical inside horizontal) can be used to build layouts in two dimensions.
Prior to CSS3, CSS defined four layout modes:
block layout - for laying out documents.
inline layout - for laying out text.
table layout - for laying out two-dimensional data in table fashion.
positioned layout - for laying out content in absolute terms, ignoring other elements in the document.
The flexbox layout mode is designed to address the shortcomings of these layout modes - the modes we've used for years to layout our pages - and to better address the challenges of modern web design - a way to present content visually that doesn't force us to exploit these other layout modes in ways for which they weren't originally conceived.
CSS's block layout is inherently designed for vertical organization of elements; inline is designed for horizontal layout. Flexbox is designed to be agnostic and, as the W3C's CSS Flexible Box Layout Module site states, "designed for laying out more complex applications and webpages".
An Evolving Standard
At the time of this writing, there is considerable (and justified) confusion about the "correct" CSS values for flexible box-model layouts. The use of vendor prefixes is recommended (and very much needed); support among browsers varies considerably. While the flexbox layout is becoming better and better supported by browsers, if you expect users with older browsers (even not-too-old browsers) as a significant percentage of your traffic, then using flex layout might not be a good idea.
Chris Coyer's excellent article "Old" Flexbox and "New Flexbox" details the evolution of the flexbox standard and browser support thereof: display:box and display:flexbox are outdated; display:flex is the current standard. We will use the newer (display:flex) syntax here, as that is the emerging consensus standard. But keep in mind that it is poorly supported as of now; Chrome is your best bet for the demos and exercises below.
See the caniuse.com site for the current state of browser support.
Flexbox layout is perhaps best demonstrated through a set of examples. Open ClassFiles/CssPageLayout/Demos/flex.html in a code editor and browser, and ClassFiles/CssPageLayout/Demos/flex.css in a code editor. The page presents a series of examples of flexbox layout; each example is a set of three <div>s illustrating some aspects and properties of the flexible box layout. Please note that, throughout our discussion here, for brevity, we will refer to the unprefixed syntax for the various properties; the example code includes the -webkit- prefix.
The markup for each example is very simple:
<div id="box1" class="box">
At the top of our CSS, we give each "item" box some padding, set its text color to white, align its text in the center, and add some rounding to its corners. We use nth-child() to set the color for the first, second, and third element of each example.
"Example 1" shows the three <div>s arranged in a simple row:
flex-flow: row wrap;
The display: flex statement indicates that we want to use flexible box layout for child elements.
The flex-flow property is shorthand for flex-direction (the first value) and flex-wrap (the second value). flex-direction specifies how child elements are arranged in the container, defining both the main axis (whether elements should be presented vertically or horizontally) and also the order in which items should be placed along that axis (in-order or reverse.) Valid values for flex-direction are:
- row (default)
In this first ("Example 1") example, we've used a value of row for flex-direction, so the three <div>s display horizontally. We'll experiment with other values in a bit.
The flex-wrap property defines whether children are forced into a single line (vertical or horizontal, depending on the flex-direction value). Valid values are:
- nowrap (default)
Because the content in our three <div>s is small, this property doesn't affect the layout for "Example 1"; we'll change this in a later example.
"Example 2" presents the same three <div>s, but this time we specify how the content should justify:
flex-flow: row-reverse wrap;
This second example specifies a row-based flex layout, just like the first example. But now we add justify-content, which defines how flex items align along the main axis of the flex container. In the absence of any individual flex items specifying how they take up space relative to their sibling flex items (which we'll see below), justify-content defines how unused space in the container will be allocated. Here's the list of possible values:
||Pack items toward the start of the container
||Pack items toward the end of the container
||Pack items toward the center of the container
||Unused space in the flex container is allocated evenly between the items, with the first and last items at the start and end of the container, respectively
||Unused space in the flex container is allocated evenly around the items
In the first example, in the absence of an explicit statement, #box1 had a default justify-content value of flex-start, meaning the elements were packed into the start (left) end of the container. In "Example 2," we set justify-content to space-between: each of the three <div>s takes up only as much space for itself as needed for its text content (plus padding), and the extra (light gray) space from #box2 is allocated evenly around each element. Try resizing the browser wider and narrower to see this change.
In "Example 3," we set flex-direction (the first value of flex-flow) to column-reverse; we also give a set height and width (300 and 100 pixels, respectively) to the container #box3. The result is that the three elements show in reverse order, with "item3" first and "item1" last.
We also set justify-content: flex-start. This has the result of packing the elements at the bottom of the container: since the "reverse" part of flex-flow: column-reverse wrap defines the "start" of the container as the end, justify-content: flex-start packs the elements at the bottom of #box3.
In "Example 4" and "Example 5," we give each of the three contained <div>s a bit more text content. We now see the effect of flex-wrap: wrap (the wrap value of flex-flow: row wrap): for "Example 4", at narrower browser widths the third item (and, for very narrow browser width, both second and third items) wraps to the next line. The screenshots below show "Example 4" in very narrow, less narrow, and wide browser widths:
"Example 5" looks exactly like "Example 4" when the browser is wide enough. When the browser narrows, however, the three contained elements in "Example 5" shrink in width, a result of the fact that we set #box5 to not wrap: flex-flow: row nowrap.
In "Example 6," we apply a style to each of the contained <div>s to allow it to specify its own order within the flow of the container: order:3, for instance, sets the "item 1" element to be third in the flow of elements within #box6; similarly, "item 2" is set as the first and "item 3" as the second. Values for order can be any positive or negative integer; the default is 0. Ordering is relative to the direction specified for the flow: reversing the order through a statement like flex-direction: row-reverse would mean that the element with the lowest order value would be on the right side of the container. The order property has no effect on elements that are not flex items.
Last, in "Example 7" and "Example 8," we demonstrate the use of the flex property on the contained elements. The flex property is a shorthand for setting flex-grow (the first value), flex-shrink (the second value), and flex-basis (the third value). In "Example 7," we set flex-grow to 8, 2, and 4 for each of the three contained elements, respectively. Coupled with the fact that we set flex-basis to 0 for each of the elements, the result is that the first element ("item 1 item 1 item 1") gets 8/14 of the total space, the second element gets 2/14 of the total space, and the third element gets 4/14 of the total space. To summarize: add up the total of all of the contained flex items' flex-grow values to get the denominator and allocate each element's space (width or height, depending on whether we are working with rows or columns) according to its value as the numerator.
The flex-basis value determines whether to allocate each flex element's width or height in the contained space absolutely or after determining each contained element's inherent width or height. A value of 0 means "allocate space absolutely"; as in "Example 7", we ignore how big or small each flex item is (per its content, padding, etc.) and set each item according to its flex-grow value.
In "Example 8," we set each flex item to have a flex-basis value of auto. This means that the browser will first give each flex item its own inherent width, then allocate the remaining space from the container around each element proportionally, per their flex-grow values. See the W3C specification for more information. Try changing the width of the browser to see the difference between "Example 7" and "Example 8".
We'll explore more of the flexible box layout concepts in the next exercise.