CSS scroll snap – Scroll Horizontally

Alvaro Trigo Avatar

Follow on Twitter

CSS Snap Scroll, when used effectively, can help to improve the user scrolling experience in websites made out of different full-screen sections or accessed via touch devices. And of course, it can also be applied horizontally! If you want a fullscreen horizontal website, this can be done with CSS snap scroll! We’ll be explaining in this article how to it.

If you are on a desktop make sure to press SHIFT while you use the mouse wheel (in order to scroll horizontally) or use the trackpad and swipe horizontally. On touch screen devices swiping horizontally as expected will do. So, as you can see, this is an ideal solution specially for touch or mobile devices. If you need a full compatibility desktop solution look no further, go with fullpage.js. It has an option that scroll horizontally when using the mouse-wheel/trackpad.

What is CSS Snap Scroll?

CSS Snap Scrolling is simply a way of forcing the scroll to behave in a very specific or precise manner: once a user has finished scrolling, via snap scroll you can make sure that the scrollbar stops at the place you want it to stop. This restrictive scroll is very effective in touch devices to make sure the scrollbar ends at the right spot.

Creating the scroll container

First off we need a container element that wraps the sections that the scroll will be attracted to. This element is called a “scroll container” in the context of CSS Snap Scroll. In this tutorial, we will be using a main element.

Inside, we add our sections:

    <main>
      <section>
        <h1>Never gonna give you up</h1>
      </section>
      <section>
        <h1>Never gonna let you down</h1>
      </section>
      <section>
        <h1>Never gonna run around and desert you</h1>
      </section>
    </main>
Code language: HTML, XML (xml)

Implementing snap scroll horizontally

Next, we will implement the effect by setting the height of thee html and body elements to 100%. This is important because this tutorial aims to make all the section elements scale to full height. The percentage unit relies on the parent’s dimensions, so the body and main must have height: 100% if we want the sections to expand full-height.

html,
body {
  font-family: sans-serif;
  margin: 0;
  height: 100%;
}
Code language: CSS (css)

To turn an ordinary container element into a “scroll container”, we set the property scroll-snap-type to a pair of values that follow the syntax <x|y|both> <mandatory|proximity>. The latter of the 2 describes how often the snap should happen while the first one describes which direction the snap should happen in. CSS Snap scroll only works if overflow is set to auto or scroll in the direction(s) described in scroll-snap-type.

This tutorial focuses on how to create horizontal scrolling, so the scroll direction would be along the x axis. To make the effect easier to spot at all times we will use mandatory.

We also use CSS3 Flexbox to align each section in main horizontally by setting display to flex.

main {
  height: 100%;
  display: flex;
  /* important */
  overflow-y: hidden;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}
Code language: CSS (css)

Finally, the next CSS snap scroll-specific property that needs to be set is scroll-snap-align. This property describes where exactly the snap should take place relative to the direction in which scrolling happens. You could set it to one of 3 values: start, center, and end. When set to start (while scrolling horizontally) the point is at the top-left edge of the viewport.

The rest of the styles are merely aesthetics. We use CSS3 Flexbox to center the h1 tags horizontally and vertically, make sure the section takes up the full width and height, and set the font-size for the h1 tags.

The nth-child() selector is also used to style each odd section, this selector can be useful if your elements have some styling that is applied according to a specific pattern.

section {
  /* aesthetics */
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  min-width: 100vw;
  font-size: 3.5ch;
  /* important */
  scroll-snap-align: start;
}

section*:nth-child*(odd) {
  background: black;
  color: white;
}
Code language: CSS (css)

Of course, feel free to improve the reusability of these styles by simply extracting the important bits out of the styles written above like this:

.snap-scroll-horizontal {
  overflow-y: hidden;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

.snap-child-at-start {
  scroll-snap-align: start;
}
Code language: CSS (css)

Then simply add the classes to the elements appropriately alongside any other styling.

Excluding the scrollbar width from the 100vh

If you are familiar with viewport units like vh or vw you might be confused as to why we didn’t simply set height to 100vh earlier. The reason is that viewport units have not been built with an awareness of the scrollbar. By default, the viewport includes the scrollbar.

To get around this you could sprinkle a bit of Javascript to get the width of the scrollbar and then make it available to CSS via _calculateScrollbarWidth variable. This CSS variable is recalculated every time the DOM loads, resize, and when the whole page loads fully.

To get the width of the scrollbar we subtract the width of the document without the scrollbar from the width of the page with the scrollbar.

function _calculateScrollbarWidth() {
  document.documentElement.style.setProperty(
    '--scrollbar-width',
    window.innerWidth - document.documentElement.clientWidth + 'px'
  );
}

document.addEventListener('DOMContentLoaded', _calculateScrollbarWidth, false);
window.addEventListener('load', _calculateScrollbarWidth);
window.addEventListener('resize', _calculateScrollbarWidth, false);
Code language: JavaScript (javascript)

Then you can readily use the available CSS variable to set the height of the section via vh.

section {
  /* height: 100%; */
  height: calc(100vh - var(--scrollbar-width));
}
Code language: CSS (css)

And here’s our codepen with our hozizontal snap scroll website:

Conclusion

If you are now a fan of snap scrolling (as we are!), then you will be delighted to know that all the cumbersome issues that come with compatibility among browsers and devices are handled for you via one neat well-documented, and tested Javascript library called fullPage. Specially if you want a desktop solution that scrolls horizontally while scrolling vertically with the mouse/trackpad as mentioned above.

In fact, once you start playing with this kind of layout, you’ll quickly realize you’ll want to do many more things. Things like fire events to trigger animations, use conditional styles based on the active sections, or even change the URL depending on which section is visible. fullPage.js provides you with all of this (and much more) out of the box in addition to horizontal snap scroll. It also comes with support for your favorite front-end frameworks like React, Vue, or Angular and WordPress web builders like Elementor or Gutenburg. So totally a must-check if you are into snap scrolling!

Was this page helpful?