CSS overflow controls what happens when an element’s content is larger than its box. The default browser behaviour is to let content spill out โ but on real sites you need scrollable panels, truncated text, clipped images, and contained layouts. In this lesson you will master all overflow values, the block formatting context they create, text truncation patterns, and the modern overflow: clip option.
Overflow Property Values
| Value | Behaviour | Scrollbar | Common Use Case |
|---|---|---|---|
visible |
Default โ content spills outside the box | None | Normal flow content; tooltip anchors |
hidden |
Content beyond the box edge is clipped โ invisible | None | Image cropping, containing floats, decorative clips |
scroll |
Content clipped; scrollbar always present | Always (even if not needed) | Fixed-width sidebars where scrollbar space must be reserved |
auto |
Content clipped; scrollbar appears only when needed | On demand | Most scrollable panels โ preferred over scroll |
clip |
Like hidden but does NOT create a Block Formatting Context | None | Clip without layout side effects |
overflow-x and overflow-y
| Pattern | CSS | Effect |
|---|---|---|
| Horizontal scroll only | overflow-x: auto; overflow-y: hidden; |
Wide tables or code blocks scroll sideways |
| Vertical scroll only | overflow-y: auto; overflow-x: hidden; |
Scrollable panels โ most common use case |
| Both axes | overflow: auto; |
Scrollable in both directions |
| Clip one, allow other | overflow-x: clip; overflow-y: visible; |
Clip horizontally without affecting vertical |
Text Overflow Patterns
| Pattern | CSS Required | Result |
|---|---|---|
| Single-line ellipsis | overflow: hidden; white-space: nowrap; text-overflow: ellipsis; |
Long text ends in “โฆ” |
| Multi-line clamp | display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; |
Clamps to N lines with “โฆ” |
| Scroll on overflow | overflow-x: auto; white-space: nowrap; |
Content scrolls horizontally |
overflow: hidden and overflow: auto both create a Block Formatting Context (BFC) โ the same mechanism that makes display: flow-root contain floats. This means overflow: hidden is an alternative to flow-root for containing floats, but at the cost of potentially clipping overflowing content like tooltips and dropdowns.overflow: hidden to clip the text, white-space: nowrap to prevent wrapping to a new line, and text-overflow: ellipsis to show the “โฆ” character. Missing any one of these three means the truncation will not work.overflow: hidden โ they will be clipped. If you need both containment and visible overflow for children, use a different containment strategy: move the positioned element up the DOM tree so it is not clipped by the overflow: hidden ancestor.Basic Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Overflow</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; padding: 32px; background: #f8fafc; }
.label { font-size: 0.75rem; font-weight: 700; text-transform: uppercase;
letter-spacing: 0.06em; color: #64748b; margin-bottom: 6px; }
.demo-box {
width: 260px;
height: 80px;
background: white;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 20px;
font-size: 0.875rem;
color: #334155;
}
/* overflow: visible โ spills out (default) */
.ov-visible { overflow: visible; }
/* overflow: hidden โ clips content */
.ov-hidden { overflow: hidden; }
/* overflow: auto โ scrollbar on demand */
.ov-auto { overflow: auto; }
/* overflow: scroll โ always shows scrollbar */
.ov-scroll { overflow: scroll; }
/* Single-line truncation */
.truncate {
width: 260px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background: #ede9fe;
padding: 10px 14px;
border-radius: 6px;
font-size: 0.875rem;
margin-bottom: 12px;
}
/* Multi-line clamp */
.clamp {
width: 260px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
background: #d1fae5;
padding: 10px 14px;
border-radius: 6px;
font-size: 0.875rem;
line-height: 1.5;
margin-bottom: 12px;
}
/* Horizontal scroll โ code block pattern */
.code-scroll {
width: 260px;
overflow-x: auto;
white-space: nowrap;
background: #1e293b;
color: #e2e8f0;
padding: 12px 16px;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 0.8rem;
margin-bottom: 20px;
}
/* Image crop with overflow: hidden */
.img-crop {
width: 260px;
height: 120px;
overflow: hidden;
border-radius: 12px;
position: relative;
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #0891b2 100%);
margin-bottom: 20px;
}
.img-crop-inner {
width: 130%;
height: 130%;
position: absolute;
top: -15%;
left: -15%;
background: inherit;
border-radius: 50%;
}
</style>
</head>
<body>
<div class="label">overflow: visible (default) โ content spills outside</div>
<div class="demo-box ov-visible">This is a lot of content that is much longer than the 80px height of this box. With overflow: visible it spills right outside the border.</div>
<div class="label">overflow: hidden โ content clipped at boundary</div>
<div class="demo-box ov-hidden">This is a lot of content that is much longer than the 80px height of this box. With overflow: hidden it is clipped at the border edge.</div>
<div class="label">overflow: auto โ scrollbar appears when needed</div>
<div class="demo-box ov-auto">This is a lot of content that is much longer than the 80px height of this box. With overflow: auto a scrollbar appears โ try scrolling!</div>
<div class="label">Single-line text-overflow: ellipsis</div>
<div class="truncate">Very long filename: project-design-system-components-v2-final-approved.sketch</div>
<div class="label">Multi-line clamp to 3 lines</div>
<div class="clamp">This is a longer description that would normally span more than three lines of text in a card. The -webkit-line-clamp property truncates it at exactly three lines and adds an ellipsis at the end.</div>
<div class="label">Horizontal scroll โ code block</div>
<div class="code-scroll">const result = await fetch('https://api.example.com/v1/users?filter=active&sort=created_at&limit=50');</div>
<div class="label">Image crop with overflow: hidden</div>
<div class="img-crop"><div class="img-crop-inner"></div></div>
</body>
</html>
How It Works
Step 1 โ overflow: visible Is the Default (and Often Surprising)
Without any overflow setting, content that exceeds the element’s bounds paints over neighbouring elements. This is usually not a problem for text that wraps, but for absolutely positioned children or injected content it can cause unexpected overlaps.
Step 2 โ overflow: hidden Clips and Creates a BFC
The clipping happens at the element’s padding edge. As a side effect, overflow: hidden creates a Block Formatting Context โ the same effect as display: flow-root. This is why it contains floated children. The tradeoff is that any descendant with position: absolute or tooltips will also be clipped.
Step 3 โ overflow: auto Only Shows Scrollbar When Needed
On systems with overlay scrollbars (macOS, mobile) the scrollbar appears over content and takes no space. On systems with classic scrollbars (Windows) the scrollbar permanently occupies 15โ17px of width. Using overflow: auto means this space is only consumed when content genuinely overflows.
Step 4 โ Text Truncation Requires All Three Properties
white-space: nowrap prevents the text from wrapping to a new line. overflow: hidden clips the text that extends beyond the element’s width. text-overflow: ellipsis draws the “โฆ” character at the clip point. All three are required โ any one alone is insufficient.
Step 5 โ -webkit-line-clamp Clamps to N Lines
Despite the vendor prefix, -webkit-line-clamp is now supported in all browsers. The display: -webkit-box changes the element to a deprecated but still functional box model used specifically for line clamping. It requires overflow: hidden to actually clip the content at the clamped line.
Real-World Example: Card Grid with Clamped Text
/* card-grid.css */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #f8fafc; padding: 40px 24px; }
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
}
.article-card {
background: white;
border-radius: 16px;
border: 1px solid #e2e8f0;
overflow: hidden; /* clips the hero image to border-radius */
display: flex;
flex-direction: column;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.2s;
}
.article-card:hover { box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); }
/* Hero image โ fills card width, fixed height, cropped */
.card-hero {
width: 100%;
height: 180px;
object-fit: cover; /* crops image to fill the box */
display: block;
flex-shrink: 0;
background: linear-gradient(135deg, #4f46e5, #7c3aed);
}
.card-body {
padding: 20px;
display: flex;
flex-direction: column;
flex: 1;
}
/* Category tag */
.card-tag {
display: inline-flex;
align-items: center;
background: #ede9fe;
color: #4f46e5;
font-size: 0.7rem;
font-weight: 700;
padding: 3px 10px;
border-radius: 9999px;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 10px;
width: fit-content;
}
/* Title โ clamp to 2 lines */
.card-title {
font-size: 1rem;
font-weight: 700;
color: #0f172a;
line-height: 1.35;
margin-bottom: 8px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* Description โ clamp to 3 lines */
.card-desc {
font-size: 0.875rem;
color: #64748b;
line-height: 1.6;
flex: 1;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
margin-bottom: 16px;
}
/* Horizontal scrollable code block */
.card-code {
background: #1e293b;
border-radius: 6px;
padding: 10px 14px;
overflow-x: auto;
white-space: nowrap;
font-family: 'Courier New', monospace;
font-size: 0.78rem;
color: #e2e8f0;
margin-bottom: 16px;
}
.card-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: auto;
padding-top: 12px;
border-top: 1px solid #f1f5f9;
}
.card-author {
font-size: 0.8rem;
color: #94a3b8;
/* Single-line truncation for author name */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 140px;
}
Common Mistakes
Mistake 1 โ Missing white-space: nowrap for ellipsis
โ Wrong โ text wraps to new lines instead of truncating with ellipsis:
.title { overflow: hidden; text-overflow: ellipsis; }
/* Without white-space: nowrap, text wraps โ ellipsis never triggers */
โ Correct โ all three properties required together:
.title { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
Mistake 2 โ overflow: hidden clipping absolutely positioned children
โ Wrong โ tooltip inside overflow: hidden parent gets clipped:
.card { overflow: hidden; }
.card .tooltip { position: absolute; top: -40px; } /* clipped! */
โ Correct โ move the tooltip outside the overflow: hidden container:
<div class="card">Card content</div>
<div class="tooltip">Tooltip outside overflow container</div>
Mistake 3 โ Using overflow: scroll causing permanent scrollbar space
โ Wrong โ scrollbar permanently reserves space even when content fits:
.panel { height: 300px; overflow: scroll; }
/* On Windows, scrollbar takes ~17px regardless of content */
โ Correct โ use overflow: auto for on-demand scrollbars:
.panel { height: 300px; overflow: auto; }
Quick Reference
| Value / Pattern | Scrollbar | Creates BFC | Best Use |
|---|---|---|---|
overflow: visible |
None | No | Default; tooltips that must spill out |
overflow: hidden |
None | Yes | Image crop, float containment, decorative clip |
overflow: auto |
On demand | Yes | Scrollable panels โ most common choice |
overflow: scroll |
Always | Yes | Reserve scrollbar space to prevent layout shift |
overflow: clip |
None | No | Clip without BFC side effects |
| Single-line ellipsis | N/A | N/A | hidden + nowrap + ellipsis (all 3 required) |
| Multi-line clamp | N/A | N/A | -webkit-line-clamp: N with hidden |