CSS Smooth Scrolling

Last year, while building the FilePond product page I stumbled upon the scrollIntoView API. It’s a handy method to instruct the browser to scroll an element into the viewport.

The scrollIntoView API can be instructed to animate the scrolling part by adding the behavior property on the scrollIntoViewOption object.

element.scrollIntoView({ behavior: 'smooth' });

I quickly jumped on my JavaScript horse and wrote a tiny script to automatically detect clicks on anchors so the browser would animate the jump towards the anchor target. This jump can be really disorienting, so animating this process would improve the user experience quite a bit.

scrollIntoViewOption currently only works on Firefox and Chrome.

// Everytime someone clicks on something
document.body.addEventListener('click', e => {
  
  const href = e.target.href;
  
  // no href attribute, no need to continue then
  if (!href) return;
  
  const id = href.split('#').pop();
  const target = document.getElementById(id);
  
  // no target to scroll to, bail out
  if (!target) return;
  
  // prevent the default quick jump to the target
  e.preventDefault();
  
  // set hash to window location so history is kept correctly
  history.pushState({}, document.title, href);
  
  // smooooooth scroll to the target!
  target.scrollIntoView({
    behavior: 'smooth',
    block: 'start'
  });

});

Live demo

This works wonderfully.

I posted it on Twitter and called it a day.

Then, Hans Spieß points out that this can also be done with CSS, WHAT!?

Turns out there’s a scroll-behavior CSS property that we can set to smooth, it’s literally that literal. It’s almost like awesome: yes-please. We can set the scroll-behavior property to the container we want to exhibit smooth scroll behavior and we’re done.

I created a new demo on CodePen using only CSS.

Smooth scroll comparison

Before we go nuts and apply this to all our sites, there are a couple of things to keep in mind.

Scroll distance matters

If there is a lot of content to scroll, Firefox will skip content to keep the scroll time-limited, while Chrome has a max velocity and will just take its time to get to the target.

Long distance scroll comparison

Live Demo on CodePend

We could use Smart CSS to detect long pages and conditionally apply the smooth scroll style.

Accessibility

People might get motion sickness when watching the animation. To circumvent this you can wrap the CSS property in a prefers-reduced-motion media query. Unfortunately, Chrome does not support this. Safari supports it, but Safari doesn’t support smooth scrolling.

.my-smooth-container {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  .my-smooth-container {
    scroll-behavior: auto;
  }
}

Conclusion

When deciding on a new functionality we shouldn’t reach out to JavaScript immediately. We should do a quick search first to find out if it can be done with CSS as well. The scroll-behavior property can be a nice UX improvement, do make sure you disable it on very long pages and offer an option to disable it to keep your pages accessible.

Rik Schennink

Web enthusiast

to pqina.nl