Scroll Snapping with CSS Snap Points

Many of you, as web developers, are trying to accomplish sophisticated scrolling effects such as paginated scrolling. WebKit now supports paginated scrolling through CSS Snap Points. Though some JavaScript scrolling libraries already allow you to implement paginated scrolling on your websites and web views, these approaches have important weaknesses – first, they run JavaScript on every frame of a scrolling animation, which WebKit’s fast scrolling infrastructure cannot optimize. Second, the current event model standard does not expose trackpad scrolling phases, making it difficult for you to get optimal paged scrolling using JavaScript. Fortunately, with scroll snapping, you won’t need information about scrolling phases to implement great paginated scrolling.

New CSS Properties

Scroll snapping allows you to define special points in the content of a scrollable container using CSS. When a user scrolls in this container, the scroll offset will come to rest at one of these special offsets. To motivate snap scrolling, consider this horizontally scrolling gallery:

Notice how scrolling can leave us at an awkward position where we see a sliver of one image and only part of the next. Let’s look at how we can use the new scroll-snap-* properties to achieve paginated scrolling. Note that the examples shown in this post are all available in this scroll snapping gallery.

Scroll Snap Type

By default, scroll-snap-type on the parent scrolling container is none, which indicates that the container opts out of any scroll snapping behavior. To opt in, we set the value to be mandatory, which guarantees that the visual viewport of the container will rest on a snap point when there are no active scrolling operations. Also note that another value for scroll-snap-type is proximity, which indicates that scrolling may come to rest on a snap point only if scrolling takes us near the snap point. Currently, WebKit only supports mandatory snapping.

Scroll Snap Points-X and -Y

The scroll-snap-points-x and scroll-snap-points-y properties determine how to place snap points along the horizontal and vertical axes of the scroll snap container, respectively. The values here are none (the default) or repeat(length), where a CSS length unit indicates any type of expression indicating a length. These include raw pixel values, viewport units, percentages (relative to the container’s padding box) and even calc() expressions. Please note that negative repeat() values are handled as parsing errors, and any value that comes out to less than 1px is clamped to 1px. A repeat() value indicates that snap points should be placed at regular intervals along the x or y axes, respectively. Using the above two properties, here is an example of mandatory scroll snapping with horizontal snap points at every 100% of the container width using repeat(100%). If you’re on a browser that supports scroll snapping, you can also check out Scroll Snapping Demo 1.

Each snap point is located at the top left corner of each image. Scroll snapping makes each snap point come to rest at the top left corner of the container, marked by the blue cross.

Scroll Snap Coordinate

If our images are different sizes, we won’t be able to use repeat(), since the interval will vary from image to image. Instead, we can use scroll-snap-coordinate on a child element to generate one or more snap points relative to each child element. This property accepts either “none” or a space-separated list of coordinates. Each coordinate is of the form length length, representing the x and y positions of the coordinate, respectively. Just like before, each length is generalized to match any generic length value, and percentage-based length values use the dimensions of the child element’s border-box. The following example places a snap coordinate at each child element’s top left corner, allowing us to snap to unevenly spaced elements. See Scroll Snapping Demo 2.

Scroll Snap Destination

Another way to improve the gallery would be to center each image relative to the container. Up until now, we’ve been aligning each snap point to the top left corner of the parent container. However, by changing scroll-snap-destination, we’re able to manipulate where snap points animate to. The destination value is a single position consisting of two space-separated lengths. Percentage-based values are computed relative to the padding box of the scroll snapping container. We use this property in conjunction with scroll-snap-coordinate on each child element to animate each child element to the center of the container. See Scroll Snapping Demo 3.

Notice that the position of the blue cross is now in the center of the container, since we set the destination to 50% 50%. The snap points are also now in the center of each child element, so the center of each child element now comes to rest at the center of the container.

Additional Details

Our scroll snapping implementation supports 2D snapping as well, when scroll snap points in both axes are active or when elements are positioned in a grid-like fashion in a container. Scrolling diagonally animates the scroll offset on a curved path to its destination. See Scroll Snapping Demo 4.

We also support scroll snapping on containers to which CSS transformations have been applied. See Scroll Snapping Demo 5.

Standards Compliance and Future Work

We currently only support the most complete part of the snap points specification: 2D scroll snapping to child elements that are aligned on a grid. This means you may find more snap points than expected if your 2D scrolling container contains scroll snapping elements of uneven sizes, such as in a masonry grid layout. The CSS Scroll Snap Points specification is still a work in progress. We are considering the CSS Scroll Snapping Change Proposal. Once a consensus is reached we will either implement the alternative scrolling model, or implement the proximity type for scroll snapping, tracked by bug 135994. Since the specification is still in flux, we have prefixed our implementation with -webkit in keeping with the W3C requirements for features that have not completed the standardization process. We intend to unprefix our scroll snapping properties once the specification approaches its final state. The spec also does not explicitly state how to handle unreachable snap points. Our current implementation clamps all x and y offsets of each snap point to the min and max scroll offsets of the scroll snapping container. Furthermore, to prevent content from being unreachable, we emit snap points at the min and max scroll offsets of the container as well. You can use scroll snapping on the nightlies, as well as Safari on OS X El Capitan and iOS 9. We hope you’ve enjoyed reading about scroll snapping, and we’re excited to see what you create with it!