Native CSS Scroll Snap Points

Serg Hospodarets Blog

Scrolling effects are increasingly popular these days.

Scroll snapping is used widely for a better separation of the provided content (vertical full height pages) or, for example, to emulate galleries behavior.

There are many popular plugins (fullPage.js, jQuery.panelSnap, jQuery Scrollify etc.) which provide such functionality.

Can you imagine how easy would be creating such effects using CSS only?

Meet the CSS Scroll Snap Points specification!

Demo

Description

scroll-snap-type for a scrolling container

First- you have to set how strictly are snap points used. For that use:

scroll-snap-type: none | mandatory | proximity;

mandatory sets that scroll has to be always on the closest snap poin- in the end of scrolling, on adding content etc.

proximity means that scroll may come to rest on a snap point. There is no clear description of that behavior, so if you want to be sure your Scroll Snap Points work- use mandatory

scroll-snap-points-x and scroll-snap-points-y for a scrolling container

These rules set the positioning of snap points inside a scroll container. Syntax is:

scroll-snap-points-x: none | repeat(< length >)
scroll-snap-points-y: none | repeat(< length >)

repeat(< length >) is measured from the start of a container and provides an interval at which snap points are defined.

E.g. scroll-snap-points-x: repeat(100%); defines that scroll has to be snapped after the each length of the container horizontally.

scroll-snap-points-y: repeat(100px); sets scroll to snap each 100px vertically etc.

scroll-snap-destination for a scrolling container

Defines the position within the scrolling container which snap points align with.

It means, that scroll-snap-destination: 50% 50%; sets all snapping points to be aligned with the center of a container.

Default value is: scroll-snap-destination: 0px 0px; - akigning with a starting point of a container.

scroll-snap-coordinate for items inside of a scrolling container

For an element you can define the Snap Point which would be aligned with the closest element with a scroll. In general it looks like:

scroll-snap-coordinate: none | < position >;

You can set it as the following:

.item{
    /*
    Aligns the center of the .item
    with a scroll container snap-destination.
    */
    scroll-snap-coordinate: 50% 50%;
}

Demo

For the demo I decided to implement one page with the galleries and another one- with the full page vertical scrolling effect.

Scrolling galleries

Demo

There is a block of common CSS rules for all galleries:

.gallery {
    /*
    Enables the Scroll Snapping,
    saying it is mandatory (hard snap)
    */
    scroll-snap-type: mandatory;

    /*
    Make a Native Smooth Scrolling work: blog.hospodarets.com/native_smooth_scrolling/
    After that browser starts to scroll blocks
    when you use anchors: http://help.typepad.com/anchor-tags.html
    It adds smooth scrolling instead of "jumping"
    when you press number of a slide to scroll to.
    */
    scroll-behavior: smooth;
}

Horizontal gallery is implemented using the following CSS rules:

.horizontal-gallery {
    overflow-x: auto;
    overflow-y: hidden;
    white-space: nowrap;

    /*
    Sets the scroll snapping to the each point which is
    on the X*GALLERY_WIDTH distance horizontally from start.
    Images width are the same as containers.
    That's why the scroll snaps to the beginning of the each image.
    */
    scroll-snap-points-x: repeat(100%);
}

Vertical gallery implementation is similar, but vertical repeat is used instead:

.vertical-gallery {
    overflow-y: auto;
    overflow-x: hidden;

    /*
    Sets the scroll snapping to the each point which is
    on the X*GALLERY_HEIGHT distance vertically from start.
    Images height are the same as containers.
    That's why the scroll snaps to the beginning of the each image.
    */
    scroll-snap-points-y: repeat(100%);
}

“People You May Know” gallery shows a different type of snapping: snapping to the center/left of the image. Similar effect (showing part of the nearby images) is used, e.g. in the Facebook “People You May Know” block.

In these examples Scroll Snapping alignement is set to the left/center of the gallery and can be switched using the checkboxes.

.youmayknow-gallery {
    overflow-x: auto;
    overflow-y: hidden;
}

/*
SCROLL SNAPPING ALIGNMENT
*/
/* CENTER */
#align-to-center:checked ~ .gallery-wrapper .youmayknow-gallery{
    /*
    Defines that each snap coordinate has to be aligned
    with the center of the gallery wrapper
    */
    scroll-snap-destination: 50% 50%;
}

