CSS Scroll Snap, Smooth Scrolling and Multi-Column Layout

Build swipeable carousels with scroll-snap, smooth anchor scrolling with scroll-behavior and scroll-margin, and newspaper-style text columns with the columns property.

Lesson 49 of 49Intermediate16 min read

Some of the most app-like effects on the web come from scrolling. Swipe through a row of product cards on a phone and each card settles neatly into place. Click "Back to top" and the page glides up instead of jumping. These used to need JavaScript slider libraries; now CSS does them natively with scroll snap and smooth scrolling, and they feel better because they use the device's own scrolling physics.

This lesson also covers a quieter but useful layout tool: multi-column layout. With one property, columns, long text or a list of short items flows into newspaper-style columns that adjust to the screen width.

You'll build a swipeable carousel with no JavaScript, make in-page links scroll smoothly and land below a sticky header, and create responsive column layouts for text, link lists and masonry-style galleries. These features build on overflow and flexbox, which you already know.

Scroll snap basics

CSS
.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}
.carousel > * { scroll-snap-align: start; flex: 0 0 80%; }
  • On the scroll container, scroll-snap-type sets the direction (x, y or both) and the strictness.
  • mandatory always snaps to an item when scrolling stops. proximity only snaps when the user stops close to one, which is gentler for long content.
  • On each child, scroll-snap-align chooses which edge snaps: start, center or end.
  • scroll-snap-stop: always stops a fast swipe from skipping past items.

Smooth scrolling and scroll-margin

CSS
html { scroll-behavior: smooth; }
h2 { scroll-margin-top: 80px; }
  • scroll-behavior: smooth animates jumps to #anchor links and JavaScript calls like scrollTo().
  • If your site has a sticky header, linked headings land underneath it. scroll-margin-top on the targets adds space so they stop just below the header.
  • Wrap smooth scrolling in @media (prefers-reduced-motion: no-preference) so people who prefer less motion get instant jumps.

Multi-column layout

