1. Introduction
We think scroll snapping is a great idea, and fully support exposing this functionality through CSS. However, a major weakness of the current spec is the way it conceives snapping on a coordinate model rather than a box model. This requires a lot of manual calculations in figuring out the correct coordinates from the box model; and also makes sensible scroll-snap settings dependent on the relative sizes of the viewport and the snappable contents, causing problems for users are unexpectedly large and/or small screens (a problem commonly ignored by many authors).
This proposal builds off of roc’s model, using an area-snapping model to intelligently handle adaptation to multiple screen sizes. It also adds group alignment as a built-in concept, rather than requiring authors to build one in JavaScript.
2. Use Cases
-
Snapping to 0.25rem above the top of each heading
:root { scroll-snap-type: proximity; } h1, h2, h3, h4, h5, h6 { scroll-snap-align: start; scroll-snap-area: 0.25em; }
-
Snapping to the center of each photo
:root { scroll-snap-type: mandatory; } img { scroll-snap-align: center; }
-
Snapping to the top of each "page" of address book entries in a list of entries
:root { scroll-snap-type: proximity; scroll-group-align: start; } article { scroll-snap-align: group; }
-
Snapping each flow chart entry to within the viewport when it falls near the edge:
:root { scroll-snap-type: proximity; } li { scroll-snap-align: edges; }
-
Snapping each city on a map to the center of the viewport, but only once it gets near the center in both dimensions:
:root { scroll-snap-type: proximity; } .city { scroll-snap-align: center; }
class="slides">.slides { display: flex; flex-flow: row; scroll-snap-type: mandatory; overflow-x: scroll; width: 100vw; height: 100vh; } .slide { scroll-snap-align: edges; width: 100vw; min-height: 100vh; } .slide.details { display: flex; flex-flow: column; scroll-snap-type: mandatory; overflow-y: scroll; }class="slide">...class="slide">...class="slide details">class="slide">...class="slide">...class="slide">...
3. Overview of Change
On the scroll container:
Spec | Proposal | Priority |
---|---|---|
scroll-snap-type | scroll-snap-type (no change) | High priority |
''scroll-snap-destination: | ''scroll-snap-destination: auto | | Keep this property only if needed for compat. |
scroll-snap-points-x | dropped | High priority to drop. :) |
scroll-snap-points-y | dropped | High priority to drop. :) |
n/a | ''scroll-group-align: [ start | end | edges ]{1,2} | | Low priority |
On the children:
Spec | Proposal | Priority |
---|---|---|
''scroll-snap-coordinate: | ''scroll-snap-align: [ none | start | end | edges ]{1,2} | | High priority; simpler version adds a center keyword to the bracketed set instead of allowing the full |
n/a | ''scroll-snap-area: [ border-box | margin-box ] || | High priority |
4. Element-based Snapping
4.1. Scroll Snapping Area: the scroll-snap-area property
Name: | scroll-snap-area |
---|---|
Value: | [ border-box | margin-box ] || |
Initial: | border-box |
Applies to: | all elements |
Inherited: | no |
Percentages: | n/a |
Media: | visual |
Computed value: | as specified, with lengths made absolute |
Animatable: | yes, if border-box / margin-box are constant |
Specifies the box that is used for snapping.
Note: This functionality effectively replaces scroll-snap-destination , in a way that allows for more control in mixed-content environments (each element can specify its own offsets from the viewport edges) and also plays better with smaller viewports (see §4.2.1 Snap Alignment in Small Viewports ).
4.2. Scroll Snapping Alignment: the scroll-snap-align property
Name: | scroll-snap-align |
---|---|
Value: | [ none | start | end | edges | center ] {1,2} |
Initial: | none |
Applies to: | all elements |
Inherited: | no |
Percentages: | n/a |
Media: | visual |
Computed value: | as specified |
Animatable: | no |
Specifies the element’s snap position as an alignment of its box within the viewport.
The first value gives alignment in the inline axis; the second value gives alignment in the block axis. If one value is specified, it is duplicated.
center keyword can be replaced by
4.2.1. Snap Alignment in Small Viewports
The snapped position of a box is given by its scroll-snap-align property. However,if the scroll-snap-area is larger than the viewport...
-
Inertial scrolling (and scroll-adjustment caused by snapping) continues to align to the snap-position as normal.
-
For explicit/programmatic (non-fling) scrolling:
-
While the area fully fills the viewport in a given axis, snapping is ignored in that axis: the container can be scrolled arbitrarily and will not react.
-
If the container is scrolled such that the area no longer fully fills the viewport in an axis, the area acts as if it has both-edges snapping in that axis, resisting outward scrolling until you fling out or pull it sufficiently to trigger snapping to a different snap-position (with either proximity or mandatory behavior as appropriate).
-
As an example, imagine a photo as the area, or a slide in a slideshow. You want mandatory snapping from item to item, but if the item happens to be larger than your viewport, you want to be able to scroll around the whole thing once you’re over it.
5. Scope of Snapping Influence
Current spec doesn’t define how to select which snap-point to snap to. See https://lists.w3.org/Archives/Public/www-style/2015Jul/0325.html for a proposal to ignore snap positions far outside the viewport.
UAs should be encouraged to ignore snap positions that require scrolling in two dimensions when a one-dimensional scroll is triggered.
Define that snap-point selection is based on the final scroll position that the scroll physics would land the scroller in after a fling.
6. Group-based Snapping
Collects all snapping areas of group-snapped boxes, segments them into groups that will fit within the viewport, then creates snap areas that capture the specified alignment of each such group. (Note that such areas may overlap, if group-snapped boxes are arranged in an overlapping pattern.)
This is a simple form of "scrolling by pages".
:root { scroll-snap-type: proximity; scroll-group-align: start; } article { scroll-snap-align: group; }
article { scroll-snap-type: proximity; scroll-group-align: start; } article > * { scroll-snap-align: group; }
.gallery { scroll-snap-type: proximity; scroll-group-align: center; } .gallery > img { scroll-snap-align: group; }
6.1. Turning On Group Snapping: the group value of scroll-snap-align
Name: | scroll-snap-align |
---|---|
New values: | group |
The group value specifies that this element’s scroll snap area should be group-aligned to the viewport.
6.2. Aligning the Group: the scroll-snap-group property
Name: | scroll-snap-group |
---|---|
Value: | [ start | end | edges | center ] {1,2} |
Initial: | start |
Applies to: | all elements |
Inherited: | no |
Percentages: | n/a |
Media: | visual |
Computed value: | as specified |
Animatable: | no |
Specifies the alignment of a group-snapped group’s area within the viewport.