CSS Flexible Flow Module

CSS Flexible Flow Module


This module defines the flow attribute and the flex unit. The two allow creating flexible layouts, which can fit in various view and content sizes.

The flow property and flex units address following problems that are not achievable in modern CSS:

  • vertical and horizontal alignment of elements inside containers and viewport;
  • flexible multicolumn layouts (sidebar-content-sidebar) when elements replaced horizontally in a row and have the same height.
  • complex position:static layouts when elements are replaced in visual order that is different from DOM order of elements.  

1. Overview

Flexible layouts are defined by using flex length units and the flow attribute. Flex Length Units (flex units) allow to define dimensions, margins, and paddings of elements as portions of free space available inside the containing block. Flex value is a decimal number, appended with ‘*‘ (asterisk symbol) as a unit identifier.

The flow attribute defines the layout method of contained blocks in normal (position:static) flow. In other words, flow defines the layout manager of the container. This module contains definitions of the following standard layout managers:

  1. vertical
  2. horizontal
  3. horizontal-flow
  4. vertical-flow
  5. templated layout

Flex unit values can be thought of as tension in spring coils, which shift dimensions or position of elements, according to their "strength". Flex values per se:

Markup of the layout above:

<div class="container">
  <p>... some text ...</p>

where p has the style:

  width: 40%;             /* fixed width - 40% of width of the container */
  margin-left: 2*;        /* left "spring" of power 2 */
  margin-right: 1*;       /* right "spring" of power 1 */
  border:1px solid black; /* border of fixed width */

2. Flex length units

Dimensions of elements, defined in flex units, are computed against free space available inside the containing box.

Computation of flex unit values is made after all non-flex values – the final step of the layout algorithm. It’s possible that some space inside the container will be left undistributed during this step. That space can be alloted among all flex values competing for free space in vertical or horizontal directions in the container.

Flex units are only applicable to padding, margin, width, and height CSS attributes of elements. They are also only determined for statically (normal flow), and absolutely positioned elements. Floated elements do not support flex units – attributes given in flex units shall be evaluated to auto value.

While computing the final dimensions of elements, a flex unit value is interpreted as a weight. If the sum of flex values competing for free space is less than 1*, a corresponding portion will remain undistributed. If the sum of flexes is greater than or equal to 1*, all free space will be allocated by using flex values as weights of proportional distribution.

For example, the following styles:

#container { width:300px; }
#element { width:1*;
           border:2px solid;
           padding:0; }

applied to the #element inside the #container will lead to following dimensions of the #element:

sum-of-flexes = 1* + 2* + 1* = 4*;
width-to-distribute = 300px - 2px - 2px; // container width minus fixed borders
width        = 1/4 * width-to-distribute = 74px;
margin-right = 1/4 * width-to-distribute = 74px;
margin-left  = 2/4 * width-to-distribute = 148px;

Flex unit computations respect all usual constraints. For example, min-width and max-width define boundaries where the width above can "flex". For flex unit computation purposes, the initial (default) value of  min-width attribute is being interpreted as having minimal intrinsic value.

2.1. min-intrinsic, max-intrinsic length values.

CSS properties min-width, max-width, width and min-height, max-height, height additionally may accept special values: 'min-content' and 'max-content'.

  • min-content value of some container is a minimal length that is needed to render the container without overflow in corresponding direction. E.g. for elements with overflow:auto it is a minimal width of the element to show it without scrollbars.
  • max-content value of some container is a minimal length needed to show the element without wrapping. In other words when width is max-intrinsic the height of the element is minimal in horizontal writing systems like rtl/ltr. In vertical writing systems (like ttb)  height:max-content will get minimal value of the height when width of the element becomes minimal.

width:min-content for paragraph <p>first second</p> will be equal to the width of widest word in the paragraph. width:max-content for the paragraph will be equal to the sum of all words and spaces in the paragraph – the paragraph will be rendered as a single line of text.

3. The flow property

The flow property defines how the container lays out its children. In other words, it establishes a layout manager for the container element.


Value:   default | horizontal | vertical | horizontal-flow | vertical-flow | "template" | grid | row(…) | stack |inherit
Initial:   default
Applies to: box elements with display model "blocks inside"
Inherited:   no
Percentages:   no
Media:   visual
Computed value:   specified value

This attribute defines the layout method of children in normal flow:

