Overflow and Scrolling
IntermediateContent that does not fit has to go somewhere. Choosing deliberately — clip, scroll, wrap or truncate — and making the result keyboard-accessible is the difference between a bug and a feature.
Overview
Overflow is where layout meets real content, which is always longer than the design assumed. The default is to spill visibly, which at least makes the problem obvious; overflow: hidden makes it invisible, which is why content silently disappears in production. Beyond the property itself, this concept covers the horizontal-scrollbar hunt every developer eventually runs, scroll snapping, and the accessibility rule that a scrollable region must be reachable by keyboard.
The Values, and Their Side Effects
Each does more than clip.
overflow: visible /* default — content spills, visibly */
overflow: hidden /* clipped, NOT scrollable — even by keyboard */
overflow: scroll /* always shows a scrollbar track */
overflow: auto /* scrollbar only when needed — usually correct */
overflow: clip /* clips without creating a scroll container */
/* Separate axes */
overflow-x: auto; overflow-y: hidden;
/* Note: setting one axis to a non-visible value forces the other to
'auto' — you cannot have visible on one and hidden on the other. */
/* Side effects of overflow != visible, all of which bite:
- creates a block formatting context (stops margin collapsing)
- BREAKS position: sticky for descendants
- becomes the containing block for scroll calculations */
/* overflow: clip avoids the scroll-container side effects, which
makes it the better choice for pure clipping: */
.mask { overflow: clip }
/* Text overflow */
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.clamp-3 { display: -webkit-box; -webkit-line-clamp: 3;
-webkit-box-orient: vertical; overflow: hidden }
.break { overflow-wrap: break-word } /* long URLs and tokens */The Horizontal Scrollbar Hunt
The usual suspects, and how to find the culprit in one line.
/* Find what is wider than the viewport */
document.querySelectorAll('*').forEach(el => {
if (el.getBoundingClientRect().right > document.documentElement.clientWidth)
console.log(el)
})
/* Or, visually */
* { outline: 1px solid red }
/* The usual causes:
width: 100vw — includes the scrollbar width on desktop,
so it is wider than the content area
a negative margin
position: absolute with a right offset outside the parent
an unbroken long string
a fixed-width image without max-width: 100%
a table wider than its container */
/* Global guards worth having */
img, video, svg { max-width: 100%; height: auto }
body { overflow-x: hidden } /* a plaster — it hides the symptom
and can break sticky. Find the cause. */
/* Prefer 100% to 100vw for full-bleed sections, or account for the
scrollbar: */
width: calc(100vw - (100vw - 100%));Scroll Behaviour and Accessibility
Snapping, smooth scrolling, and the rule people miss.
/* A scrollable region must be keyboard-reachable. Without tabindex,
a keyboard-only user cannot scroll it at all. */
<div class="table-wrap" tabindex="0" role="region" aria-label="Results">
/* Scroll snapping — a carousel with no JavaScript */
.carousel {
display: flex; gap: 1rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
scrollbar-width: thin;
}
.carousel > * { scroll-snap-align: start; flex: none; width: 80% }
/* Smooth scrolling, respecting the user's motion preference */
@media (prefers-reduced-motion: no-preference) {
html { scroll-behavior: smooth }
}
/* Offsets for a sticky header, so anchors do not land underneath */
html { scroll-padding-block-start: 5rem }
section { scroll-margin-block-start: 5rem }
/* overscroll-behavior stops a scroll inside a modal from continuing
into the page behind it — the "scroll chaining" annoyance */
.modal-body { overscroll-behavior: contain }
/* Styling scrollbars, without hiding them entirely */
.scroller { scrollbar-width: thin; scrollbar-color: var(--rule) transparent }
/* A hidden scrollbar removes the only visual signal that there is
more content. Style it; do not delete it. */Key Points to Remember
- 1overflow: hidden clips content with no way to reach it; auto is usually what was intended
- 2Any non-visible overflow creates a formatting context and breaks position: sticky for descendants
- 3A scrollable container needs tabindex and a label, or keyboard users cannot scroll it
- 4width: 100vw includes the scrollbar and is the most common cause of an unexpected horizontal scrollbar
- 5scroll-padding offsets anchors under a sticky header, and overscroll-behavior: contain stops scroll chaining out of a modal
Interview Questions
Sign in to ask AriaWhat is the difference between overflow: hidden, auto and clip?
How would you track down an unexpected horizontal scrollbar?
Why does a scrollable div need tabindex="0"?
Ask Aria about Overflow and Scrolling
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.