{"id":274810,"date":"2018-08-15T07:05:55","date_gmt":"2018-08-15T14:05:55","guid":{"rendered":"http:\/\/css-tricks.com\/?p=274810"},"modified":"2020-06-18T13:04:10","modified_gmt":"2020-06-18T20:04:10","slug":"practical-css-scroll-snapping","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/practical-css-scroll-snapping\/","title":{"rendered":"Practical CSS Scroll Snapping"},"content":{"rendered":"\n

CSS scroll snapping allows you to lock the viewport to certain elements or locations after a user has finished scrolling. It\u2019s great for building interactions like this one:<\/p>\n\n\n\n\n\n\n\n

\"\"
Live Demo<\/a><\/figcaption><\/figure>\n\n\n

Browser support and basic usage<\/h3>\n\n\n

Browser support for CSS scroll snapping has improved significantly since it was introduced in 2016<\/a>, with Google Chrome (69+), Firefox, Edge, and Safari all supporting some version of it.<\/p>\n\n\n

This browser support data is from Caniuse<\/a>, which has more detail. A number indicates that browser supports the feature at that version and up.<\/p><\/div>

Desktop<\/h4>
Chrome<\/span><\/th>Firefox<\/span><\/th>IE<\/span><\/th>Edge<\/span><\/th>Safari<\/span><\/th><\/tr><\/thead>
69<\/span><\/td>68<\/span><\/td>11*<\/span><\/td>79<\/span><\/td>11<\/span><\/td><\/tr><\/table><\/div>

Mobile \/ Tablet<\/h4>
Android Chrome<\/span><\/th>Android Firefox<\/span><\/th>Android<\/span><\/th>iOS Safari<\/span><\/th><\/tr><\/thead>
123<\/span><\/td>124<\/span><\/td>123<\/span><\/td>11.0-11.2<\/span><\/td><\/tr><\/table><\/div><\/div>\n\n\n\n

Scroll snapping is used by setting the scroll-snap-type<\/code><\/a> property on a container element and the scroll-snap-align<\/code><\/a> property on elements inside it. When the container element is scrolled, it will snap to the child elements you\u2019ve defined. In its most basic form, it looks like this:<\/p>\n\n\n\n

<div class=\"container\">\n  <section class=\"child\"><\/section>\n  <section class=\"child\"><\/section>\n  <section class=\"child\"><\/section>\n  <p>...<\/p>\n<\/div><\/code><\/pre>\n\n\n\n
.container {\n  scroll-snap-type: y mandatory;\n}\n\n.child {\n  scroll-snap-align: start;\n}<\/code><\/pre>\n\n\n\n

This is different to the first version of the spec<\/a>, which allowed you to set snap-points manually using the repeat<\/code> keyword:<\/p>\n\n\n\n

.container {\n  \/* OLD *\/\n  scroll-snap-points-y: repeat(300px);\n}<\/code><\/pre>\n\n\n\n

This method is pretty limited. Since it only allows evenly-spaced snap points, you can\u2019t really build an interface that snaps to different-sized elements. If elements change their shape across different screen sizes, you\u2019re also bound to run into issues.<\/p>\n\n\n\n

At the time of this writing, Firefox, Internet Explorer, and Edge support the older version of the spec, while Chrome (69+) and Safari support the newer, element-based method.<\/p>\n\n\n\n

You can<\/em> use both methods alongside each other (if your layout allows it) to support both groups of browsers:<\/p>\n\n\n\n

.container {\n  scroll-snap-type: mandatory;\n  scroll-snap-points-y: repeat(300px);\n  scroll-snap-type: y mandatory;\n}\n\n.child {\n  scroll-snap-align: start;\n}<\/code><\/pre>\n\n\n\n

I\u2019d argue a more flexible option is to use the element-based syntax exclusively and loading a polyfill<\/a> to support browsers that don\u2019t yet support it. This is the method I\u2019m using in the examples below.<\/p>\n\n\n\n

Unfortunately, the polyfill doesn’t come with a browser bundle, so it’s a bit tricky to use if you’re not using a build process. The easiest way around this I’ve found is to link to the script on bundle.run<\/a> and initializing it using cssScrollSnapPolyfill()<\/code> once the DOM is loaded. It\u2019s also worth pointing out that this polyfill only<\/em> supports the element-based syntax, not the repeat<\/code>-method.<\/p>\n\n\n

Parent container properties<\/h3>\n\n\n

As with any property, it\u2019s a good idea to get familiar with the values they accept. Scroll snap properties are applied to both parent and child elements, with specific values for each. Sort of the same way flexbox and grid do, where the parent becomes a “flex” or “grid” container. In this case, the parent becomes a snap container, if you will.<\/p>\n\n\n\n

Here are the properties and values for the parent container and how they work.<\/p>\n\n\n

scroll-snap-type \u201cmandatory\u201d vs. \u201cproximity\u201d<\/h4>\n\n\n

The mandatory<\/code> value means the browser has<\/em> to snap to a snap point whenever the user stops scrolling. The proximity<\/code> property is less strict\u2014it means the browser may<\/em> snap to a snap point if it seems appropriate. In my experience, this tends to kick in when you stop scrolling within a few hundred pixels of a snap point.<\/p>\n\n\n\n