Display, Normal Flow and Formatting Contexts
IntermediateBefore flexbox and grid there is normal flow, and every layout question starts with which formatting context an element is in. This is the model that makes the rest predictable.
Overview
A browser lays out a page top to bottom in normal flow: block boxes stack, inline boxes sit in a line. Setting display on a container replaces the rules its children follow — that is what a formatting context is. Most confusion about why a property "does nothing" is really a property that does not apply in the current context: width on an inline element, or align-items outside a flex or grid container. Getting this model straight first is what makes flexbox and grid feel like tools rather than incantations.
Block, Inline and Inline-Block
The default behaviours, and which properties each ignores.
/* block — new line, fills the width, respects every box property */
div, p, h1, section, ul, li
/* inline — flows in the line, and IGNORES width, height and
vertical margins. Horizontal padding works but does not push
siblings apart vertically. */
span, a, strong, em, code
span { width: 200px } /* has no effect */
/* inline-block — flows in the line, but accepts every box property */
.chip { display: inline-block; width: 6rem; padding: .5rem }
/* The mysterious gap between inline-block elements is the
WHITESPACE in your HTML being rendered as a space character.
Use flex with gap instead of fighting it. */
/* Two special values worth knowing */
display: none /* removed from the layout AND from the
accessibility tree — screen readers skip it */
visibility: hidden /* invisible but still occupies its space */
.sr-only /* visually hidden but STILL announced —
the pattern for screen-reader-only text */
/* display: contents removes the box but keeps the children, useful
for making a wrapper disappear from a grid — but it also removed
semantics in older browsers, so avoid it on lists and tables. */Formatting Contexts
Setting display on a parent changes the rules its children obey.
/* The parent's display decides the children's layout model */
.parent { display: block } /* children stack in normal flow */
.parent { display: flex } /* children become flex items — one axis */
.parent { display: grid } /* children become grid items — two axes */
/* This is why these do nothing outside the right context: */
.item { align-self: center } /* needs a flex or grid parent */
.item { grid-column: 1 / 3 } /* needs a grid parent */
.item { flex: 1 } /* needs a flex parent */
/* A BLOCK FORMATTING CONTEXT is an independent layout region.
Margins do not collapse through it and floats are contained.
These create one: */
display: flow-root /* the explicit, side-effect-free way */
display: flex | grid
overflow: hidden | auto /* the historical trick — but it also
clips content and breaks sticky */
position: absolute | fixed
contain: layout
/* So the classic "parent has zero height because its children are
floated" is fixed with: */
.parent { display: flow-root }Logical Properties
Direction-aware equivalents that work in right-to-left languages without a second stylesheet.
/* Physical Logical */
margin-left -> margin-inline-start
margin-right -> margin-inline-end
margin-top -> margin-block-start
padding-left/right -> padding-inline
width -> inline-size
height -> block-size
text-align: left -> text-align: start
/* Shorthands that set both sides */
margin-inline: auto /* the modern horizontal centring */
padding-block: 1rem /* top and bottom */
margin-block-start: 2rem
/* In an RTL language (Arabic, Urdu, Hebrew) 'inline-start' becomes
the RIGHT side automatically. A layout built in logical properties
mirrors with one attribute: */
<html dir="rtl">
/* This matters directly for an Indian product with Urdu content, and
it costs nothing to adopt — the names are the only difference. */
/* Centring, for reference — the three that come up constantly */
.a { margin-inline: auto; max-width: 60ch } /* horizontal block */
.b { display: grid; place-items: center } /* both axes */
.c { display: flex; justify-content: center; align-items: center }Key Points to Remember
- 1Inline elements ignore width, height and vertical margins; inline-block accepts them but inherits whitespace gaps
- 2display: none removes an element from the accessibility tree, while .sr-only hides it visually but keeps it announced
- 3A parent's display value decides which layout model its children follow, which is why flex and grid properties do nothing elsewhere
- 4display: flow-root creates a block formatting context without the clipping side effects of overflow: hidden
- 5Logical properties (margin-inline, block-size) mirror automatically for right-to-left languages
Interview Questions
Sign in to ask AriaWhy does setting width on a <span> have no effect?
What is a block formatting context and how do you create one deliberately?
What is the difference between display: none, visibility: hidden and a .sr-only class?
Ask Aria about Display, Normal Flow and Formatting Contexts
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.