value of the flow Resulting display of children
vertical Stacked vertically.
horizontal Positioned horizontally in a single row. Direction is governed by the direction attribute, in particular by values ltr and rtl.
horizontal-flow Positioned horizontally, wrapping on multiple rows, if needed. Direction is governed by the direction attribute.
vertical-flow Positioned vertically, wrapping on multiple columns, if needed.
"template" Replaced according to the template. Binding of child position with the named cell in the template is made by using the float:"name" attribute.
row(tag1,tag2,...) Positioned in rows according to the row() template function.
stack Elements positioned on top of each other.

    Terms used in the document:

    flow container
    The flow container is the block element that has a ‘flow’ style attribute with a value other than default. Such an element uses the given layout manager to lay out its block children. If the flow container has children with display attribute values other than block, list-item or table they are wrapped into corresponding anonymous block or table containers in order to participate in the flow.
    flowed element
    The flowed element has the flow container as its immediate parent. Such an element is a subject of replacement by the layout manager of its flow container.

3.1. flow:vertical

Flow vertical is close to standard top-to-bottom layout of block elements, such as div, ul, and others. The only difference is in the use of flex units by flowed elements.

All static child elements of the flow:vertical container are replaced from top to bottom, one by one, forming a single column spanning the width of the container. Horizontal dimensions of the contained elements, defined in flex units, are computed against the width of the container. Similarly, vertical dimensions of contained elements are computed against the height of the container. If the container height is not defined, or defined as height:auto, there is no free space to distribute among flex values. Thus, the flex computation in that direction can be ommited.

If the container has its height defined, and that height is greater than min-intrinsic height of the contained elements, then there is a free space. In such a container, this space is distributed among the vertical flex dimensions of the contained elements.

For example, the following styles:

  #container { height:100%; border:1px dotted; }
  #first { margin-bottom:1*; }

when defined for the markup:

  <div id="container">
    <h2>Alignment to top/bottom</h2>
    <div id="first" style="margin-bottom:1*">
      <p>Shifts rest to the bottom</p>
    <div id="second">
      Normal div

will position the first element at the top of the #container, and the #second element at its bottom.

3.1.1. Margin collapsing in flow:vertical containers

Vertical margins of contained elements collapse as usual in CSS.  The only difference appears if one of the collapsing vertical margins has a flex value, and the corresponding margin of the other element has fixed value. In that case, that fixed value establishes a "min-constraint" for the flex computation of the margin between these two elements. Such a margin is flexible, but not less than the fixed value.

3.2. flow:horizontal

This is a single row layout. All static child elements of flow:horizontal container are replaced horizontally one by one, forming a single row. Layout is performed with respect to the direction attribute of the container.

In the horizontal direction, width, left and right margins, borders and paddings of contained elements, when given in flex units, participate in free space distribution. All immediate children of the flow:horizontal container are competing for free space available between the left and right sides of the container’s content box. Thus, this free space is distributed among them, proportionally to corresponding flex values.

In the vertical direction: flex values of height, top and bottom margins, borders and paddings of contained elements are computed against the height of the container. This makes it possible to align elements of the flow:horizontal container not only horizontally, but also vertically.

All children have the same height with this style:

#container       { flow:horizontal; border-spacing:4px; padding:4px; }
#container > div { margin:0; height:1*; }

The rendering will look like:

Here, all children have their intrinsic height. However, the top margin is set to 1*, shifting boxes to the bottom:

#container       { flow:horizontal; border-spacing:4px; padding:4px; }
#container > div { margin-top:1*; height:auto; }
                               /* that is "intrinsic" height */

3.2.1. Margin collapsing in flow:horizontal containers

Horizontal margins of contained block elements collapse in the same way vertical margins of flow:vertical containers do. With its in-flow children, margins of flow:horizontal container do not collapse.

3.2.2. Intrinsic dimensions

Intrinsic height of flow:horizontal container is the height of the margin box of the tallest ellement in the row. Intrinsic width of flow:horizontal container is a sum of intrinsic dimensions of the contained elements with respect to the horizontal margins collapsing.

3.3. flow:horizontal-flow

flow:horizontal-flow is a variation of flow:horizontal layout. It contains blocks which are allowed to wrap if there is not enough space in the horizontal direction inside the container.

The attribute clear:left|right|both allows to break the flow of elements into multiple rows explicitly.

The row will wrap if any of the base conditions are met:

  1. clear:left|right|both is used on the correspondent element;
  2. there is not enough horizontal space in the row to replace the element.

In the vertical direction, flex values of height, top and bottom margins, borders and paddings of contained elements, are computed against the height of the current row. The height of the row is equal to the height of the tallest element in the row, calculated without the influence of flex units.

In the horizontal direction, flex values of the elements in rows are computed exactly as in flow:horizontal.

For example, the following markup:

<div style="flow:horizontal-flow" >
  <div style="width:100px" > width:100px </div>
  <div style="width:1*"    > flexible width:1*
                             flexible width:1* </div>
  <div style="width:1*"    > flexible width:1*
   flexible width:1*
                             flexible width:1* </div>
  <div style="width:150px" > width:150px</div>

