Display and flow

Know what normal flow does, and what each display value changes about it.

05of 26
≈ 10Min read
8Parts

Normal flow

With no CSS at all, boxes are laid out in normal flow: block-level boxes stack down the page, each filling the width available; inline boxes sit in a line, wrapping when they run out of room.

Everything else in CSS — flex, grid, floats, positioning — is a way of stepping out of normal flow. Knowing what it does on its own is what makes the rest predictable.

block and inline

blockinline
WidthFills the containerAs wide as its content
Starts a new lineYesNo
width / heightRespectedIgnored
Vertical margin and paddingRespectedPadding draws but does not push; margin is ignored
Horizontal margin and paddingRespectedRespected
Examplesdiv, p, h1, section, lispan, a, strong, em, code
The one that surprises everyone

Setting height or margin-top on an <a> or a <span> does nothing. Its vertical padding will paint a background over the lines above and below without moving them. Change display first.

inline-block

inline-block sits in a line like an inline box, but keeps a block's box model: width, height and vertical margins all work. It was the standard answer for horizontal layout before flexbox, and it is still right for something that belongs inside a sentence but needs a size — a badge, a small button.

a tag inside a paragraph
.tag {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  background: #eee;
}

One quirk: inline-block elements are separated by whatever whitespace is between them in the HTML, because that whitespace is a real space character. That gap is why people once wrote their markup with no line breaks between tags. Flexbox has gap and no such problem.

none and hidden

  • display: none removes the element from layout and from assistive technology. Nothing is reserved.
  • The hidden attribute in HTML does the same thing, and expresses the intent in the markup where it belongs.
  • visibility: hidden keeps the space and hides the content. It is still skipped by screen readers.
  • opacity: 0 keeps the space and the element is still focusable and clickable — a classic accessibility trap.
  • To hide visually but keep it for a screen reader, use the visually-hidden pattern, not any of the above.
visually hidden, still announced
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

The display property is really two values

display: flex is shorthand for display: block flex — the outer value says how the box behaves in its parent, the inner value says how its children are laid out.

the two-value syntax makes it obvious
display: block flow;     /* = block */
display: inline flow;    /* = inline */
display: block flex;     /* = flex */
display: inline flex;    /* = inline-flex */
display: block grid;     /* = grid */
display: block flow-root;/* = flow-root */

That is why inline-flex exists: a flex container that itself sits in a line of text. The children do not know the difference.

flow-root

display: flow-root makes an element a block formatting context: it contains its children's floats, and margins inside it stop collapsing through it. It exists to do that one job cleanly, replacing the old overflow: hidden trick which also clipped things you wanted to see.

contents

display: contents removes the element's own box while keeping its children, which lets them become direct items of a grandparent grid or flex container. Use it carefully: in several browsers it removed the element from the accessibility tree as well, and although that is fixed for most elements, it is still not a tool to reach for on anything semantic.

the box disappears, the children stay
.passthrough {
  display: contents;
}

Changing display changes the game

A <li> is display: list-item. Set display: flex on it and the marker disappears. A <table> set to display: block loses every table behaviour. display does not just describe the element — it selects the layout algorithm, and the rest of your CSS is interpreted by that algorithm.

This is also why float, vertical-align and margin: auto behave differently, or not at all, depending on the display value of the parent.

Practice