Module 3: Box Model & Spacing
Display, Overflow and Visibility
How block, inline and inline-block really differ, what overflow does to content that does not fit, and the four different ways to hide an element.
Three properties decide how a box takes part in layout, what happens when its content is too big, and whether it is shown at all. They interact, and mixing them up is behind a lot of "why is there a gap here" and "why is my content cut off" bugs.
display: the box's role in layout#
Every element has an outer display type (how it sits among siblings) and an inner type (how its children are laid out). The three classic values show the outer part:
.a { display: block; } /* starts on a new line, fills the width */
.b { display: inline; } /* flows within a line of text */
.c { display: inline-block; } /* flows like text, but has block-box behavior inside */
| Behavior | block | inline | inline-block |
|---|---|---|---|
| Starts on a new line | yes | no | no |
width and height apply | yes | no | yes |
| Vertical margin and padding push neighbors | yes | no | yes |
| Fills available width by default | yes | no (fits its content) | no (fits its content) |
Examples of block elements by default: div, p, h1 to h6, ul, section. Inline by default: span, a, strong, em. img and button are special. They are inline-level but have inline-block-like behavior, which is why they accept a width.
Newer values change the inner type, and you will use them a lot: display: flex and display: grid (Module 5). There are also none, which removes the element entirely from layout, and contents, which removes the box but keeps its children as if they were children of the parent.
The inline-block gap#
Inline-block elements sit in a line of text, so the whitespace between them in your HTML is rendered as a space.
<div class="tab">One</div>
<div class="tab">Two</div>
.tab { display: inline-block; width: 50%; }
/* the browser renders a ~4px space between them, so 50% + 50% + space wraps */
This is why "two inline-blocks at 50% wrap" is a known trap. It is also one of the reasons flexbox replaced inline-block for layout. Do not fight the whitespace, use display: flex.
overflow: what happens when content does not fit#
When a box has a fixed size and its content is bigger, the extra content overflows. The overflow property decides what to do:
.box { width: 200px; height: 100px; }
.v { overflow: visible; } /* default: content spills outside the box */
.h { overflow: hidden; } /* clipped, and not scrollable by the user */
.s { overflow: scroll; } /* clipped, always shows scrollbars */
.a { overflow: auto; } /* clipped, scrollbars only when needed */
.c { overflow: clip; } /* like hidden, but no scroll container is created */
auto is the right choice for scrollable regions. scroll shows scrollbar tracks even when there is nothing to scroll, which looks cluttered on some systems.
You can set each axis separately:
.code { overflow-x: auto; overflow-y: hidden; }
There is a quirk here. If one axis is visible and the other is not, the browser changes visible to auto. You cannot have horizontal scrolling and visible vertical overflow at the same time.
Side effects of overflow#
Any value other than visible makes the box a scroll container, and it also establishes a new block formatting context. That is why overflow: hidden on a parent "magically" fixes collapsed margins and contains floats. It is also why it can bite you:
- Clipping. Box shadows, focus outlines, dropdown menus and tooltips that extend past the edge get cut off.
- Sticky breaks.
position: stickydescendants attach to the nearest scroll container. If a parent hasoverflow: hidden, sticky stops working as you expect (Lesson 5.1 covers this).
Truncating text#
The classic single-line ellipsis needs three properties together:
.title {
white-space: nowrap; /* keep it on one line */
overflow: hidden; /* clip what does not fit */
text-overflow: ellipsis; /* draw the ... */
}
text-overflow alone does nothing. It works only on an element that clips its overflow, and the text must not wrap.
Four ways to hide something#
They look similar and behave very differently:
| Method | Takes up space | Visible | Accessible to screen readers | Receives clicks and focus |
|---|---|---|---|---|
display: none | no | no | no | no |
visibility: hidden | yes | no | no | no |
opacity: 0 | yes | no | yes | yes |
hidden attribute | no (same as display none) | no | no | no |
display: noneremoves the box from the layout and from the accessibility tree. It is the right choice for "gone, as if it did not exist".visibility: hiddenkeeps the space but hides the content. Children can override it withvisibility: visible. It cannot be transitioned smoothly in the usual way, though there are tricks.opacity: 0is only fully transparent. The element is still there. It can still be clicked, tabbed to and read aloud. People use it for fade effects and then wonder why an invisible button still captures clicks. Pair it withpointer-events: noneorvisibility: hiddenwhen it should be unreachable.- The
hiddenattribute is the HTML equivalent ofdisplay: none. The browser's own stylesheet is what makes it work, so a rule like.card { display: flex; }can accidentally override it. Add[hidden] { display: none !important; }if you use both.
There is a fifth case worth knowing, a "visually hidden" utility that hides content from sight but keeps it for screen readers, which you will build in Module 9.
height: 100% and why it fails#
A percentage height needs a parent with a known height. A block's height is normally determined by its content, so a percentage of an unknown value is ignored and treated as auto.
.parent { } /* height depends on content */
.child { height: 100%; } /* no effect */
Either give every ancestor an explicit height, or, better, use a layout that distributes the space for you: flexbox or grid with flex: 1, or min-height: 100dvh on a root container.
Common mistakes#
overflow: hiddento fix a layout problem that really needs a proper fix, then losing shadows, focus rings and dropdowns.text-overflow: ellipsiswithoutoverflow: hiddenandwhite-space: nowrap.- Hiding with
opacity: 0and leaving an invisible clickable target on top of other elements. - Using
display: noneon content that screen readers should still announce (like a visually hidden label). - Inline-block layouts with unexplained gaps.
height: 100%on a child of an auto-height parent.
Practice#
- Build three buttons in a row with
display: inline-block. Observe the gap, then remove it withdisplay: flexandgap. - Create a 200 by 100px box with long text. Compare all five overflow values, and note what scrollbars appear.
- Truncate a long title to one line with an ellipsis. Then research and implement a three-line clamp using
-webkit-line-clamp. - Make four identical boxes and hide each with a different method. Tab through the page and click where they were, to record exactly what each method does to space, clicks and focus.