Overflow and scrolling: overflow, scroll snap and smooth scrolling

Handle content that does not fit, truncate text, build scroll-snapping rows, and control scroll behaviour.

  • Beginner
  • 8 min read
  • Updated 17 Oct 2026

What you will learn

  • Contain overflowing content
  • Truncate text with an ellipsis
  • Build a scroll-snap row
  • Control smooth scrolling and scroll chaining

Content does not always fit the box you give it. A long word, a wide table or a fixed-height panel can all overflow. CSS lets you decide what happens: show it, clip it, or make the box scroll. It also lets you shape how scrolling feels.

What overflow looks like

CSS
.box {
    width: 12rem;
    height: 6rem;
    border: 1px solid #d9e2ec;
}

If the text inside needs more than 6rem, it spills out of the bottom border and over whatever comes next. That is the default, overflow: visible. CSS chooses to show overflowing content rather than hide it, so that nothing is ever lost silently.

The best fix is often to remove the fixed size (use min-height). When a box genuinely must stay a certain size, use overflow.

The overflow values

ValueOverflowing contentScrollable?
visible (default)Shown outside the boxNo
hiddenClippedNot by the user, but by script or focus
clipClippedNo, never
scrollClippedYes, scrollbars always shown
autoClippedYes, scrollbars only when needed

Use overflow-x and overflow-y to control each direction separately:

CSS
.code-sample {
    overflow-x: auto;   /* horizontal scrollbar only if the line is too long */
}

.chat-log {
    max-height: 20rem;
    overflow-y: auto;
}

auto is almost always better than scroll, which shows empty scrollbars on some systems even when nothing overflows.

hidden versus clip

Both clip content. The difference is subtle but important:

  • overflow: hidden makes the element a scroll container. It can still be scrolled with JavaScript or when focus moves into hidden content, and it breaks position: sticky for descendants.
  • overflow: clip simply clips. It does not create a scroll container, so sticky descendants keep working.

Use overflow: clip when you only want to trim decorative overflow, for example an image poking out of rounded corners. Reach for hidden when you need its side effect of creating a new formatting context, which also contains floats and margins.

Making scroll areas accessible

A scrollable region must be usable by keyboard. Some browsers make scroll containers focusable automatically, but not all. If a region scrolls and contains no focusable elements, make it focusable and give it a name:

HTML
<div class="table-wrapper" tabindex="0" role="region" aria-label="Opening hours by branch">
    <table>...</table>
</div>
CSS
.table-wrapper {
    overflow-x: auto;
}

.table-wrapper:focus-visible {
    outline: 3px solid #f0b429;
}

Keyboard users can now tab to the table and scroll it with the arrow keys. Responsive patterns for wide tables and embeds are covered in the Responsive Web Design course.

Truncating text

One line with an ellipsis

CSS
.file-name {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

All three declarations are needed: white-space: nowrap keeps the text on one line, overflow: hidden clips it, and text-overflow: ellipsis adds the "…". The element also needs a constrained width. Inside a flex item, remember min-width: 0, or the item will grow instead of truncating.

Several lines

CSS
.summary {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
}

This clamps the text to three lines with an ellipsis. The prefixed syntax looks odd, but it is the form that works across all current browsers. An unprefixed line-clamp property is specified but not yet broadly supported.

Truncated text is still in the HTML, so screen readers read it all, but sighted users cannot. Never truncate information people need, such as prices or error messages. Provide a way to see the full text, like a link to the full article.

Breaking long words

Long URLs and unbroken strings can overflow narrow columns. Allow them to break:

CSS
.comment {
    overflow-wrap: anywhere;
}

Scroll snap

Scroll snapping makes a scroll container settle on specific positions after the user scrolls, like a carousel or a gallery. It needs two parts: the container and its children.

HTML
<ul class="carousel">
    <li class="slide">...</li>
    <li class="slide">...</li>
    <li class="slide">...</li>
</ul>
CSS
.carousel {
    display: flex;
    gap: 1rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: 1rem;
    padding: 0 1rem 1rem;
    margin: 0;
    list-style: none;
}

.slide {
    flex: 0 0 80%;
    scroll-snap-align: start;
}
  • scroll-snap-type: x mandatory snaps horizontally and always settles on a snap point. proximity snaps only when the user stops close to one.
  • scroll-snap-align on each child sets which edge snaps: start, center or end.
  • scroll-padding on the container offsets the snap position, here keeping 1rem of space at the start.

Setting each slide to 80% of the width lets the next one peek in, which tells users there is more to scroll. This is pure CSS: touch, trackpad, mouse wheel and keyboard scrolling all work.

mandatory can trap content: if a slide is taller or wider than the scroll container, parts of it may become unreachable. Use proximity when slide sizes vary, and test with large text sizes.

Smooth scrolling

scroll-behavior: smooth animates jumps to in-page anchors and scripted scrolling:

CSS
@media (prefers-reduced-motion: no-preference) {
    html {
        scroll-behavior: smooth;
    }
}

Wrap it in a reduced-motion query. A long animated scroll down a page is exactly the kind of motion some people need to avoid.

Stopping content hiding under sticky headers

When a link jumps to a section, a sticky header can cover the heading. scroll-margin-top on the target, or scroll-padding-top on the scroll container, adds an offset:

CSS
html {
    scroll-padding-top: 5rem;   /* the height of the sticky header, plus a little */
}

Controlling scroll chaining

When you scroll to the end of a scrollable panel, the browser starts scrolling the page behind it. This is scroll chaining. In modals, side panels and chat windows it is usually unwanted:

CSS
.modal-body {
    overflow-y: auto;
    overscroll-behavior: contain;
}

contain stops the scroll from passing to the parent and also prevents the bounce or pull-to-refresh effect on mobile. none does the same.

Avoiding layout jumps from scrollbars

On systems with classic scrollbars, content shifts sideways when a scrollbar appears. scrollbar-gutter reserves the space in advance:

CSS
.panel {
    overflow-y: auto;
    scrollbar-gutter: stable;
}

It is a newer property; check its Baseline status. Where unsupported, it is ignored and you get the old behaviour.

Finding the cause of horizontal scrolling

An unexpected horizontal scrollbar on the whole page is one of the most common layout bugs.

  1. Scroll the page sideways and note which part sticks out past the right edge. Inspect it in DevTools.
  2. Temporarily add * { outline: 1px solid red; } in the Styles pane to see every box.
  3. Look for fixed widths, 100vw, negative margins, long unbroken strings and flex items without min-width: 0.

Avoid "fixing" it with body { overflow-x: hidden; }. That hides the symptom, can break position: sticky, and leaves content cut off. Find the element that is too wide and fix it.

Summary

  • overflow decides whether extra content is shown, clipped or scrollable; prefer auto over scroll.
  • hidden creates a scroll container; clip does not, so sticky elements keep working.
  • Make scrollable regions keyboard-focusable and named when they contain no focusable elements.
  • Truncate one line with text-overflow: ellipsis, several lines with the prefixed -webkit-line-clamp.
  • Build carousels with scroll-snap-type and scroll-snap-align; wrap smooth scrolling in a reduced-motion query.

Further reading