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:
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:
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> </body>
where p has the style:
p { 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 */ }
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*; margin-left:2*; margin-right: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.
CSS properties min-width, max-width, width and min-height, max-height, height additionally may accept special values: 'min-intrinsic'
and 'max-intrinsic'
.
min-intrinsic
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-intrinsic
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-intrinsic
will get minimal value of the height when width of the element becomes minimal.width:min-intrinsic
for paragraph <p>first second</p>
will be equal to the width of widest word in the paragraph. width:max-intrinsic
for the paragraph will be equal to the sum of all words and spaces in the paragraph - the paragraph will be rendered as single line of text.
fx()
function.The fx()
function allows to define flexible length with a base value. There are two forms of the fx function:
fx( <decimal> | <percent> )
- this form defines plain (a.k.a absolute) flex value. fx(1)
is an exact equivalent of 1*
and fx(50%)
is just another form of defining 0.5*
.fx( <decimal> | <percent>, <length> | <percent> )
- this form defines flexible length that is "flexing around" the base value.Example: these two children that use relative flexes:
<div style="flow:horizontal; width:440px"> <div style="width:fx(1,160px)" /> <div style="width:fx(1,80px)" /> </div>
will be rendered using following computed lengths:
To compute relative flex values we susbstract all base lengths from free space of the container and distribute only space that is left after that (a.k.a. post-base space). The post-base space can be as positive as negative. If post-base space is negative then final lengths will be less than their base values.
The fx()
function can be used in the same set of properties where flex units are acceptable.
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" | 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:
block
, list-item
or table
they are wrapped into corresponding anonymous block or table containers in order to participate in the flow.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*"> <code>margin-bottom:1*;</code> <p>Shifts rest to the bottom</p> </div> <div id="second"> Normal div </div> </div>
will position the first element at the top of the #container, and the #second element at its bottom.
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.
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 */
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.
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:
clear:left|right|both
is used on the correspondent 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> </div>
will be rendered as:
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:
clear:left|right|both
is used on the corresponding elementIn 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> </ul>
each list item is styled as having width:150px
. This will produce the following layout, where these list items are wrapped into three columns:
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 3x4 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"
; width:150px; height:max-intrinsic; } li:nth-child(3) {float:"c"
; width:*; height:*; } /* flexes, a.k.a. shrink-to-fit */ li:nth-child(4) {float:"d"
; width:150px; height:*; } li:nth-child(5) {float:"e"
; width:150px; height:*; } li:nth-child(6) {float:"f"
; width:150px; 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:
<ul> <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> </ul>
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: "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.
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:
<dl><dt>First</dt> <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:
<dl> <header>Group</header> <dt>First</dt> <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>
</dl>
will be rendered as:
Group | |
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.
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> </section>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.
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.
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.
Flex 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:
#light-box-dialog { 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.
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.
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.
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.
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:
<style> p { padding:4px; border:2px solid black; line-height:1.8em; } span { display:inline-block; border: 2px solid salmon; background:seashell; } </style> <p> First span:<span style="width:2*">width:2*</span> and second one:<span style="width:1*">width:1*</span> </p>
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.
spring-engine.h (C++ source) is an implementation of the algorithm used for computation of system of flex values.
The algorithm has clear physical interpretation:
It calculates final lengths of a set of coil springs attached one by one to each other. Each spring in the set has its own strength value (flex unit) and min/max limiters ( min/max constriants ).
Non-flexible element can be thought as element with no flexibility and the only min-constraint given. So the implementation can be used to compute distribution of systems of fixed and flexible elements.
Date: April, 5, 2009.
Updated on: April, 16, 2015.