will be rendered as:

3.4. flow:vertical-flow

flow:vertical-flow is a multi-column layout that is similar to flow:horizontal-flow, but in the vertical direction. Elements are replaced from top to bottom. If there is not enough vertical space inside the container, the elements will wrap, forming as many columns as needed.

The attribute clear:left|right|both allows to break the flow of elements into multiple columns explicitly.

The column will wrap if any of the base conditions are met:

  1. the sum of flexes in the column in the vertical direction becomes greater than 1*
  2. clear:left|right|both is used on the corresponding element
  3. not enough vertical space is available in the column to replace next element

In the horizontal direction, flex values of width, left and right margins, and paddings of contained elements, are computed against the width of the current column. The width of the row is equal to the width of the widest element in the column, calculated without influence of flex units.

In the vertical direction, flex values of the elements in columns are computed exactly as in flow:vertical.

For example, in the following markup:

<ul style="flow:vertical-flow">
    <li style="height:150px" > 1. height:150px </li>
    <li style="height:100px" > 2. height:100px</li>
    <li style="height:0.3*"    > 3. flexible height:0.3*
                                    flexible height:0.3* </li>
    <li style="height:0.7*"    > 4. flexible height:0.7*
                                    flexible height:0.7*
                                    flexible height:0.7* </li>
    <li style="height:150px" > 5. height:150px</li>
    <li style="height:150px" > 6. height:150px</li>
    <li style="height:150px" > 7. height:150px</li>

each list item is styled as having width:150px. This will produce the following layout, where these list items are wrapped into three columns:

3.5. flow:"template" / flow: grid(…)

Note that this is simplified version of http://www.w3.org/TR/css3-layout/. All credits for the idea go to authors of that document.

flow: <template-expression> allows to replace elements according to the template expression.

Here, the template expression is a sequence of string literals. Each such string literal is a whitespace-separated list of name tokens, where each name designates a cell in the grid. Multiple cells may have the same name. In this case, the name defines a placeholder that spans multiple cells of the grid.

For example, the following template defines a 3×4 grid of cells with 6 placeholders named from "a" to "f". Some placeholders span multiple cells of the grid:

flow: "a a a"
      "b c e"
      "d c e"
      "d c f";

Each child of the element that has such a template defined can be bound with a particular named placeholder by using float:"placeholder-name" attribute:

li:nth-child(1) { float:"a"; }
li:nth-child(2) { float:"b";
                  height:max-intrinsic; }
li:nth-child(3) { float:"c";
                  width:*; height:*; } /* flexes, a.k.a. shrink-to-fit */
li:nth-child(4) { float:"d";
                  height:*; }
li:nth-child(5) { float:"e";
                  height:*; }
li:nth-child(6) { float:"f";
                  height:150px; }

Note about reuse of the float attribute: float:left|right attribute is not supported for immediate children of the flow container. In other words flowed elements can float (be replaced) only to designated cells defined by the flow:"template".

All non-bound children of the templated container are appended to the grid as if they span a single row in it. If there are multiple children with the same placeholder name, only the first element (in DOM order) will be bound with the placeholder. The rest of the elements will be replaced as unbound.

A placeholder name can only span rectangular, and unique, areas of cells in the template. Otherwise, the whole template is invalid, and the computed value of the flow shall be interpreted as flow:default.

For example, this markup:

    <li> "a", width:auto (that is 1*), height:auto(that is max-intrinsic) </li>
    <li> "b", width:150px, height:max-intrinsic </li>
    <li> "c", width:*, height:* (a.k.a. shrink-to-fit) </li>
    <li> "d", width:150px, height:* </li>
    <li> "e", width:150px, height:* </li>
    <li> "f", width:150, height:150px</li>

with styles defined above will be rendered as:


Instead of characters defining "names" of child elements the template may contain ordinal numbers of children of the templated container. So this template

flow: grid(1 1,
2 3);

will cause first three children of the container to be laid out in two rows where first element will span entire first row and second and third will be replaced in second row.

3.6. flow: row(tag1, tag2, …)

The flow:row() is used to replace elements in table alike layouts. The row() function contains list of tag names of elements that will be placed in single row and in corresponding columns ot the table.

Consider this markup:

    <dd>Description of the first item</dd>
<dt>Second</dt> <dd>Description of the second item</dd></dl>

and this style declaration:

dl { flow: row(dt,dd); }

It will be rendered as if items were placed in table like this:

First Description of first item
Second Description of second item

If the flow:row(…) element contains elements that do not match the row template then such elements will be placed in separate row spanning all available columns, so this markup:

    <dd>Description of the first item</dd>
