C.W.K.
Stream
Lesson 02 of 04 · published

display And Normal Flow: How Elements Arrange Themselves

~11 min · display, block, inline, flow, formatting-context

Level 0Markup Novice
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Normal flow is what the browser does when you don't tell it to do anything else. Most layout is just nudging it."

Normal Flow: The Default Behavior

If you put a stack of elements in a page with no positioning, no flex, no grid, the browser arranges them with normal flow:

  • Block-level elements stack vertically, each on its own line, taking the full width of their container.
  • Inline-level elements sit side by side in lines that flow left-to-right (or right-to-left for RTL languages), wrapping when they hit the container edge.
  • The vertical direction is the block axis; the horizontal direction is the inline axis. (These flip in vertical writing modes — Japanese, Mongolian — which is why they're named by axis, not direction.)

Every display value is a recipe that tweaks this default.

The Core display Values

  • block — block-level: new line, full container width. Default for <div>, <p>, <h1><h6>, <section>, <article>, <ul>, <li>, etc.
  • inline — sits in text flow, no new line, ignores width/height/vertical margins. Default for <span>, <a>, <em>, <strong>, <code>.
  • inline-block — sits in text flow (inline) but accepts width/height/all margins (block-like internals). Hybrid for inline buttons, badges, custom controls.
  • none — removed from the layout entirely. Not visible, not in flow, not focusable. Different from visibility: hidden, which hides the element but keeps its space.
  • flex — turns the element into a flex container; its children are arranged in one direction with alignment controls. (Track 5.)
  • grid — turns the element into a grid container; children placed in rows and columns. (Track 6.)
  • contents — the element itself disappears from the box tree but its children remain. Useful for unwrapping a semantic container without affecting layout.
  • list-item — block-level with a marker (default for <li>). Any element can be turned into a list item.
  • flow-root — block-level that establishes a new block formatting context (BFC). Used to clear floats inside without a clearfix hack, prevent margin collapse with children, contain floats.

Block Formatting Context (BFC)

A BFC is an isolated layout region where margins don't escape, floats are contained, and certain layout rules are bounded. New BFCs are created by:

  • display: flow-root (the modern, clean way)
  • Floats themselves
  • display: inline-block
  • display: flex or grid
  • overflow: hidden, auto, or scroll (other than visible)
  • position: absolute or fixed
  • contain: layout or paint or strict

When you find yourself adding overflow: hidden to stop floats from leaking out of a parent, the modern fix is display: flow-root — it creates the BFC without any side effects.

display: none vs visibility: hidden vs opacity: 0 vs hidden attribute

  • display: none — gone from the layout. Not visible, not focusable, no space taken, ARIA-hidden by default.
  • visibility: hidden — invisible but still takes up space. Not focusable.
  • opacity: 0 — visible (in the tree, in flow, focusable, clickable) but transparent. Use for animations only, never for hiding.
  • HTML hidden attribute — equivalent to display: none, but settable from HTML/JS without touching CSS.
  • aria-hidden="true" — hidden from screen readers but visible. Pair with one of the above only if the element should also be visually hidden.
Pick the hide that matches the intent. Hiding from everyone? display: none. Hiding visually but keeping screen reader access (a skip link)? Custom "visually-hidden" class with clip-path. Animating out? Don't use display: none — animate opacity and then set display: none on transitionend.

Visually Hidden — The Accessibility Standard

Sometimes you want content visible to screen readers but not visually rendered (skip links, hidden labels, button names that the design replaces with an icon). The community-standard pattern:

Inline Whitespace Gaps

If you have <span>A</span> <span>B</span>, the whitespace between them renders as a visible space because inline elements collapse whitespace into a single space. This is why inline-block buttons sometimes have mysterious gaps between them. Fixes: put them on the same line without whitespace, use font-size: 0 on the parent (and reset on children), or switch to flexbox where whitespace doesn't matter.

Pippa's Note

Pippa's WebUI uses display: flex and display: grid for every layout container. There's not a single float or clearfix hack in the codebase. The classic display: contents trick appears once — in the chat message list, where an <article> wraps two visually-disjoint groups but should not interrupt the flex layout's spacing. Modern display modes solve modern problems.

Code

Block, inline, inline-block in action·html
<!-- Demo: normal flow -->
<style>
  .block { background: #cdf; padding: 10px; margin: 5px 0; }
  .inline { background: #fdc; padding: 5px; }
  .ib { display: inline-block; background: #cfd; padding: 5px; width: 80px; }
</style>

<div class="block">Block 1 — full width, new line</div>
<div class="block">Block 2 — full width, new line</div>

<span class="inline">inline A</span>
<span class="inline">inline B</span>
<span class="inline">inline C</span>

<span class="ib">IB 1</span>
<span class="ib">IB 2</span>
<span class="ib">IB 3</span>
<!-- IBs respect width: 80px each; gap between them is
     the whitespace in the source — see the inline gap section -->
The visually-hidden CSS pattern·css
/* The visually-hidden pattern — content for screen readers only */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Use it like this: */
/* <a href="#main" class="visually-hidden focus-visible-show">
     Skip to main content
   </a> */

/* For icon-only buttons: */
/* <button>
     <svg aria-hidden="true">...</svg>
     <span class="visually-hidden">Close menu</span>
   </button> */
flow-root, contents — modern display tools·css
/* Modern BFC: flow-root */

/* Old clearfix hack — DON'T use anymore */
.cf::after {
  content: '';
  display: table;
  clear: both;
}

/* Modern equivalent — one declaration, no pseudo-element */
.contains-floats {
  display: flow-root;
}

/* Use overflow: hidden ONLY when you actually want overflow hidden */
.scroll-region {
  overflow: auto;       /* scrolls when content exceeds */
  max-height: 300px;
}

/* display: contents — the element vanishes from the box tree */
.unwrap {
  display: contents;    /* children become siblings of the parent's siblings */
}
/* Useful for: making a semantic <article> wrapper invisible to flexbox
   so its children participate directly in the parent's flex layout */

External links

Exercise

Build a navigation with five items. Render them three ways: (1) as default block elements (each on its own line), (2) as inline-block (sitting in a row, with the whitespace gap visible — measure it in DevTools), (3) as a flexbox row (gap controlled by gap, no whitespace issue). Then add a skip link using the visually-hidden pattern and confirm it appears only when focused.
Hint
DevTools → Layout panel shows you the boxes. For the visually-hidden skip link, give it position: absolute; clip-path: inset(50%); width: 1px; height: 1px; — and a :focus rule that makes it visible. Tab onto the page from the URL bar and the skip link should appear.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.