Looking at normal flow, flex, and grid
CSS supports a number of different layout modes, and understanding which one to use in a given situation is a fundamental skill needed to use CSS effectively. It determines how child elements are positioned relative to their parent, and affects how layout-related properties like width and margin work.
Today, we're going to cover the most commonly used layouts - normal flow, flex, and grid. We'll also take a look at multi-column layout and float, as these mostly act like variants of normal flow.
Normal flow, multi-column and float are all layout modes which prioritise laying out text easily. They are seen most commonly in apps that involve users reading content, rather than interacting with it - like news articles, wiki pages, recipes, and social media posts. However, any app which includes at least a paragraph of text can take advantage of them. All three use the display property with a value of either block or inline (or inline-block, flow-root, or list-item, but we're not going to cover those).
Normal flow lays out content the same way that a word processor does - with lines of text organised into blocks (or paragraphs). The paragraph-like elements are known as block elements, and include < div >, < p >, < section >, < h1-6 > and any other element with display: block. The "text", on the other hand, consists of plain text content and elements with display: inline. Unsurprisingly, these elements are known as inline elements, and they're usually either text-formatting elements (like < strong > or < code >), or non-text content, (like < img > or < video >).
The block direction refers to the direction that block elements are placed on the page. For horizontally-written languages (like English, or Arabic) this is vertically down the page. For languages written vertically (like traditional Chinese), this is right to left across the page. However, languages which are traditionally written vertically on paper are usually written left-to-right on the web (see https://zh.wikipedia.org, https://www.nintendo.com/jp/index.html, https://www.joongang.co.kr/, etc), so, in practice, the block direction is almost always vertical.
The position of a block element in the block direction depends entirely on the size of its previous siblings, and the gaps between them (which are controlled by the margin-block or margin-top/margin-bottom properties). If the height of one block element changes, the positions of all the subsequent elements will be affected. This is the "flow" part of "normal flow".
The inline position of a block element is controlled by margin-inline-start or margin-left (margin-right for right-to-left-languages). A positive value will shift the element towards the end of the container, a negative value will shift it towards the start (and potentially out of the borders of the parent container). However, two block elements will never sit side-by-side, even if there's space for them to do so.
Fig. 1: The size and position of block elements can be adjusted, but they're always laid out one per row
Inline elements, on the other hand, are always laid out inside a block element, and they flow like words inside a paragraph - left-to-right or right-to-left across the page, wrapping to a new line as necessary. The direction they run in is referred to as the inline direction.
The mechanism that the browser uses to lay out inline content is called a linebox. Unfortunately, lineboxes aren't exposed in the dev tools, so we can't actually see one in the wild. But we can image them as tiny containers inside the block element, each holding a single row of text, as in Figure 2, below.
Fig. 2: The dotted lines represent lineboxes, which the inline elements are laid out inside of
The width of a linebox is always the same width as the parent, and the height is controlled by the line-height property, which can either be set on the parent element, or on any of the children. When it's set on a child, it only affects the specific linebox that child is laid out in. When the parent and child have conflicting values, the larger value is used, as shown in Figure 3, below.
Fig. 3: The parent has line-height: 60px, but the < span> containing the word "hat" has line-height: 80px, causing the first linebox to be larger than the rest.
It's a subtle distinction, but elements in a linebox are laid out relative to the linebox, not the parent. When elements are laid out relative to the parent (like block elements are), the vertical position can be adjusted using margins. But vertical margins (and paddings) have no effect on position inside a linebox, as shown in Figure 4, below.
Fig: 4: The "hat" < span> has padding: 20px. While this affects its horizontal position, relative to the previous word, it has no effect on its vertical position, with the padding simply overflowing the linebox.
Instead, the vertical position is controlled by the vertical-align property, which comes with a few gotchas.
There are also a couple of ways to jazz up a text layout, while maintaining the convenience of normal flow. The first is multi-column layout, which produces newspaper-style columns.
Fig. 5: Multi-column layout gives newspaper-style columns.
Multi-column layout is essentially normal flow, but with the lineboxes inside column containers inside the parent element. The child elements are still either block or inline, and follow the same rules as in normal flow.
Fig. 6: Lineboxes (indicated by white dotted lines) inside column containers (indicated by blue dashed lines)
To use multi-column layout, we need to add a columns declaration to a block element parent. The value of columns can be either
It's also possible to style the gap between the columns, and have elements span all of the columns.
.columns {
columns: 2 200px;
column-gap: 20px;
column-rule: 2px solid;
img {
column-span: all;
display: block;
width: 80%;
margin-inline: auto;
margin-block-start: 10px;
}
}
Fig. 7: Multicolumn layout with column...