CSS
.article { columns: 3 220px; column-gap: 32px; column-rule: 1px solid #e5e7eb; }
  • columns: 3 220px means "up to 3 columns, each at least 220px wide". On a phone you get one column, with no media query.
  • column-gap and column-rule set the space and an optional line between columns.
  • break-inside: avoid on items stops a card or paragraph splitting across two columns.
  • column-span: all lets a heading stretch across every column.

Columns read top to bottom then left to right, so they suit short items such as link lists, tags and photo galleries. For long articles, scrolling down to the end of one column and back up to the next is awkward, so use them carefully.

CSS Scroll Snap Syntax

Scroll and columns

.track { display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 16px; }
.slide { flex: 0 0 80%; scroll-snap-align: start; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
:target, h2 { scroll-margin-top: 80px; }
.links { columns: 3 180px; column-gap: 24px; }
.links li { break-inside: avoid; }

CSS Scroll Snap Summary

  • scroll-snap-type on the container and scroll-snap-align on children make items settle into place.
  • A flex row with overflow-x: auto and snap points is a carousel with no JavaScript.
  • scroll-behavior: smooth animates anchor jumps; respect reduced motion.
  • scroll-margin-top stops linked headings hiding under sticky headers.
  • columns: 3 220px creates responsive multi-column layouts; use break-inside: avoid on items.

CSS Scroll Snap Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

A swipeable carousel with no JavaScript

Scroll or swipe sideways. Each card snaps into place.

.track {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding: 0 12px;
  padding: 0 12px 10px;
  overscroll-behavior-x: contain;
}

.slide {
  flex: 0 0 75%;
  scroll-snap-align: start;
  height: 140px;
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, #fde68a, #fb923c);
  border-radius: 14px;
  font: 700 18px sans-serif;
}
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 2 to 4A horizontal row that scrolls sideways.
  2. Line 5Snap on the x axis, always.
  3. Line 6Snapped cards line up with the 12px page padding.
  4. Lines 12 to 13Each card is 75% wide so the next one peeks in, and its start edge snaps.
Example 2

Smooth jumps below a sticky header

Click the links. The page scrolls smoothly and headings stop below the header.

.page {
  height: 260px;
  overflow-y: auto;
  scroll-behavior: smooth;
  font-family: sans-serif;
  border: 1px solid #e5e7eb;
}

.bar { position: sticky; top: 0; background: #111827; padding: 12px; }
.bar a { color: white; margin-right: 12px; }

h2 { scroll-margin-top: 56px; margin: 0; padding: 8px 12px; }
.filler { min-height: 200px; margin: 0; padding: 0 12px; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 4Jumps inside this box animate smoothly.
  2. Line 9The header stays at the top while scrolling.
  3. Line 12Each heading stops 56px from the top, just below the header.
Example 3

Responsive columns for links and cards

Narrow the panel and the columns drop from three to one.

.links {
  columns: 3 150px;
  column-gap: 24px;
  column-rule: 1px solid #e5e7eb;
  font-family: sans-serif;
}

.masonry { columns: 2 140px; column-gap: 10px; margin-top: 16px; }
.tile {
  break-inside: avoid;
  margin-bottom: 10px;
  display: grid; place-items: center;
  background: #e0e7ff; border-radius: 10px; font: 700 18px sans-serif;
}
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 2Up to three columns, each at least 150px.
  2. Line 4A thin line between columns.
  3. Line 8Tiles of different heights stack into columns, a simple masonry look.
  4. Line 10No tile is split across two columns.

Common CSS Scroll Snap Mistakes

Snapping on the children instead of the container

Don't
.slide { scroll-snap-type: x mandatory; } /* no effect */
Do
.track { scroll-snap-type: x mandatory; }
.slide { scroll-snap-align: start; }

Headings hidden under a sticky header

Don't
header { position: sticky; top: 0; height: 64px; } /* #section links land under it */
Do
header { position: sticky; top: 0; height: 64px; }
:target { scroll-margin-top: 72px; }

Long articles in columns

Don't
article { columns: 3; } /* readers scroll down and back up for every column */
Do
article { max-width: 65ch; }
.related-links { columns: 3 180px; }

CSS Scroll Snap in Real Projects

Where you'll see these

App stores, streaming services and online shops use snap carousels for rows of apps, shows and products. Documentation sites, including this one, use smooth scrolling with scroll-margin-top so table-of-contents links land neatly below the sticky header. Footers and site maps use multi-column lists, and image sites like Pinterest popularised the masonry look that columns approximates.

CSS Scroll Snap Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Make the container snap horizontally on every scroll.

.track {
  overflow-x: auto;
  scroll-snap-type: x ;
}
Multiple choiceeasy

Which property goes on each slide?

Multiple choicemedium

What does scroll-margin-top: 80px do on a heading?

Multiple choicemedium

What does columns: 3 200px mean?

Fix the bugmedium

The track should snap on the x axis, each slide should snap at its start, and links inside the page should scroll smoothly. Fix it.

.tr { display: flex; overflow-x: auto; }
.sl { flex: 0 0 80%; scroll-snap-type: x mandatory; }
html { scroll-behavior: auto; }
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Container snaps on x
  • Slides snap at the start
  • Smooth scrolling

CSS Scroll Snap Quiz

5 questions to check what you learned.

  1. What does scroll-behavior: smooth change?

    • Jumps to anchor links animate instead of happening instantly
    • Scroll speed with the mouse wheel
    • It hides scrollbars
    • It makes pages load faster
  2. When is proximity better than mandatory?

    • Never
    • For small carousels
    • When using columns
    • When snapped items can be taller than the screen, such as long sections
  3. Why make each slide about 80% wide?

    • It's required for snapping
    • It loads faster
    • The next slide peeks in, showing users they can swipe
    • It hides the scrollbar
  4. Which property stops a card being split between two columns?

    • column-span: none
    • break-inside: avoid
    • overflow: hidden
    • display: block
  5. How do you make a carousel usable with the keyboard?

    • It's not possible without JavaScript
    • Give the scroll container tabindex="0" and a label so it can be focused and scrolled with arrow keys
    • Use scroll-snap-stop
    • Add hover styles

CSS Scroll Snap Challenge

A gallery page

  1. Make .gallery a flex row that scrolls sideways (overflow-x: auto) with scroll-snap-type: x mandatory and gap: 10px
  2. Give .item flex: 0 0 70% and scroll-snap-align: center
  3. Give .notes columns: 2 160px and column-gap: 20px
  4. Give h2 a scroll-margin-top of 60px
.item { height: 100px; background: #dbeafe; }
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Gallery scrolls and snaps
  • Items are 70% wide and centre-snap
  • Notes use columns
  • Heading scroll margin