Display
Control how an element generates boxes with block, inline, flex, grid, hidden and the rest — the first decision in every layout.
display decides what kind of box an element creates and how its children are laid out. Almost every layout problem starts here.
Overview#
<div class="space-y-3 p-4 text-sm">
<div class="rounded bg-indigo-100 p-2 text-indigo-900">block — fills the width, starts a new line</div>
<div>
<span class="rounded bg-emerald-100 p-2 text-emerald-900">inline</span>
<span class="rounded bg-emerald-100 p-2 text-emerald-900">flows with text</span>
</div>
<div class="flex gap-2 rounded bg-amber-50 p-2">
<span class="rounded bg-amber-200 px-2 py-1 text-amber-900">flex</span>
<span class="rounded bg-amber-200 px-2 py-1 text-amber-900">children in a row</span>
</div>
<div class="grid grid-cols-3 gap-2 rounded bg-sky-50 p-2">
<span class="rounded bg-sky-200 px-2 py-1 text-center text-sky-900">grid</span>
<span class="rounded bg-sky-200 px-2 py-1 text-center text-sky-900">in</span>
<span class="rounded bg-sky-200 px-2 py-1 text-center text-sky-900">columns</span>
</div>
</div>Reference#
| Utility | CSS | Use for |
|---|---|---|
block | display: block | Full-width boxes, stacked children |
inline | display: inline | Text-level elements; ignores width/height |
inline-block | display: inline-block | Flows inline but accepts width, height and vertical padding |
flex | display: flex | One-dimensional layout |
inline-flex | display: inline-flex | A flex container that sits inside a line of text |
grid | display: grid | Two-dimensional layout |
inline-grid | display: inline-grid | Grid that flows inline |
flow-root | display: flow-root | Contains floats and stops margin collapse |
contents | display: contents | Removes the box, promoting children to the parent's layout |
table, table-row, table-cell | display: table* | Emulating table layout on non-table elements |
list-item | display: list-item | Elements that should render a marker |
hidden | display: none | Remove from layout and the accessibility tree |
When to use which#
block vs inline-block vs inline#
inlineignoreswidth,heightand vertical margin. Padding renders but does not push surrounding lines apart.inline-blockrespects all box properties while still sitting in the text flow — good for badges and buttons inside a paragraph.blocktakes the full available width and starts a new line.
<p class="max-w-md p-4 text-sm leading-loose text-gray-700">
A status of
<span class="inline-block rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-semibold text-emerald-800">Active</span>
sits neatly in the sentence because <code class="rounded bg-gray-100 px-1">inline-block</code> accepts vertical padding.
</p>flex vs grid#
flex | grid | |
|---|---|---|
| Dimensions | One (a row or a column) | Two (rows and columns) |
| Sizing driven by | Content | The track definition |
| Best for | Toolbars, nav bars, button groups, card internals | Page shells, galleries, forms, dashboards |
| Alignment across siblings | Only along one axis | Rows and columns line up |
Rule of thumb: if you are describing a line of things, use Flexbox. If you are describing a matrix, use Grid. See Flexbox and Grid.
flow-root#
Creates a new block formatting context. Two things it fixes:
<!-- 1. Contains floated children -->
<div class="flow-root">
<img class="float-left mr-4" src="…" alt="">
<p>Text wraps, and the parent still has height.</p>
</div>
<!-- 2. Stops the first child's margin escaping the parent -->
<div class="flow-root bg-gray-100">
<p class="mt-8">The margin stays inside the grey box.</p>
</div>contents#
Removes the wrapper's own box so its children participate in the grandparent's layout. Useful when a component adds a wrapper that would otherwise break a grid.
<div class="grid grid-cols-3 gap-4">
<div class="contents">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</div>Hiding elements#
There are three different meanings of "hidden", and they are not interchangeable.
| Approach | Occupies space | Screen readers | Use for |
|---|---|---|---|
hidden | No | Not announced | Genuinely irrelevant content |
invisible | Yes | Not announced | Reserving space while hidden |
opacity-0 | Yes | Still announced | Fade transitions |
sr-only | No (visually) | Announced | Labels for icon-only controls |
<div class="space-y-2 p-4 text-sm">
<button class="inline-flex items-center gap-2 rounded-md bg-gray-900 px-3 py-2 text-white">
<svg class="size-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" d="M5 12h14m-7-7v14"/></svg>
<span class="sr-only">Add item</span>
</button>
<p class="text-gray-600">The button above is icon-only but still announces "Add item".</p>
</div>Use not-sr-only to reveal it again at a breakpoint: sr-only sm:not-sr-only.
Common patterns#
Responsive show/hide#
<nav class="hidden md:flex">Desktop navigation</nav>
<button class="md:hidden">Open menu</button>Icon and label alignment#
<a href="#" class="inline-flex items-center gap-2 text-indigo-600 hover:text-indigo-500">
<svg class="size-4" …></svg>
Documentation
</a>inline-flex keeps the link inline while aligning the icon to the text.
Toggling with data-*#
<div data-open="false" class="hidden data-[open=true]:block">…</div>Common mistakes#
- Setting width on an
inlineelement. It has no effect. Useinline-block. - Using
hiddenfor content that should stay available to screen readers. Usesr-only. - Reaching for
opacity-0to hide interactive content. It stays focusable and announced. Pair withpointer-events-noneandaria-hidden, or usehidden. flexon the item instead of the container.flexmakes an element's children flex items.- Expecting
gap-*to work withoutflexorgrid. It does nothing on a block element.
Related topics#
- Flexbox — one-dimensional layout
- Grid — two-dimensional layout
- Position — taking elements out of flow
- Overflow & Layering — clipping and stacking