Before any flexbox or grid, the page already lays itself out somehow: headings one under another, links in a row. This is normal flow — the default layout, and it is governed by the display property. Understanding flow matters even in the world of modern layouts: 90% of a page's content lives in it.
Block and inline
Two basic behaviors:
block(div,p,h1,section) — takes the full available width, the next element starts on a new line. Accepts width/height, all margins and padding.inline(span,a,strong) — flows inside a line of text, wraps word by word like text. Width/height are ignored, vertical margins don't work, vertical padding is painted but doesn't push lines apart.
The intermediate hybrid is inline-block: the element sits in the line, but inside behaves like a block — sizes and spacing work fully. Before the flexbox era this is how horizontal menus were built; today its main use is small "badges in text": labels, counters, buttons inside a paragraph.
Two pitfalls of the inline world. First: whitespace in the markup is significant — between adjacent inline-block elements a gap of "one space character" is visible, coming from the line break in the HTML (one of the reasons menus moved to flex with its honest gap). Second: an <img> image is an inline element and aligns to the text baseline, hence the mysterious 3-4 pixels below it; fixed with display: block for the image.
display can turn anything into anything: an <a> into a block (a fully clickable card), an <li> into a line. The HTML semantics don't change — only the layout. The flex and grid values of the same property enable the corresponding layouts for the element's children — they get their own articles.
Hiding an element: three dissimilar ways
.a { display: none; } /* the element is GONE: takes no space, descendants hidden */
.b { visibility: hidden; } /* the element is invisible but STILL TAKES SPACE */
.c { opacity: 0; } /* transparent, takes space, CAN BE CLICKED */
The difference is not cosmetic:
display: noneremoves the element from layout entirely — neighbors collapse into its place. You can't animate its appearance (the property doesn't animate).visibility: hiddenleaves a "hole" of the right size — the layout doesn't jump. A curious detail: a descendant can "reappear" with its ownvisibility: visible.opacity: 0— the element is fully interactive: it receives clicks and focus. An invisible clickable button is a ready-made trap (for users and for tests alike); transparency is combined withpointer-events: none.
For accessibility, all three mean different things: display: none and visibility: hidden hide the element from screen readers too, opacity: 0 does not. Hiding only visually while keeping it for screen readers is a separate pattern, "visually-hidden" (a class of several properties cutting the size down to 1px).
float: what it was and what it's for now
Before flexbox, columns were built out of floated elements: float: left pulls the element out of the flow and pins it to the left, and the following content wraps around it. Float-based columns required rituals — clear: both spacers and "clearfixes", because the parent stopped seeing the height of its floated children and collapsed.
This is worth knowing for two reasons. First — legacy: in an old project, float: left on half of the blocks means "this is a pre-modern layout", and fixing it point-by-point requires care (or migrating to flex entirely). Second — float has kept one legitimate job that nothing else does: genuine text wrapping.
.article img.illustration {
float: right;
margin: 0 0 8px 16px; /* text wraps around the image on the left and below */
}
Flexbox and grid lay out boxes, but they can't "flow text around an image" — that is still float (and the wrap shape can be set with shape-outside). The parent is saved from collapsing today by the already familiar display: flow-root — no clearfix hacks.
In short
- Normal flow: block — on a new line at full width; inline — flows in the text, ignores sizes; inline-block — in the line, but with sizes.
- The gap between inline-block elements and the space under an image are properties of the inline world, not bugs.
- Hiding:
display: none— not in the layout;visibility: hidden— a hole of the right size;opacity: 0— invisible but clickable (careful). - float is a historical layout; today its only job is wrapping text around images. A collapsed parent is fixed by
display: flow-root. display: flex/gridenable the modern layouts for children — the next articles cover them.
What to read next
- Positioning — the second way to pull an element out of the flow, this time a controlled one.
- Flexbox — the tool that now builds what used to be built with floats.
- The box model — the previous topic in the series.