Lesson 16 / 26
Overflow Management
Handling the case where content does not fit its box; comparing overflow values, the separate mechanisms for text overflow, and clipping's accessibility cost.
Contents
Throughout this topic, boxes were sized, arranged, and overlapped. One situation has not been addressed: what happens when content does not fit its box?
The situation is unavoidable. A long station name does not fit a narrow cell, a nine-column measurement table does not fit a narrow screen, a long caption does not fit a card given a fixed height. This lesson takes up what overflow is, what each of the options costs, and why text overflow is a separate matter.
Overflow’s Default Behavior Is Visibility
When a box’s content does not fit the box, the default behavior is for the content to spill out and stay visible. It is not clipped, not hidden, and does not grow the box.
This choice is deliberate: content that disappears is worse than content that breaks the layout. Overflowing text stays visible and readable; the problem is seen and can be fixed.
// overflow.mjs — computes whether content fits the box and the result of each overflow value function overflowState({ boxWidth, boxHeight, contentWidth, contentHeight, overflow }) { const horizontal = Math.max(0, contentWidth - boxWidth); const vertical = Math.max(0, contentHeight - boxHeight); const result = { "visible": { visible: true, scrollable: false, clips: false }, "hidden": { visible: false, scrollable: false, clips: true }, "clip": { visible: false, scrollable: false, clips: true }, "scroll": { visible: false, scrollable: true, clips: true }, "auto": { visible: false, scrollable: horizontal > 0 || vertical > 0, clips: true }, }[overflow]; return { horizontal, vertical, ...result }; } const box = { boxWidth: 320, boxHeight: 120 }; const content = { contentWidth: 480, contentHeight: 200 }; console.log(`box ${box.boxWidth}x${box.boxHeight}, content ${content.contentWidth}x${content.contentHeight}`); for (const value of ["visible", "hidden", "clip", "scroll", "auto"]) { const r = overflowState({ ...box, ...content, overflow: value }); console.log(`${value.padEnd(8)} overflow=${r.horizontal}x${r.vertical} visible from outside=${String(r.visible).padEnd(5)} scrollable=${String(r.scrollable).padEnd(5)} clips=${r.clips}`); } console.log("\n--- content that fits ---"); for (const value of ["scroll", "auto"]) { const r = overflowState({ ...box, contentWidth: 300, contentHeight: 100, overflow: value }); console.log(`${value.padEnd(8)} overflow=${r.horizontal}x${r.vertical} scrollable=${r.scrollable}`); } console.log("\n--- a long word fitting a line ---"); const word = "North-Slope-Automatic-Measurement-Station"; const CHAR_WIDTH = 8; // an example font's character width const boxWidth = 160; const fits = Math.floor(boxWidth / CHAR_WIDTH); console.log(`word ${word.length} characters x ${CHAR_WIDTH}px = ${word.length * CHAR_WIDTH}px`); console.log(`box ${boxWidth}px -> ${fits} characters fit a line, overflow ${word.length * CHAR_WIDTH - boxWidth}px`); const pieces = word.match(new RegExp(`.{1,${fits}}`, "g")); console.log("split with overflow-wrap: anywhere:"); for (const p of pieces) console.log(` |${p.padEnd(fits)}|`);
box 320x120, content 480x200 visible overflow=160x80 visible from outside=true scrollable=false clips=false hidden overflow=160x80 visible from outside=false scrollable=false clips=true clip overflow=160x80 visible from outside=false scrollable=false clips=true scroll overflow=160x80 visible from outside=false scrollable=true clips=true auto overflow=160x80 visible from outside=false scrollable=true clips=true --- content that fits --- scroll overflow=0x0 scrollable=true auto overflow=0x0 scrollable=false --- a long word fitting a line --- word 41 characters x 8px = 328px box 160px -> 20 characters fit a line, overflow 168px split with overflow-wrap: anywhere: |North-Slope-Automati| |c-Measurement-Statio| |n |
Comparing the Five Values
visible — the initial value. Content overflows and stays visible. The box itself does not
grow; its neighbors have no knowledge of the overflow and can be painted over it.
hidden — the overflowing part is clipped and becomes unreachable. No scrollbar is
shown, but the box is still a box that can be scrolled programmatically; a focused element can
be brought into the visible area.
clip — clips like hidden but does not make the box scrollable. The clipping is final.
scroll — the overflowing part is clipped and a scrollbar is always shown.
auto — clips and shows a scrollbar only when needed.
The output’s second block gives the difference between these last two values: when content
fits the box, scroll still produces a scrollable box, auto does not. In some environments a
scrollbar takes up space; writing scroll in this case reserves a strip and narrows the box
even when content fits. If scrolling should appear only when needed, auto is written.
The Cost of Clipping
Writing overflow: hidden is a frequently reached-for fix, and it is used for two different
purposes. The two need to be told apart.
The first use is deliberate clipping: fitting an image into a box, keeping a decorative shape from spilling out. What is clipped here carries no information.
The second use is making the overflow problem invisible: content does not fit, and hiding it pretends the problem has gone away. This does not solve the problem; it hides it from the user. Clipped text can still be read by a screen reader but is visually unreachable; a user navigating by keyboard can focus it but cannot see it.
The distinction is made with this question: is it acceptable for the clipped thing to be
lost? If the answer is no, hidden is not written — auto is written instead, or the box’s
size is made flexible.
There is one more side effect, and it has come up twice in earlier lessons: an overflow
value other than visible makes the box an independent formatting root. Floated children
are included in the height, margin collapsing stops at the box’s boundary. This is why writing
overflow: hidden sometimes produces a layout change that looks unrelated.
Two Axes Separately
overflow is a shorthand; it sets the overflow-x and overflow-y longhands. Their logical
counterparts are overflow-inline and overflow-block.
The two axes can be given different values, but there is a constraint: if one axis is
visible while the other takes a clipping value, the visible axis is treated like auto.
The two axes are not independent; a box that clips in one direction cannot leave overflowing
content visible in the other.
Horizontal scrolling needs special handling. The way to show a wide measurement table on a
narrow screen is to place the table inside a horizontally scrollable box. Such a box must also
be reachable by keyboard; giving a scrollable region tabindex="0" and naming it with an
accessible name is the counterpart of this.
Text Overflow Is a Separate Mechanism
The output’s third block points to a different problem. A forty-one-character word does not
fit a 160-unit box and overflows by 168 units. This is not a problem to be solved with
overflow — the word needs to be broken.
Line-breaking rules are managed by separate properties:
overflow-wrap: anywhere — breaks a word that does not fit at any point. The output’s last
lines showed the break made under this rule. The break-word value does similar work but is
not included in the box’s minimum content size calculation.
word-break: break-all — breaks words whether or not they fit; it pays no attention to word
integrity.
hyphens: auto — hyphenates according to the document’s language and puts a hyphen at the
break point. It requires the lang attribute to be declared; this is one more counterpart of
the lang="en" declaration written on the root element in the Web Fundamentals and HTML
course.
white-space: nowrap — does the opposite: prevents line breaking altogether. This does not
prevent overflow, it guarantees it.
text-overflow: ellipsis — puts an ellipsis at the end of overflowing text. It does not work
alone; the box must be clipping and must be a single line:
.station-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
The three declarations are written together. It must not be forgotten that the clipped text is
lost: the full name should stay reachable in a title attribute or somewhere else.
/* station.css — step 15: overflow */ .table-wrapper { overflow-x: auto; overscroll-behavior-x: contain; } .measurement-table { min-width: 40rem; } .card .description { overflow-wrap: anywhere; hyphens: auto; } .location-image img { overflow: clip; }
.table-wrapper makes the table horizontally scrollable on a narrow screen; the min-width
declaration keeps the table from being squeezed into unreadability. Together, the two choose
to scroll the table rather than shrink it.
overscroll-behavior-x: contain keeps the page from shifting once the box’s own scrolling
ends — it keeps the scroll chain stopping at that box.
Summary
- Overflow’s default behavior is visibility: content spills outside the box and stays readable. This rests on the assumption that lost content is worse than a broken layout.
hiddenandclipclip;scrollalways shows a scrollbar,autoshows one only when needed; when content fits,scrollstill produces a scrollable box.- An
overflowvalue other thanvisiblemakes the box an independent formatting root; it contains floated children and stops margin collapsing at its boundary. - The two axes are not independent: while one axis clips, the other cannot stay
visible, and is treated likeauto. - A word that does not fit is solved not with
overflowbut with line-breaking properties;text-overflow: ellipsisdoes not work alone, it is written together with clipping and single-line declarations.
Next Step
This topic built the size, arrangement, and boundaries of boxes. The page that results is structured but colorless: every decision so far concerned geometry. The next topic takes up visual properties, and the first question is color itself — how a color is written, in which space it is defined, and how the contrast between two colors turns into a number.
To keep your progress and take notes, Log in
My notes
Log in to take notes.