CSS Flexbox Layout Module

Last updated:

(Note: This is a draft proposal to replace the current Flexbox module located at http://dev.w3.org/csswg/css3-flexbox. It has not been endorsed or officially reviewed by the CSSWG yet, and should not be taken as anything more than my own personal proposal.)

Introduction

CSS 2.1 defined two layout modes, algorithms which determine the size and position of boxes based on their relationships with their sibling and ancestor boxes: table layout, designed for laying out information in a tabular format, and static layout, designed for laying out documents and simple applications. This module introduces a new layout mode, flexbox layout, which is designed for laying out more complex applications and webpages.

Flexbox layout is superfically similar to static layout. It lacks many of the more complex text or document-formatting properties that can be used in static layout, such as float or columns, but in return it gains several additional properties for determining the size and placement of boxes that address common needs in complex applications and web pages. The children of a flexbox can be laid out in any direction and have their order swapped around, as well as "flexing" their sizes and positions to respond to the available space in the flexbox.

Creating a Flexbox

property : display-inside

new value : flex

You can declare that an element is a flexbox, and thus should use flexbox layout, by setting the display-inside property on the element to the value flex. Alternately, you may set the display property to the value flex, which is equivalent to declaring display-outside:block; display-inside:flex.

All children of a flexbox must be blocks (have display-outside:block). Any element children of a flexbox are treated as blocks (their display-outside must compute to block). Runs of text that are direct children of a flexbox and are not purely whitespace are wrapped in an anonymous block box.

Setting display-inside:flex on an element forces it to use a new layout algorithm, and so some properties that were designed with the assumption of "normal" flow don't make sense in a flexbox context. In particular:

  • the 'float' property always computes to 'none' on any child of a flexbox
  • all of the properties in the Multicol module compute to their initial values on a flexbox
  • [Others?]

Flexbox Direction

property : flex-begin

values : [[top | bottom] || [left | right]] | [[start | end] || [before | after]]

default value : start before

applies to : flexboxes

The flex-begin property determines where flexbox children are first placed, and how they are laid out as more are added. The value must either be a pair of perpendicular physical directions, a pair of perpendicular logical directions, or a single physical or logical direction. If the latter, then the provided direction is taken as the first direction of a pair, and the second value is the "start" or "before" direction, whichever would be perpendicular to the provided direction. That is, in documents with ltr text and top-to-bottom line stacking, specifying just "top" is the same as specifying "top left", and specifying just "left" is equivalent to specifying "left top", while in a document containing primarily Hebrew or another rtl language text, specifying "top" would be equivalent to specifying "top right".

The first value in flex-begin determines which content edge of the flexbox the flexbox's first child is placed against. Later children are placed along the same axis in order, packed as closely as their margins will allow. For example, if the first value was "left", then the first child is placed against the left edge of the flexbox's content area, and later children are placed to the right of that, with their left margins connecting with the right margins of the previous sibling. If the first value is "left" or "right", or a logical direction that computes to "left" or "right", the flexbox is said to be "horizontally oriented" or a "horizontal flexbox". Otherwise, it is "vertically oriented" or a "vertical flexbox".

The second value in flex-begin determines which content edge of the flexbox the flexbox's first line of children is placed against, if the flexbox has multiple lines. For example, if the second value was "top", the first line of flexbox children is placed against the top edge of the flexbox's content area, and later lines are placed below previous lines. If the second value was "bottom", though, then the first line is placed along the bottom edge of the flexbox's content area, and later lines are placed above previous lines. The second value of flex-begin has no effect if the flexbox has only a single line.

For example, a very basic flexbox can be marked up as:

<div style="display: flex; width: 400px; height:100px;">
  <div></div>
  <div style="width:2fl;"></div>
  <div></div>
</div>

A vertically-oriented flexbox can be used to emulate the functionality of HTML's <center> element:

<div>
  <span>foo foo foo foo</span>
  <span>bar bar<br>bar bar</span>
  <span>foo foo foo foo foo foo foo foo foo foo foo foo</span>
</div>
<style>
  div { display: flex; flex-begin: top; width: 200px; }
  span { margin: 0 1fl; }
</style>

Flexibility

property : width, height, padding, border-width, margin

new values : <flex>

applies to : All in-flow children of a flexbox; padding accepts flex values on flexboxes that aren't children of a flexbox

Each element within a flexbox may be made either flexible or inflexible. Flexible elements may grow when the containing box has extra space available after the size of all its children have been computed, and shrink if the size would cause the containing box to overflow, yet the box is still larger than its minimum width. Inflexible elements do not change in size, even when there is extra space left over in the flexbox.

The flex unit is written as a <number> followed by "fl". Negative flex values are not allowed, though they are valid from a syntax standpoint, and may appear in intermediate expressions (such as, for example, calc(5fl - 2fl)). If a flex value would be negative when it is actually used in a property (after resolving intermediate expressions like the previous example), it must instead be treated as 0fl.

The nature and amount of flexibility a box has is specified by using new flex units in the width, height, padding, border-width, or margin properties. A box is flexible when at least one of these properties on the box has a positive non-zero flex value used in its value. Flex units specify the relative degree of flexibility of a given length. Flex units may only be used on elements that are in-flow children of a flexbox, or in the padding of a flexbox itself if the flexbox is not a child of another flexbox. Using a flex unit in a property on an absolute-position or fixed-position element would make that property invalid, even if the element was a child of a flexbox, because it is not in-flow and does not participate in the distribution of free space to flexes.

In this example, flex units are used for the width, padding-left, and padding-right of the box. The padding-left and padding-right are 1fl each and the width is 2fl, so the width is twice as flexible and will take up twice as much room if there is free space, or will shrink twice as fast if the div is small enough that the span will overflow. In this case, the div is 200px wide, and since there are no other inflexible lengths specified in that direction, the entire 200px are "free space"; distributing that evenly between the flexes means that the span's padding-left and padding-right will both be 50px, while its width will be 100px.

<div style="display: flex; width: 200px;">
  <span style="padding: 0 1fl; width: 2fl;">
    foo
  </span>
</div>

The general procedure for determining how wide a flexible length should be is simple. First, determine the width and height of the flexbox, according to whatever normal rules would apply to the flexbox. For example, if the flexbox was a child of a block element and had a width of 'auto', the flexbox would fill its parent's width as normal. Second, subtract any inflexible lengths from the width and height to get the amount of free space. For example, if a flexbox was 200px wide, and had two children both with a padding-left and padding-right of 20px but a width of 1fl, then the amount of free space would be 200px - 20px - 20px - 20px - 20px, for a total of 120px of free space. Then, distribute the free space among the flexible lengths in proportion to their flexibility. In the previous example, the two children both have a width of 1fl, so the 120px of free space gets split evenly between them and they both end up with a width of 60px.

<div style="display: flex; width: 200px;">
  <div style="width: 1fl; padding: 20px;"></div>
  <div style="width: 1fl; padding: 20px;"></div>
</div>

Flexible lengths can have minimum and maximum size constraints placed on them, which limit how much they can flex. If a flexible length would be made smaller than its minimum size, or larger than its maximum size, it instead gets set to that size and stops being flexible; the amount of free space is recalculated with that length acting like an inflexible length and then redistributed among the remaining flexible lengths. By default, the minimum size of every flexible length is 0px, and the maximum size is infinite. The min-width, max-width, min-height, and max-height properties set the minimum and maximum sizes for width and height, respectively, of the elements they appear on. padding, border-width, and margin may all have minimum and maximum lengths specified by the min() and max() functions. Additionally, margins may acquire a minimum size constraint when they collapse together.

<div style="display: flex; width: 300px;">
  <div style="width: 1fl; max-width:60px; padding: 20px;"></div>
  <div style="width: 1fl; padding: 20px;"></div>
</div>

Flexible lengths may also have a preferred size. The preferred size is subtracted from the total amount of space when the amount of free space is being calculated, similar to a minimum size, and when the free space is distributed among the flexible lengths it is added to or subtracted from the preferred size of each flexible length. The default preferred size of a flexible length is 0, but the preferred size may be set with the calc() function. For example, "width: calc(20px + 1fl);" establishes the width of a box as being flexible, with a preferred width of 20px. If a flexbox was 200px wide with two children, one with a width of calc(30px + 1fl) and the other with a width of calc(10px + 3fl), then the amount of free space would be 200px - 30px - 10px, or 160px. That free space would be divided up with 1 part going to the first child and 3 parts to the second child, or 40px for the first child and 120px for the second. The final widths of each child are thus 30px+40px, or 70px, and 10px+120px, or 130px.

<div style="display: flex; width:200px;">
  <div style="width: calc(30px + 1fl);"></div>
  <div style="width: calc(10px + 3fl);"></div>
</div>

A more complex example can be had by starting with a 20px wide flexbox, and the same chidlren. In this case, there is no free space left at all; in fact, the two children overflow the flexbox by 20px (20px wide parent, and 10px+30px preferred widths for the children). To combat this the widths of the children would shrink in proportion to their flexibility: the first child would attempt to shrink by 5px and the second by 15px. This would make the second child drop to -5px wide, though, which is smaller than its minimum width of 0px. So the second child is instead set to 0px wide, and free space is recalculated. The parent is still 20px wide, and the remaining inflexible segments sum to 30px (this is just the preferred width of the first child). This still overflows the flexbox by 10px, so the first child's width shrinks by 10px, as it is the only flexible length left. In the end, the first child is 20px wide and the second child is 0px wide, which exactly fills the flexbox.

By default, the margins of flexbox children collapse together, just as in normal flow. Computing the size of a collapsed margin is made somewhat more complex by the presence of flexible lengths, however. Two inflexible margins collapse exactly as normal, with the combined margin being the greater of the original two margins. If one margin is inflexible and the other is flexible, the collapsed margin is a flexible length with a minimum size equal to the inflexible margin (unless the flexible margin had a larger minimum size already, in which case the minimum size doesn't change). If both margins are flexible, the collapsed margin is also flexible, with the flexibility, preferred size, minimum size, and maximum size all equal to the largest of each value in the two original margins. For example, if one margin is "calc(20px + 1fl)" (a length with a flexibility of 1 and a preferred size of 20px) and the other is "max(50px, 2fl)" (a length with a flexibility of 2 and a minimum size of 50px), the collapsed margin would have a flexibility of 2, a minimum size of 50px, and a preferred size of 20px.

The exact algorithm for determining free space and distributing it among flexible lengths is detailed later in this specification.

Multiple Lines

property : flex-wrap

values : wrap | nowrap

default value : nowrap

applies to : flexboxes

property : flex-break-before, flex-break-after

values : always | avoid | auto

default value : auto

applies to : children of a flexbox

The flex-wrap property determines whether flexbox children will automatically create a new line and wrap onto it when they would otherwise overflow the flexbox. A value of nowrap means that the flexbox children must not automatically wrap. A value of wrap means that, if the flexbox children would overflow the flexbox, instead the first child that would overflow the flexbox is placed on a new line, and the placement of additional children proceeds as normal from there on the new line, possibly creating additional new lines if necessary to keep the flexbox children from overflowing the flexbox.

The flex-break-before and flex-break-after properties determine whether individual flexbox children are placed on new lines or not. The flex-break-after of a child and the flex-break-before of its following display sibling together determine whether a line break is forced, allowed, or avoided between the two boxes.

If either value is avoid, then the boxes must always remain on the same line together, even if this would cause the elements to overflow the flexbox. Otherwise, if either value is always, then a line break must be created between the two elements, even if the flexbox has a flex-wrap value of nowrap. Otherwise, a line break may be placed between the two elements if necessary to prevent the line from overflowing the flexbox.

The exact algorithm for determining where line breaks are placed is detailed later in this specification.

Free Space Determination and Distribution

The following algorithm details precisely how to determine the dimensions of the flexbox and its children, and how to determine the size of flexible lengths used on the flexbox and its children.

Initial Computation

  1. Convert 'auto' widths and heights on flexbox children.

    1. If a flexbox child's width or height is 'auto' and all padding, border-width, and margin lengths in the same direction that are outside of the box that the width applies to are not flexible and not 'auto', then the width or height must be treated as "1fl" for the purposes of this algorithm. (By default, all of padding, border, and margin are outside of the box that the width applies to. If box-sizing is set to a non-default value, though, then some lengths may be inside of the relevant box. For example, if box-sizing is set of border-box, then padding and border lengths are inside the box that the width applies to, while margin lengths are outside.)
    2. Otherwise, if a flexbox child's width or height is 'auto', it must be treated as "fit-content" for the purposes of this algorithm.
    3. If any of a flexbox child's margins are 'auto', they must be treated as "1fl" for the purposes of this algorithm.
  2. Resolve all flexible lengths into (flexibility, minimum size, maximum size, preferred size) tuples.

    1. If a width, height, padding, border-width, or margin length does not use a flex unit in its value, the length is inflexible. Skip this step.
    2. Otherwise:
    3. If the length is a flexible value (a value with the fl unit), the flexibility is the magnitude of that value. If the length is a min() expression, the flexibility is the least of the flexible values in the expression. If the length is a max() expressions, the flexibility is the greatest of the flexible values in the expression. If the length is a calc() expression, the flexibility is the flexible part of the expression, after resolving to simplest terms.
    4. If the length is a max() expression with at least one of the values being non-flexible, the length's minimum size is the largest of the non-flexible values. If the length is a width or height, the minimum size is the greater of the value found in the previous sentence, if any, and the min-width or min-height property, as appropriate.
    5. If the length is a min() expression with at least one of the values being non-flexible, the length's maximum size is the smallest of the non-flexible values. If the length is a width or height, the minimum size is the lesser of the value found in the previous sentence, if any, and the max-width or max-height property, as appropriate.
    6. If the length is a calc() expression, the preferred size is the sum of all the non-flexible values in the calc() expression, after resolving to simplest terms.
  3. Collapse margins on flexbox children.

    1. If two adjacent flexbox children (modified by flex-index if applicable) do not both have collapsible margins, their margins must not collapse.
    2. Otherwise, collapse the margins of all adjacent flexbox children.
    3. If both children have inflexible margins, the collapsed margin must be inflexible and given a size equal to the greater of the two inflexible margins.
    4. If one child has a flexible margin and the other has an inflexible margin, the collapsed margin must be flexible with a flexibility equal to the flexibility of the flexible margin, a minimum size equal to the greater of the flexible margin's minimum size and the inflexible margin's size, and a maximum and preferred size both equal to the maximum and preferred sizes of the flexible margin.
    5. If both children have flexible margins, the collapsed margin must be flexible, with its flexibility, minimum size, maximum size, and preferred size all being equal to the greater of the flexibility, minimum size, maximum size, and preferred size of the two flexible margins.
  4. Determine the width and height of the flexbox according to the normal rules of the flow it is in. For the purposes of any calculations involved in this step, the preflex size[0] of all flexible lengths must be used. If the flexbox itself is not in a flow that allows flex units, then any flexible lengths used in the padding must be treated as the preflex size[0]. (If the flexbox is in a flow that allows flex units, then any flex units used in any properties of the flexbox are dealt with by that flow directly.)

  5. Determine line breaks. [[Define this.]] [[Maybe this should be part of the previous step? Line breaking affects the size of the element, particularly the *-content values.]]

[0] The preflex size of an inflexible length is merely the length itself. The preflex size of a flexible length is the greater of the flexible length's minimum and preferred sizes.

First Distribution Round

  1. Determine the width and height of the flexbox content. For a horizontal flexbox, the width of the flexbox content is the greatest width of any line in the flexbox. The height is the sum of the heights of the lines in the flexbox. For a vertical flexbox, the height of the flexbox content is the greatest height of any line in the flexbox. The width is the sum of the widths of the lines in the flexbox.

    1. The width of a line in a horizontal flexbox, or the height of a line in a vertical flexbox, is equal to the sum of all the relevant preflex horizontal or vertical (respectively) lengths[1] of all the flexbox children on that line.
    2. The height of a line in a horizontal flexbox, or the width of a line in a vertical flexbox, is equal to the largest value among the flexbox children on that line for the sum of the relevant preflex vertical or horizontal (respectively) lengths of that child.
  2. Distribute any horizontal free space according to the free space allocation algorithm with the total length being the width of the padding area of the flexbox, and the set of lengths being the horizontal padding of the flexbox and the width of the flexbox content.

  3. Distribute any vertical free space according to the free space allocation algorithm with the total length being the height of the padding area of the flexbox, and the set of lengths being the vertical padding of the flexbox and the height of the flexbox content.

[1] A length is "relevant" if it is a width or height, or it is a padding, border width, or margin outside of the sizing box specified by the box-sizing property on the element. For example, if box-sizing is padding-box for an element, then the padding lengths are not relevant, but width, height, border widths, and margins are.

Second Distribution Round

  1. Determine the width and height of each line of the flexbox. This is identical to the widths and heights determined in the first distribution round, except for the last line.

    1. In a horizontal flexbox, the height of the last line is equal to either the height of the line as determined in the first distribution round, or equal to the height of the flexbox's content area minus the heights of all the other lines, whichever is larger.
    2. In a vertical flexbox, the width of the last line is equal to either the width of the line as determined in the first distribution round, or equal to the width of the flexbox's content area minus the widths of all the other lines, whichever is larger.
  2. Distribute free space along each line according to the free space allocation algorithm. For a horizontal flexbox, the total length is the width of the line, and the set of lengths is all the relevant horizontal lengths of all children on that line. For a vertical flexbox, the total length is the height of the line, and the set of lengths is all the relevant vertical lengths of all children on that line.

  3. Distribute free space for each flexbox child in the opposite direction according to the free space allocation algorithm. For a horizontal flexbox, the total length is the height of the line the flexbox child appears on, and the set of lengths is all the relevent vertical lengths of that child. For a vertical flexbox, the total length is the width of the line the flexbox child appears on, and the set of lengths is all the relevant horizontal lengths of that child.

Third Distribution Round

This round is only relevant for flexbox children that had some flexible lengths that were deemed not 'relevant' in the prior distribution rounds, due to the element having a box-sizing value other than 'content-box'.

  1. For each flexbox child, if no flexible lengths were ignored as not relevant in the second distribution round, then skip this distribution round for that box.
  2. Otherwise, distribute free space for the remaining flexible lengths according to the free space allocation algorithm. The total length is the width or height of the sizing box of the element, as appropriate. The set of lengths is the intrinsic width or height or the element's content, as appropriate, the horizontal or vertical padding, as appropriate, of the element if the box-sizing value of the element is 'padding-box' or 'border-box', and the horizontal or vertical border widths, as appropriate, of the element if the box-sizing value of the element is 'border-box'.

Free Space Allocation Algorithm

This algorithm begins with a total length to fill in, and a set of flexible and inflexible lengths.

  1. Determine the amount of free space available. This must be equal to the total length minus the sum of all the preflex lengths in the set of lengths.

  2. If the amount of free space is positive, some flexible lengths must expand to fill that space.

    1. Determine the total amount of flex by summing the flexibility of all the flexible lengths.
    2. Divide the free space proportionally among every flexible length. The space allocated to every flexible length is equal to (total free space * flexibility of the length / total flexibility determined in the previous step).
    3. Add this free space to the preferred size of every flexible length.
    4. If any flexible length is now smaller than its minimum size, or larger than its maximum size, set the length to be equal to the minimum or maximum size, respectively, and set its flexibility to 0. Restart the free space allocation algorithm with the new values.
  3. If the amount of free space is negative, some flexible lengths must shrink to avoid overflowing the total length.

    1. Determine the total amount of flex by summing the flexibility of all the flexible lengths.
    2. Divide the necessary size reduction proportionally among every flexible length. The reduction allocated to every flexible length is equal to (total amount of overflow * flexibility of the length / total flexibility determined in the previous step).
    3. Subtract this size reduction from the preferred size of every flexible length.
    4. If any flexible length is now smaller than its minimum size (particularly, if any length is less than 0), or larger than its maximum size, set the length to be equal to the minimum or maximum size, respectively, and set its flexibility to 0. Restart the free space allocation algorithm with the new values.

(a limited set of Markdown is supported)