CSS The display Property
The display property is the single most important layout property in CSS. It tells the browser what kind of box an element should generate and how that box should behave in relation to the boxes around it. Change an element’s display value and you fundamentally change how it participates in the page’s layout — whether it stacks vertically, sits inline with text, disappears entirely, or becomes the parent of a flex or grid layout.
Overview / How it works
Every element on a page generates one or more boxes when the browser builds the render tree, and display determines the type of that box. Historically, browsers gave you two basic behaviors: block boxes, which stack vertically and take up the full available width, and inline boxes, which flow horizontally with surrounding text and only take up as much width as their content needs. Modern CSS has expanded this considerably: flex and grid turn an element into a layout container that controls how its children are positioned, while values like none and contents remove the box (or part of it) from the rendering process entirely.
Conceptually, display actually controls two separate things, which is why the modern specification (CSS Display Module Level 3) allows a two-keyword syntax: an outer display type (how the element behaves in its parent’s layout — block or inline) and an inner display type (how the element lays out its own children — flow, flex, grid, etc). So display: flex is shorthand for display: block flex: the flex container itself behaves like a block box from the outside, but arranges its children using flexbox rules on the inside. You rarely need to write the two-value form explicitly, but understanding it explains why, for example, inline-flex exists — it’s simply display: inline flex, an inline-level box whose children are flex items.
Because display decides the box type before layout math even begins, it interacts with almost every other layout property. Properties like width, height, vertical-align, and top/bottom margins behave completely differently — or are ignored outright — depending on whether an element is inline, block, or something else. This is why display is usually the first decision you make when styling a new component.
Syntax
The general form is a property/value declaration inside a rule:
selector {
display: value;
}
- selector — any valid CSS selector targeting the element(s) whose box type you want to change.
- value — a single keyword (the common case) such as
block,inline,inline-block,flex,grid, ornone. Two-value forms likeblock flexare also valid but rarely written by hand.
| Value | Outer type | What it does |
|---|---|---|
block |
block | Stacks full-width, starts on a new line; width/height/margin all apply normally. |
inline |
inline | Flows within text; width/height are ignored, only horizontal margin/padding push content. |
inline-block |
inline | Flows inline like text, but accepts width/height and vertical margin like a block box. |
flex |
block | Element becomes a flex container; direct children become flex items. |
inline-flex |
inline | Same as flex, but the container itself sits inline with surrounding content. |
grid |
block | Element becomes a grid container; direct children become grid items. |
inline-grid |
inline | Same as grid, but the container sits inline. |
none |
— | No box is generated at all; the element and its descendants are removed from layout and accessibility tree. |
contents |
— | The element’s own box disappears, but its children render as if they were direct children of its parent. |
list-item |
block | Generates a block box plus a marker box, as used for <li> elements. |
table, table-row, table-cell, etc. |
varies | Makes an element behave like the corresponding HTML table part, without needing table markup. |
Examples
Example 1: block vs. inline vs. inline-block
.block-demo {
display: block;
background: #cce4f7;
width: 200px;
padding: 10px;
}
.inline-demo {
display: inline;
background: #ffdcae;
padding: 10px;
}
.inline-block-demo {
display: inline-block;
background: #cdf5cd;
width: 150px;
padding: 10px;
}
Result: Applied to three <span> elements in a row, the block-styled span drops onto its own line and stretches to a fixed 200px box regardless of neighbors. The inline-styled span stays glued to surrounding text — its declared padding pushes text left and right visually but does not reliably add vertical space, and any width/height on it would simply be ignored. The inline-block span sits inline next to text like the inline one, but because it accepts width and padding fully, it renders as a distinct 150px-wide green box with a normal padding box around it.
Example 2: display: none to hide a menu item
.nav {
display: flex;
gap: 1rem;
list-style: none;
padding: 0;
}
.nav__item--hidden {
display: none;
}
Result: The .nav list lays its <li> items out in a horizontal row with 1rem gaps between them, because it is now a flex container. Any list item that also carries the .nav__item--hidden class disappears completely — it takes up zero space, and the remaining nav items shift over to fill the gap, as if that list item were never in the markup.
Example 3: a realistic card layout combining grid, flex, and contents
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
}
.card-grid__wrapper {
display: contents;
}
.card {
display: flex;
flex-direction: column;
padding: 1rem;
border: 1px solid #ddd;
border-radius: 8px;
}
Result: The outer .card-grid element arranges its children into responsive columns that are each at least 200px wide, wrapping to more rows as the viewport narrows. If the individual cards happen to be wrapped in an extra grouping element with class card-grid__wrapper (say, for a templating reason), setting that wrapper to display: contents makes it vanish from the box tree — its .card children become direct grid items of .card-grid instead of being trapped one level too deep, so the grid columns line up correctly. Each individual .card is itself a flex container, stacking its own internal content (heading, text, button) vertically.
How it works step by step / Under the hood
- Step 1 — Box generation. During style computation, the browser reads the computed
displayvalue for every element and decides whether to create a principal box at all.display: noneshort-circuits this — no box, no participation in layout, and screen readers skip it too. - Step 2 — Outer type decides placement in the parent. If the outer type is
block, the box is placed in the block flow: it generates a new line before and after itself and stretches to fill the available inline space (unless width is constrained). If the outer type isinline, the box is placed inside the current line box, flowing left-to-right (or per the text direction) alongside text and other inline boxes. - Step 3 — Inner type decides how children are laid out. Once the box itself is placed, its inner display type takes over for its own content.
flow(the default for block/inline) lays children out using normal block-and-inline rules.flexswitches to the flex layout algorithm, distributing children along a main axis according toflex-grow/flex-shrink/flex-basis.gridswitches to the grid algorithm, placing children into explicit or auto-generated rows and columns. - Step 4 — Special cases resolve their own rules.
inline-blockis a hybrid: the browser treats the box as inline for placement purposes but computes its content box using block-layout math, which is why it can have a set width/height but still sits mid-line.contentsis resolved by deleting the box but re-parenting its children up a level in the box tree before the parent’s layout algorithm runs, so aflexorgridparent never even sees the wrapper.
Common Mistakes
Mistake 1: setting width/height on an inline element
Beginners often expect an inline element to size the way a block element does.
.badge {
display: inline;
width: 100px;
height: 40px;
background: crimson;
}
This is valid CSS, but width and height have no effect on inline boxes — the badge will only be as wide and tall as its text content, ignoring the declared dimensions entirely. The fix is to switch to a box type that accepts sizing while still flowing inline:
.badge {
display: inline-block;
width: 100px;
height: 40px;
background: crimson;
}
Mistake 2: trying to animate display directly
Because display looks like any other property, people try to transition it for show/hide effects.
.tooltip {
display: none;
transition: display 0.3s ease;
}
.tooltip.is-visible {
display: block;
}
In most browsers display is not smoothly animatable — the element simply pops in or out at some point during the transition, with no visible easing, since there is no meaningful “halfway” state between having a box and having none. The idiomatic fix is to animate opacity (and pair it with visibility so the hidden element also leaves the tab order and hit-testing):
.tooltip {
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.tooltip.is-visible {
opacity: 1;
visibility: visible;
}
Best Practices
- Reach for
flexorgridfor layout instead of old inline/float tricks — they were purpose-built for arranging groups of elements and handle spacing, alignment, and wrapping far more predictably. - Use
inline-blockonly for small, self-contained pieces (icons, badges, buttons) that need to sit inline with text but also need a fixed size — for larger layout regions, prefer flex or grid instead. - Remember that
display: noneremoves the element from the accessibility tree entirely; if content should stay perceivable to screen readers while visually hidden, use a “visually hidden” clipping technique instead, notdisplay: none. - Don’t use
display: nonefor content you plan to fade or slide in and out — useopacity/visibility/transformwith transitions, and reservedisplay: nonefor instant, permanent hiding. - Reach for
display: contentscarefully — it strips the wrapper’s box but also strips its background, border, padding, and (in some browsers) certain accessibility semantics, so test before relying on it. - Check the outer/inner type mental model when a layout misbehaves: ask “is this element placed correctly by its parent?” (outer type) versus “are this element’s own children arranged correctly?” (inner type) — it narrows down which value you actually need to change.
Practice Exercises
- Exercise 1: Build a horizontal navigation bar from an unordered list where the list items currently stack vertically. Change only the
<ul>‘sdisplayvalue (plus removing default list styling) so the items sit in a row with even spacing between them. - Exercise 2: You have a
<span>that needs a fixed 120px width, a background color, and 12px of padding, but it must still sit inline in the middle of a sentence rather than breaking onto its own line. Write the single-declaration fix. - Exercise 3: A component has a wrapper
<div>around several children that is breaking a CSS Grid layout because the grid only sees one grid item (the wrapper) instead of the children. Without removing the wrapper from the HTML, usedisplayto make the grid treat the children as direct items.
Summary
displaydetermines what kind of box an element generates and therefore how it’s placed by its parent and how it lays out its own children.- The modern model splits this into an outer display type (block or inline placement) and an inner display type (flow, flex, or grid layout of children).
blockelements stack and accept full box-model sizing;inlineelements flow with text and ignore width/height;inline-blockcombines both behaviors.flex/grid(and theirinline-variants) turn an element into a layout container for its children.display: noneremoves an element from layout and the accessibility tree entirely; it cannot be smoothly animated.display: contentsremoves an element’s own box while keeping its children in the render tree, useful for un-nesting wrapper elements in grid/flex layouts.
