The Flow layout
Prerequisite: knowledge from the Box model and Layout kata is necessary to understand the concept of outer/inner display types.
In this kata, we’re going to answer the following questions:(link to the complete list of questions)
The Flow layout is the basic default layout and is optimized for the primary role of a webpage (like this one): display lines of text content with eventual boxes (such as images) between paragraphs.
This may be the most important part of the dojo. The Flow layout is the default layout and thus applies almost everywhere in a web page. By learning it correctly, many CSS frustrations will disappear.
The Block Formatting Context (BFC)🔗
The Flow layout always happens in a Block Formatting Context. A BFC is created in certain conditions, including by:
- The root html element
- Flex and Grid items if they are neither Flex nor Grid nor Table containers themselves
- Elements with position: fixed or position: absolute
- Elements having the flow-root inner display type
- And many other cases that you can find on MDN
Inside a BFC, the browser will recursively crawl every nested element:
- If the element has a flow inner display type, the browser will flag the element and its children as participating in the flow layout. We also say that the elements are in the flow. It will then scan the inner display type of the children and continue.
- If the element have something else than the flow inner display type, the element itself will participate in the flow but its children will not be added to the flow and the browser will stop there.
Reminder: most elements will have a default display value that will make them participate in the flow.
- Elements such as <p>, <section> or <div> will have a default of display: block flow; (shorthand: display: block;)
- Elements such as <strong>, <em> or <span> will have a default of display: inline flow; (shorthand: display: inline;)
When every nested element is scanned, the browser ends up with a soup of boxes that participate in the flow and that will be used for the layout algorithm.
Inline and block boxes grouping🔗
Once the browser have determined which boxes will participate in the flow, it looks at their outer display type (which is either block or inline) and create block-level boxes and inline-level boxes accordingly.
One more definition: we call a block container box any box that will contain block-level boxes or inline-level boxes. (Yes I know that using block container box and block-level box is super confusing but those are the exact terms in the spec.)
There is only one rule with block container boxes: a block container boxes must contain only block-level boxes or only inline-level boxes. There can be no mixup between block-level boxes and inline-level boxes in a block container box
What if I have some text that is not wrapped in inline elements such as <span>? In order to have only inline-level boxes in the parent block container box, the browser creates anonymous inline-level boxes around unwrapped text. You can’t select them with CSS but you can still inspect them with the dev tools.
What if I mix up inline and block elements, such as <span> and <block>? In order to have no mixup of block-level boxes and inline-level boxes in the block container box, the browser creates anonymous block-level boxes around inline-level children. Again, you can’t select them with CSS and you can’t inspect them with the dev tools (but trust me, they are here).
If we try to sum up all of this, the browser takes all elements participating in the flow in a block formatting context, turns them into boxes (block-level and inline-level boxes), arrange them in block container boxes and introduces anonymous block-level and inline-level boxes accordingly.
What if I introduce a block element inside of an inline element?
Task: Put a block element inside the middle inline element and try to predict the result
Remember, we are dealing with boxes here. The Flow layout doesn’t care about DOM elements. It only cares about block-level boxes and inline-level boxes. The browser will see five boxes to group together:
- An inline-level box created by the first <span>
- An anonymous inline-level box containing the "boxes " text
- The block-level box created by the <div> element
- An anonymous inline-level box containing the " in a" text
- An inline-level box created by the last <span>
Thus, the <div> breaks the "no-mixup rule" and forces a block formatting context by introducing anonymous block-level boxes around the groups of inline-level boxes. The browser tries to reconcile other CSS properties in the most logical way possible. Here, you can see that there is a border on the bottom, left and top sides of the first anonymous inline-level box and the right border is on the last anonymous inline-level box.
Now, let’s say we have a paragraph with inline-level boxes. We also have a block element, for instance a button, that we want to inline in our text. How can we do this?
Task: Inline the "button" in the text without changing its shape by using the display property
Have you found it?
...
If you already know the answer with the shorthand, try to do it with the outer/inner display syntax and truly understand why it works.
...
Congratulations! You just reinvented display: inline-block;!
Why display: inline; doesn’t work: by setting inline, you are explicitly setting the outer display type to inline (which is what we want because we want to inline the button in its parent) but also implicitly setting the inner display type to flow. If the inner display type is flow, the children of our button will participate in the parent flow. The only child of our button is an unwrapped text, therefore an anonymous inline-level box, therefore our button will lose some of its block-level box properties that we want to keep.
By choosing flow-root, we force the creation of an independent Block Formatting Context. The BFC will introduce a root block container box that will honor block-level boxes properties.
Good! At this stage, you should have a good grasp of how block-level boxes and inline-level boxes are organized, and how to alter this organization. Don’t hesitate to let your brain cool off and think about it twice. I’m repeating myself but it probably is the most confusing concept about CSS, and it is really important to truly understand it.
We then move on to the final part of the layout algorithm. Right now, we have a boxes tree with block container boxes containing only block-level boxes or only inline-level boxes.
- Inside a block container box containing only block-level boxes, the browser will apply block formatting
- Inside a block container box containing only inline-level boxes, the browser will apply inline formatting
Block formatting🔗
When a block container box has only block-level boxes as children, block formatting applies:
- Boxes are laid out one after the other vertically
- The width and height properties are honored, by default the box will consume all the space in the inline direction (full width of the container) and be as tall as its content
- The margin property sets the vertical distance between two sibling boxes
- Top and bottom margins (not horizontal ones!) between two adjacent block-level boxes that have no content between them (border, inline element...) collapse: they combine in a margin whose size is the largest of the individual margins
Task: here, every margin collapses and every block is 10px vertically from its adjacent boxes. Try to break the margin collapsing in at least 3 different ways
Inline formatting🔗
When a block container box has only inline-level boxes as children, inline formatting applies. Let’s bring up the schema from the styling text kata to understand what is the line height and the baseline:
- Boxes are laid out one after the other horizontally in the writing direction (left to right in English, but could be right to left in other languages such as Arabic)
- If there is not enough space, the boxes break into a new line
- You can’t set width or height on inline boxes
- Margins work only in the inline direction
- The height of a line is defined by the tallest box in it
Task: Make the "first" line bigger by changing its font-size and the "second" by changing its line-height
- The vertical-align property sets how an inline-level box should behave in the vertical direction. Possible values are baseline (the default, will align the baselines of the boxes), top (the box will stick to the top of the line), bottom (same but at the bottom of the line), middle (centers vertically inside the line, which is not the same as baseline!), sub and super for exponents. Go ahead and try all those values:
- The word-break property controls how words are broken when the text wraps. Classic values are normal and break-all (can break after any character)
Task: Break the super duper long word!
- The white-space property controls how the browser handles white spaces in the generated HTML. By default, white spaces are combined and line breaks are ignored (so that you can indent HTML as you want without interfering with the content). You can change that by setting pre-wrap to preserve white spaces and line breaks or nowrap to prevent normal text wrapping.
Task: Make the empty line in the HTML paragraph appear
What I should remember🔗
Ooof! That was a hard one. Now you should have enough knowledge to truly understand and use the Flow layout!
- The Flow layout happens in a Block Formatting Context (BFC) created by the flow-root inner display type and many other cases that you can find on MDN.
- All nested boxes in a BFC that have the flow inner display type and their children will participate in the layout.
- Inside a BFC, the browser will group block-level and inline-level boxes so that there is no mix of block-level and inline-level boxes using anonymous block-level and anonymous inline-level boxes.
- Block (or inline) formatting will be used if all children of a block container box are block (or inline)-level boxes.
- The rules of block formatting are:
- Boxes lay out vertically and consume all the space in the inline direction
- Margins collapse between adjacent boxes that don’t have content between them
- The rules of inline formatting are:
- Boxes are laid out horizontally with their baselines aligned
- There are restrictions on width, height and margin
- You can control the flow of the text with vertical-align, word-break and white-space