← back to the section

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: none removes the element from layout entirely — neighbors collapse into its place. You can't animate its appearance (the property doesn't animate).
  • visibility: hidden leaves a "hole" of the right size — the layout doesn't jump. A curious detail: a descendant can "reappear" with its own visibility: 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 with pointer-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/grid enable the modern layouts for children — the next articles cover them.
  • 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.