#align-to-center:checked ~ .gallery-wrapper .item{
    /*
    Sets the center of each gallery item
    as the Scroll Snapping point
    */
    scroll-snap-coordinate: 50% 50%;
}

/* LEFT */
#align-to-left:checked ~ .gallery-wrapper .youmayknow-gallery{
    /*
    Defines that each snap coordinate has to be aligned
    with the left edge of the gallery wrapper
    */
    scroll-snap-destination: 0 50%;
}

#align-to-left:checked ~ .gallery-wrapper .item{
    /*
    Sets the left edge of each gallery item
    as the Scroll Snapping point
    */
    scroll-snap-coordinate: 0 50%;
}

Full page height blocks vertical scrolling

Another popular examples of using Scroll Snap is a full page height block scrolling.

For eaxample, check the jQuery Scrollify Demo.

Let’s implement the same functionality using CSS only. Common HTML structure:

<div class="box-wrapper">
    <div class="box-bg">
        <div id="screen1" data-section-name="screen1" class="box">
            Screen 1
        </div>
        ...
    </div>
</div>

Let’s provide for boxes the same height as the browser window + a vertical scroll:

.box-wrapper {
    overflow-x: hidden;
    overflow-y: auto;
}

.box {
    height: 100vh;
}

And the last one- just add Scroll Snapping to the beggining of the each box:

.box-wrapper {
    scroll-snap-type: mandatory;
    scroll-snap-points-y: repeat(100%);
}

That’s it, we have set up a vertical full page height blocks with a scroll snapping.

All the other additions you can add on top of it:

  • Specific %ID% to each block to use then links with href="%ID%" to scroll to that box
  • scroll-behavior, smooth; to make the above scrolling smooth (check the Smooth Scrolling article for details)
  • Using the links from the item 1 you can add boxes switcher and specific links like “scroll to the next slide”/”scroll to top”..

As always, to make it work in browsers without a Snap Point support- the JavaScript Plugin is used (jQuery Scrollify):

var isScrollSnapSupported = 'scrollSnapType' in document.documentElement.style ||
    'webkitScrollSnapType' in document.documentElement.style;

if (!isScrollSnapSupported) {
    $.scrollify({
        section: '.box'
    });
}

Demo

How to detect Scroll Snap is supported in a browser

It’s quite easy- for JavaScript:

var isScrollSnapSupported = 'scrollSnapType' in document.documentElement.style ||
        'webkitScrollSnapType' in document.documentElement.style;

if (!isScrollSnapSupported) {
    // APPLY SOME PLUGIN TO EMULATE THE SAME BEHAVIOR
}

All browsers which have the new CSS Scroll Snap bahavior (in Internet Explorer it was implemented with an old syntax) also support CSS feature detection (CSS Feature Queries). So we can use @support CSS at-rule:

@supports ( (scroll-snap-type: mandatory) or (-webkit-scroll-snap-type: mandatory) ) {
    /* SCROLL SNAP or some specific rules */
}

@supports ( not ((scroll-snap-type: mandatory) or (-webkit-scroll-snap-type: mandatory)) ) {
    /* rules to be applied when SCROLL SNAP is NOT supported */
}

Browser support

It is interesting that Scroll Snap Points were first implemented by Microsoft in IE10 (works in IE10+) (but with another syntax).

Then Firefox team announced intent to implement the support (FF 39+), Chrome and, finally, Apple also announced their support in Safari 9.0!

You might be surprised, but it is already implemented in Firefox stable and many next versions of browsers:

If you want to play with Scroll Snap Points today, you can either download Webkit Nightly or just run Firefox.

Don’t forget to provide a -webkit vendor prefix for Safari.

Bugs/missed parts

  • In Safari when you scroll using a scrollbox (not a wheel)- scroll snapping doesn't work, scroll just stops in the same place.
  • There is no clear way to set a scroll snap points to the end of a scrollable content. That's why you might have problems with the last items in your scrollable areas (e.g. manual scrolling in FF).

    For the demo it was fixed (thanks to the BigBossSNK comment) providing:

    .youmayknow-gallery .item:last-child{
        scroll-snap-coordinate: 0 50%;
    }
  • In Firefox when scroll-snap-points-y: repeat(100%); is used for a gallery (when you have images as items in a scroll area), scroll snap points are defined to the end of the real images height (not the visual sizes which are set by CSS).

    This behavior is reproducible in the Vertical gallery.

Links

Provide your code in
        <pre><code class="{language}"></code></pre>
        
tags