Display
Control how elements participate in page layout using the display property, and distinguish it from visibility for hiding content.
Every HTML element has a default display value that determines the fundamental way it behaves in layout — whether it starts on a new line, whether it respects width and height, and whether it can sit beside other elements. Understanding display is the gateway to understanding all of CSS layout, since flexbox and grid (covered in later modules) are themselves just special display values.
block, inline, and inline-block
display: block makes an element start on its own new line, stretch to fill the full width of its container by default, and fully respect explicit width and height values. Elements like <div>, <p>, <h1>-<h6>, and <ul> are block by default.
display: inline makes an element flow within a line of text, sitting beside other inline content with no forced line break. Critically, inline elements ignore width and height entirely, and vertical padding/margin visually overlaps surrounding lines rather than pushing them away (only horizontal padding/margin reliably affects layout). Elements like <span>, <a>, <strong>, and <em> are inline by default.
display: inline-block is a hybrid: the element flows inline with surrounding content (no forced line break, sits beside neighbors) but respects width, height, and all four sides of padding/margin exactly like a block element does. This makes it the classic choice for things like navigation links that need padding and a fixed size but should still sit in a row.
• block — new line, full width by default, respects width/height • inline — flows in text, ignores width/height, only horizontal margin/padding reliably applies • inline-block — flows in text like inline, but respects width/height/margin/padding like block
none vs visibility: hidden
display: none removes an element from the page entirely — not just visually. It takes up no space, as if it were deleted from the document, and it is skipped by screen readers. visibility: hidden, on the other hand, makes the element invisible but leaves its space in the layout completely intact — other elements do not shift to fill the gap. Screen readers also skip a visibility: hidden element (unlike opacity: 0, which stays in the accessibility tree), but sighted users will still see the empty space it occupies.
Choosing between them depends on intent: use display: none when you want the element and its space gone entirely (e.g. a collapsed accordion panel); use visibility: hidden when you want to preserve the layout position but hide the content (e.g. temporarily hiding one item in a fixed grid without the other items reflowing).
contents and flow-root
display: contents is unusual — it makes the element itself disappear from the box-model rendering entirely (no box, no background, no border) while its CHILDREN remain and render exactly as if they were direct children of the element's own parent. It's useful for wrapper elements that exist only for semantic/JS reasons but shouldn't create any visual box of their own.
display: flow-root creates a new 'block formatting context' for the element — a self-contained layout area. Its most common practical use is containing floated children: normally, a container with only floated children collapses to zero height because floats are taken out of normal flow; wrapping that container's content or applying flow-root to the container fixes this 'collapsing parent' problem without needing the older clearfix hacks.
Display Values
.block-el { display: block; }
.inline-el { display: inline; }
.hybrid-el { display: inline-block; width: 120px; padding: 8px; }
.hidden-el { display: none; }
.invisible-el { visibility: hidden; }
.wrapper { display: flow-root; }Code Examples
Anchor tags are inline by default and would otherwise ignore width and vertical padding. Switching to inline-block lets each link have a consistent clickable width and full padding on all sides, while still sitting side by side in a horizontal row.
The inactive tab panel vanishes completely and other content shifts up to fill its space, appropriate for a tabbed interface. The empty placeholder slot stays invisible but keeps its exact spot in a grid, so surrounding grid items don't rearrange.
Without flow-root on .gallery, a container whose only children are floated images would collapse to zero height, since floats are removed from normal document flow. display: flow-root creates a new block formatting context that forces the container to properly wrap around its floated children.
.nav a {
display: inline-block;
padding: 8px 16px;
width: 100px;
text-align: center;
}.tab-panel.inactive {
display: none; /* removed entirely, no gap left behind */
}
.placeholder-slot.empty {
visibility: hidden; /* invisible, but space is reserved */
}.gallery {
display: flow-root;
}
.gallery img {
float: left;
margin-right: 8px;
}Important Rules
Inline elements ignore width, height, and vertical (top/bottom) margin/padding for layout purposes; only horizontal margin/padding reliably affects surrounding layout.
display: none removes the element and its space from the page; visibility: hidden removes only its visual appearance, leaving its space intact.
inline-block combines inline flow behavior with block-style sizing — no forced line break, but width/height/margin/padding all apply.
flow-root is the modern, hack-free way to contain floated children inside their parent.
Why this matters on real projects
Toggling display: none is the standard mechanism behind dropdown menus, modals, tabs, and accordions — JavaScript typically just adds or removes a class that flips this one property. Getting the block/inline/inline-block distinction right is also foundational before learning flexbox and grid, since both of those are themselves just other values of the same display property (display: flex, display: grid).
span { width: 200px; height: 50px; } /* span is inline by default */ span { display: inline-block; width: 200px; height: 50px; }
Inline elements like <span> silently ignore width and height. Without switching to inline-block or block, the width/height declarations have no visible effect at all, which is a very common source of confusion.
[Concept Matching] Match each requirement to the correct display/visibility approach: you want an element completely removed from the page with no gap left behind, versus an element invisible but still reserving its layout space. A. display: none for 'no gap'; visibility: hidden for 'reserve space' B. visibility: hidden for 'no gap'; display: none for 'reserve space' C. Both requirements are solved identically by opacity: 0 D. display: none reserves space; visibility: hidden removes it entirely
A. display: none for 'no gap'; visibility: hidden for 'reserve space' — display: none takes the element out of layout entirely, so no space is reserved and surrounding elements shift to fill the gap. visibility: hidden hides the element visually but keeps its box in the layout, so its space stays reserved and nothing shifts.
Build a horizontal navigation bar where each <a> link has equal padding on all four sides and a fixed width, but the links still sit in a single row without wrapping to new lines..nav a { display: inline-block; width: 100px; padding: 10px; text-align: center; }
Anchors are inline by default; you need a display value that keeps inline flow but adds block-style box model support.