The layout is the usual one: a fixed-width avatar on the left, a message on the right that should take the rest of the row.
<div class="message">
<img class="avatar" src="/img/ana.png" alt="" width="48" height="48">
<div class="body">
<p class="author">Ana</p>
<p>See https://example.com/reports/2026/q3/regional-breakdown-final-v2-approved.pdf</p>
</div>
</div>.message {
display: flex;
gap: 12px;
width: 320px;
}
.avatar {
flex: none;
}
.body {
flex: 1;
}On a 320px-wide message, the URL doesn’t wrap and the body sticks out past the right edge. Adding overflow-wrap: break-word to the paragraph does nothing. Setting width: 100% on .body does nothing. The page gets a horizontal scrollbar on mobile. This is one of the most-reported flexbox “bugs”, and it’s the spec working as designed.
The rule: flex items won’t shrink below their content by default
flex: 1 means flex-grow: 1; flex-shrink: 1; flex-basis: 0%. So the body is allowed to shrink. But shrinking is limited by the item’s minimum size, and for flex items the initial value of min-width is auto, not 0.
For a flex item whose overflow is visible, min-width: auto computes to its “content-based minimum size”, which is essentially its min-content width: the width of the longest thing in it that can’t be broken. For ordinary prose that’s the longest word, maybe 100px, so you never notice. For a URL with no spaces, it’s the whole URL. Say that’s 470px in the paragraph’s font. The body’s minimum width is then 470px, and the flex algorithm won’t shrink it below that.
Run the numbers for the 320px container:
container width 320
avatar (flex: none) 48
gap 12
space left for .body 260
.body's min-content width 470 (the URL)
.body's used width 470 (min-width wins)
overflow 48 + 12 + 470 - 320 = 210pxThe message box stays 320px, but its content spills 210px past the right edge.
Why doesn’t overflow-wrap: break-word help? It only breaks a word when the word would otherwise overflow its line, and it doesn’t change the min-content width. The flex item has already been sized at 470px from that min-content width, so the line is 470px wide, the URL fits on it, and nothing breaks. overflow-wrap: anywhere is different: the break opportunities it creates do count towards min-content, so putting it on the paragraph shrinks the body’s minimum and the layout fixes itself. That difference between break-word and anywhere is a good detail to know for interviews.
The fix
Tell the item it may shrink below its content:
.body {
flex: 1;
min-width: 0;
}
.body p {
overflow-wrap: break-word;
}Now .body gets the 260px left over. Its paragraph is 260px wide, and the URL is too long for that line, so break-word splits it across lines inside the box. No overflow, no horizontal scroll.
min-width: 0 alone only moves the problem: the body is 260px wide but the URL still overflows the body (and is clipped or spills, depending on its parent). You always pair it with a decision about the content, and the right decision depends on what the content is:
- Prose with long URLs or IDs:
overflow-wrap: break-word(oranywhere) so it wraps. - A single-line label:
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;to truncate with “…”. Truncation only works once the item is allowed to be narrower than the text, which is why ellipsis “doesn’t work” inside flex items until you addmin-width: 0. - A
<pre>code block or a wide table:overflow-x: autoon the block, so it scrolls inside the message instead of stretching the page.
Setting overflow to anything other than visible on the flex item has the same effect as min-width: 0, because the automatic minimum only applies when overflow is visible. So .body { flex: 1; overflow: hidden; } also shrinks it. That’s why some codebases have overflow: hidden sprinkled on flex children: it works, but it also clips focus outlines and dropdowns, so min-width: 0 states the intent more precisely.
Nested flex items: the fix has to go all the way down
The bug often appears in a chain:
<main class="layout"> <!-- display: flex -->
<nav class="sidebar">…</nav>
<section class="content"> <!-- flex: 1, also display: flex; flex-direction: column -->
<div class="panel"> <!-- flex item of .content -->
<pre>…long code…</pre>
</div>
</section>
</main>The <pre> has a large min-content width. .panel’s min-content is at least that. .content’s min-content is at least that too. Each level’s min-width: auto carries the width upward. Adding overflow-x: auto to the <pre> doesn’t help until every flex item between it and the constrained container has min-width: 0. Here that’s .content, the row flex item of .layout. .panel doesn’t need it: in a column container the automatic minimum applies to height (the main axis), and the panel’s width simply stretches to match .content. Once .content is constrained, the panel is too, and the <pre> can finally scroll.
The debugging approach: in DevTools, select the overflowing element and walk up the tree looking for the first ancestor that’s wider than you expect. The flex item just below the last correctly sized ancestor is where min-width: 0 belongs. Firefox’s flexbox inspector also tells you when an item was clamped to its minimum size, which makes the culprit easy to find.
The vertical version
In a column flex container, the same rule applies to min-height. A classic case is a full-height app shell:
.app {
display: flex;
flex-direction: column;
height: 100vh;
}
.messages {
flex: 1;
overflow-y: auto;
}This one works, because overflow-y: auto turns off the automatic minimum. Remove the overflow and put it on a child instead, and .messages grows to fit all its content, pushing the input box below the screen. If the scrolling element is nested inside .messages, give .messages min-height: 0.
Grid has the same trap
A grid track sized 1fr means minmax(auto, 1fr), and that auto minimum is the min-content size of the items in the track. So:
.page {
display: grid;
grid-template-columns: 240px 1fr;
}A wide <pre> in the second column stretches it, and the page scrolls sideways. The fix is to state a zero minimum:
.page {
grid-template-columns: 240px minmax(0, 1fr);
}Alternatively, min-width: 0 on the grid item works too, because the grid’s auto minimum asks the item for its minimum size, and you’ve just told the item that its minimum is zero.
Interview practice
A card row uses display: flex with three .card { flex: 1; } children. On a phone, one card contains a product code like SKU-8F3K2-ZQ9X-77LMN-ABCDEF and the row becomes wider than the screen, while the other two cards are squeezed. Explain the layout the browser produced and give a fix that keeps all three cards equal width.
Answer
Each card has flex-basis: 0% and equal grow factors, so in theory they’d split the row three ways. But each card’s minimum width is auto, which resolves to its min-content width, and the card with the product code has a min-content width equal to the full code, because it has no break opportunities. The flex algorithm assigns that card at least that width, then distributes what’s left to the other two, which is why they’re squeezed. If the code is wider than the row allows, the row overflows.
Set min-width: 0 on .card so all three get the same share, then deal with the code inside the card: overflow-wrap: anywhere if it’s fine for it to wrap, or text-overflow: ellipsis with white-space: nowrap and overflow: hidden if it must stay on one line (with the full value available elsewhere, for example in a title attribute or a details view). Using overflow-wrap: anywhere alone, without min-width: 0, would also work here, because it reduces the min-content width, but setting both makes the cards robust to other unbreakable content later.
Keep going
- HTML and CSS topic hub
- HTML and CSS multiple-choice questions
- CSS grid tracks and minmax sizing
- CSS text overflow and ellipsis
- MDN: min-width