<dt>Second</dt> <dd>Description of the second item</dd> <header>Another group</header> <dt>Third</dt> <dd>Description of the third item</dd>

will be rendered as:

First Description of the first item
Second Description of the second item
Another group
Third Description of the third item

The flow:row declaration may accept alternative lists of elements in columns. This declaration for example:

flow: row(label, input select textarea);

defines two columns layout with <label> elements placed in first column and all <input>,<select> and <textarea>  elements will go to second column.

3.7. flow: stack

The flow:stack layout is used to replace elements at arbitrary positions inside container. Rendering order is defined by DOM positions of elements and/or z-index CSS property.

In horizontal and vartical directions, flex values of width and height, margins, and paddings of contained elements, are computed against the width and height of the container. In flex calculations each child is treated as if it is the only child of the container – position of the child is not affected by other children positions.

Intrinsic dimensions of flow:stack container are equal to the width of widest and height of tallest child margin box in the container.

Consider this markup:

<section tab="first">
   <div>First tab</div>
   <div>Second tab</div>
and this style declaration:
section { flow: stack; width: max-content; }
section > div { size:*; /*spans whole container */
                visibility:hidden; }
section[tab=first] > div:nth-child(1) { visibility:visible; }
section[tab=second] > div:nth-child(2) { visibility:visible; }

By changing  tab attribute value on section element we can switch visibility of tabs.

In principle flow:stack layout can be approximated to some extent by using position:relative container with position:absolute children. But flow:stack intrinsic dimension calculation rule cannot be emulated by other CSS means.

4. The flow, float and block formatting context

Elements that are immediate children of elements with a non-default flow attribute value, establish new block formatting contexts exactly in the same way as cells in tables do.

5. The flow and position

The flowed element is positioned statically in its flow container. This means that the values position: absolute | fixed on such an element must be treated as position:static.

position:relative is allowed on flowed elemets. Therefore, the element can be offset from its static position by using left, right, bottom and top attributes.

5.1 position: absolute | fixed and the flex units

sample:position-fixedFlex units can be used as values of left, top, right and bottom attributes of elements having position:absolute or position:fixed defined. Combined with possible flex values of padding, margin, width, and height on these elements it makes possible to align such elements relative to their containing blocks.

Example: following style will place the element #light-box-dialog in the middle of the viewport:

  position: fixed;
  left:1*; top:1*; right:1*; bottom:1*;
  width: 400px;
  height: auto;

The #light-box-dialog element will have width 400px, height: auto (that is height:min-intrinsic) and will be placed in the middle of the viewport.

6. The flow and vertical-align

Flowed elements establish block formatting contexts. Thus, their vertical-align attribute defines the vertical alignment inside them, rather than vertical alignment of the block itself. In other words, vertical-align interpretation is the same as for table cells.

7. The flow and border-spacing

The border-spacing attribute defines the minimal value of vertical and horizontal margins between flowed elements. If a flowed element has its own defined margins, the used value for the margins is the maximum between the border-spacing, and the defined margin values. If the defined margins use a flex unit value, the flex computation uses fixed value of the border-spacing as a minimal constraint. In this case, the margin is computed using the flex algorithm, but it can’t be less than the value of the border-spacing attribute.

8. The flow and margin collapsing

Margin collapsing between flowed elements is defined for each layout manager above. Margins of flow container elements do not collapse with their in-flow children.

9. The flow and inline-block elements

Inline block elements are replaced inside corresponding line boxes. In principle, line boxes establish bounds where elements replaced in them can "flex".

Therefore, inline-block elements such as <img>, <input>, and <span style="display:inline-block"> can use flex units to define their dimensions, margins, and paddings. Calculations of flex units in the line box context are made against vertical and horizontal dimensions of the line box, and its non-flexible content.

In the horizontal direction, it’s possible that free horizontal space remains in the line box after replacing all non-flexible content (e.g. word boxes). That space gets distributed among elements having width, left, and right margins (or paddings), given in flex units.

In the vertical direction, the flex values of height, top and bottom margins, and paddings of inline-block elements, are computed against the height of the line box. For example this makes it possible to define multiple child elements with the heights equal to that of the line box.

If defined, the text-align:justify computation is performed after the flex value computation.

For example, the following markup:

    padding:4px; border:2px solid black; line-height:1.8em;
    display:inline-block; border: 2px solid salmon;
   First span:<span style="width:2*">width:2*</span>
   and second one:<span style="width:1*">width:1*</span>

will produce these layouts for various widths of the p element:

Note that on the last image last span has reached its min-intrinsic width thus its width is excluded from flex units computation.