1. Introduction
This section is not normative.
This is currently a draft spec over Scroll Snap 1.
Scroll experiences don’t always start at the beginning. Interactions with carousels, swipe controls, and listviews often start somewhere in the middle, and each require JavaScript to set this position on page load. By enabling CSS to specify this scroll start position, both users, page authors and browsers benefit.
In addition to setting an initial scroll position, developers need insights and events into Scroll Snap. Events like which element is snapped on which axis, when the snap event is changing, when snap completes and conveniences for snapping to children programmatically.
1.1. First Layout
This event should follow the Animation code path. When animation objects are created and fire events, this is when a box has it’s first layout.
2. Motivating Examples
.carousel{ overflow-inline : auto; scroll-start : center; }
< div class = "carousel" > < img src = "img1.jpg" > < img src = "img2.jpg" > < img src = "img3.jpg" > < img src = "img4.jpg" > < img src = "img5.jpg" > </ div >
.scrollport{ overflow-block : auto; } main{ scroll-start-target : auto; }
< div class = "scrollport" > < nav > ...</ nav > < main > ...</ main > </ div >
3. Setting Where Scroll Starts
3.1. The scroll-start property
Name: | scroll-start |
---|---|
Value: | [ auto | start | end | center | left | right | top | bottom | <length-percentage [0,∞]> ]{1,2} |
Initial: | see individual properties |
Applies to: | see individual properties |
Inherited: | see individual properties |
Percentages: | see individual properties |
Computed value: | see individual properties |
Animation type: | see individual properties |
Canonical order: | per grammar |
This property is a shorthand property that sets all of the scroll-start-* longhands in one declaration. The first value defines the scroll starting point in the block axis, the second sets it in the inline axis. If the second value is omitted, it defaults to start. If scroll-start-target is set on any child, scroll-start is not used, in favor of using the element as the offset.
Values are defined as follows:
- auto
- ...
- <length-percentage [0,∞]>
-
...
Negative values are invalid. Values corresponding to a length greater than the width/height of the scrollport are valid, but clamped to the width/height of the scrollport.
- start
- center
- end
- center
- Equivalent to 0%, 50%, and 100%, respectively.
3.1.1. Interaction with display: none and initial creation
Same behavior that animations follow with first layout.3.1.2. Slow page loading or document streaming behavior
TODO3.1.3. Interaction with fragment navigation
TODO If the scrollport has a in-page :target via a URL fragment or a previous scroll position, then scroll-start is unused. Existing target logic should go unchanged. This makes scroll-start a soft request in the scroll position resolution routines.3.1.4. Interaction with place-content
TODO Side note: While place-content can make a scroller appear to start in the center or end, no browser supports it and it appears complicated to resolve.3.1.5. Interaction with "find in page"
TODO3.1.6. Interaction scroll-snap containers
This effectively will layout and start scroll at the snapped child, thus negating / cancelling scroll-start. scroll-start will only work if nothing else has effected the scroll position.3.1.7. Nested scrollers with scroll-start
Should follow patterns that scroll snap has set.3.1.8. Interaction when display is toggled
Same behavior that animations follow with first layout.3.1.9. Interaction with RTL and LTR
Logical properties are offered for length offsets that should be flow relative. Furthermore, the end and start keywords are always logical.3.2. The scroll-start-target property
Name: | scroll-start-target |
---|---|
Value: | [ none | auto ]{1,2} |
Initial: | see individual properties |
Applies to: | see individual properties |
Inherited: | see individual properties |
Percentages: | see individual properties |
Computed value: | see individual properties |
Animation type: | see individual properties |
Canonical order: | per grammar |
This property is a shorthand property that sets all of the scroll-start-target-* longhands in one declaration. The first value defines the scroll starting point in the block axis, the second sets it in the inline axis. If the second value is omitted, it defaults to none.
Values are defined as follows:
- none
- Element is not a scroll-start-target.
- auto
- Element is used to calculate the scroll-start position, taking into account scroll-padding or scroll-margin , same as a scroll-snap target.
4. Styling Snapped Items
4.1. The Snapped-element Pseudo-class: :snapped
The :snapped pseudo-class matches any scroll snap targets, regardless of axis. The longform physical and logical pseudo-class selectors allow for more finite snapped children styling as they can target an individual axis.
More specific options are defined as follows:
- :snapped-x
- Matches the child snapped on the horizontal axis.
- :snapped-y
- Matches the child snapped on the vertical axis.
- :snapped-inline
- Matches the child snapped on the inline axis.
- :snapped-block
- Matches the child snapped on the block axis.
Note: Issue #6985
Need to figure out resolution of the initial frame.
5. Snap Events
5.1. snapchanged
and snapchanging
CSS scroll snap points are often used as a mechanism to create scroll interactive "selection" components, where selection is determined with JavaScript intersection observers and a scroll end guestimate. By creating a built-in event, the invisible state will become actionable, at the right time, and always correct.
Event | Interface | Targets | Description |
---|---|---|---|
snapchanged
| SnapEvent
| scroll containers | Fired at the scrolling element or Document at the end of a scroll (before a scrollend event)
or after a layout snap if the element that the scrolling element or Document is snapped to changed.
|
snapchanging
| SnapEvent
| scroll containers | Fired at the scrolling element or Document during scrolling (before a scroll event),
if the element that the scrolling would cause the scroller to snap to is
different from the target reported by the last snapchanging event that was fired.
|
5.1.1. snapchanged
snapchanged
indicates that the snap area to which a snap container is snapped along either axis has changed. snapchanged
is dispatched:
- when a scrolling operation is completed if, for either the block or inline axis, the element which the snap container is snapped to is different from the element it most recently snapped to in that axis. For snap containers with proximity strictness, the scroll may result in the snap container no longer being snapped to any element. CSS Scroll Snap 1 § 6.2 Choosing Snap Positions describes the method a UA follows when choosing between elements which are snap areas.
- if there is a change to a snap container’s style such that it goes from having a non-none value for scroll-snap-type to having a none value or vice versa.
- if, after a layout change, the element to which a snap container is snapped to changes, regardless of whether there is a change in scroll position after the layout change.
Scrolling operations always lead to scrollend
events being fired. If a
scrolling operation also results in a snapchanged
event being fired, the snapchanged
event should be fired before the scrollend
event.
Each Document
has an associated list of pending snapchanged event targets, initially empty.
Each snap container has
one snapchangedTargetBlock and one snapchangedTargetInline in the block and inline axes
respectively, each of which can either be null if the container is not
snapped in that axis or the Element
to which the container is snapped.
When asked to update snapchanged targets for a snap container, snapcontainer, run these steps:
-
Let doc be snapcontainer’s associated
Document
. -
Let blockTarget be the snapchangedTargetBlock associated with snapcontainer.
-
Let inlineTarget be the snapchangedTargetInline associated with snapcontainer.
-
Let blockSnapchangingTarget be the snapchangingTargetBlock associated with snapcontainer.
-
Let inlineSnapchangingTarget be the snapchangingTargetInline associated with snapcontainer.
-
Let snap targets changed be a boolean flag that is initially false.
-
If blockTarget is not the same element as blockSnapchangingTarget or
-
Set the snapchangedTargetBlock associated with snapcontainer to blockSnapchangingTarget.
-
Set snap targets changed to true.
-
-
If inlineTarget is not the same element as inlineSnapchangingTarget:
-
Set the snapchangedTargetInline associated with snapcontainer to inlineSnapchangingTarget.
-
Set snap targets changed to true.
-
-
If snap targets changed is true:
-
If snapcontainer is not already in doc’s pending snapchanged event targets:
-
Append snapcontainer to doc’s pending snapchanged event targets.
-
-
Note: When snapping occurs on a scroller (either due to a layout change or a
scrolling operation) the snapchangingTargetBlock and snapchangingTargetInline associated with that scroller are updated and represent the current snap targets
of that scroller. This allows the update snapchanged targets algorithm
to use these elements to determine whether a snapchanged
event should be fired.
When asked to dispatch pending snapchanged events for a Document
, doc, run these steps:
-
For each item target in doc’s pending snapchanged event targets:
-
Fire a
SnapEvent
, snapevent, namedsnapchanged
at target and let snapevent’ssnapTargetBlock
andsnapTargetInline
attributes be the snapchangedTargetBlock and the snapchangedTargetInline, respectively, that are associated with target.
-
-
Empty doc’s pending snapchanged event targets.
5.1.2. snapchanging
snapchanging
is dispatched:
-
during a scrolling operation, if the element to which a snap container would snap (in either axis) changes, or
-
if a layout change occurs such that a
snapchanged
event is to be dispatched. In this case, as with the scrolling case, thesnapchanging
event should be dispatched before thesnapchanged
event.
A scrolling operation might animate towards a particular position (e.g. scrollbar arrow clicks, arrow key presses, "behavior: smooth" programmatic scrolls) or might directly track a user’s input (e.g. touch scrolling, scrollbar dragging). In either case, the user agent chooses an eventual snap target in each axis to which the scroller will snap after the scrolling operation reaches its intended scroll position.
-
In the former case, the intended scroll position is the scroll animation’s target scroll offset.
-
In the latter case, the intended scroll position is the current scroll offset as determined by the user’s input.
snapchanging
aims to let the web page know, as early as possible,
that the scrolling operation will result in a change in the element the snap
container is snapped to. The user agent should evaluate whether to trigger snapchanging
based on the eventual snap target to which the scroller would snap were the scrolling operation
to reach its intended scroll position.
Note: Since snapchanging gives the web page hints about future snapping, the snapping hinted at by a snapchanging event might not materialize since it will be possible for subsequent scrolling input to further alter the snap container’s scroll position and result in a different eventual snap target.
snapchanging
events are fired before scroll
events.
Each Document
has an associated list of pending snapchanging event targets, initially empty.
Each snap container has
one snapchangingTargetBlock and one snapchangingTargetInlinein the block and inline axes
respectively, each of which can either be null if the container is not
snapping in that axis or the Element
to which the container is snapping.
When asked to update snapchanging targets for a snap container, snapcontainer, given an Element
newBlockTarget and an Element
newInlineTarget, run these steps:
-
Let doc be snapcontainer’s associated
Document
. -
Let blockTarget be the snapchangingTargetBlock that is associated with snapcontainer.
-
Let inlineTarget be the snapchangingTargetInline that is associated with snapcontainer.
-
If newBlockTarget is not the same element as blockTarget:
-
Set the snapchangingTargetBlock associated with snapcontainer to newBlockTarget.
-
If snapcontainer is not already in doc’s pending snapchanging event targets,
-
Append snapcontainer to doc’s pending snapchanging event targets
-
-
-
If newInlineTarget is not the same element as inlineTarget:
-
Set the snapchangingTargetInline associated with snapcontainer to newInlineTarget.
-
If snapcontainer is not already in doc’s pending snapchanging event targets,
-
Append snapcontainer to doc’s pending snapchanging event targets.
-
-
When asked to dispatch pending snapchanging events for a Document
, doc, run these steps:
-
For each item target in doc’s pending snapchanging event targets:
-
Fire a
SnapEvent
, snapevent, namedsnapchanging
at target and let snapevent’ssnapTargetBlock
andsnapTargetInline
attributes be the snapchangingTargetBlock and the snapchangingTargetInline, respectively, that are associated with target.
-
-
Empty doc’s pending snapchanging event targets.
5.1.3. Snap Events due to Layout Changes
When a snap container, snapcontainer, re-snaps, run these steps:-
Let newBlockTarget be the element that snapcontainer has snapped to in the block axis or null if it did not snap to any element.
-
Let newInlineTarget be the element that snapcontainer has snapped to in the inline axis or null if it did not snap to any element.
-
Run the steps to update snapchanging targets with newBlockTarget as newBlockTarget and newInlineTarget as newInlineTarget.
-
Run the steps to update snapchanged targets for snapcontainer.
5.2. SnapEvent interface
dictionary :
SnapEventInit EventInit {Node ?;
snapTargetBlock Node ?; }; [
snapTargetInline Exposed =Window ]interface :
SnapEvent Event {(
constructor DOMString ,
type optional SnapEventInit = {});
eventInitDict readonly attribute Node ?snapTargetBlock ;readonly attribute Node ?snapTargetInline ; };
snapTargetBlock
, of type Node, readonly, nullable-
The element that the snap container is snapped to in the block axis at the snap position for the associated snap event.
snapTargetInline
, of type Node, readonly, nullable-
The element that the snap container is snapped to in the inline axis at the snap position for the associated snap event.
For snapchanged
events, the snap position is the position already
realized by the snap container after a scroll snap. For snapchanging
events it is the snap position that the snap container will eventually
snap to when the scrolling operation ends.
A SnapEvent
should not bubble and should not be cancellable.
Appendix A: Longhands
The physical and logical longhands (and their shorthands) interact as defined in [CSS-LOGICAL-1].
Physical Longhands for scroll-start
Name: | scroll-start-x, scroll-start-y |
---|---|
Value: | auto | start | end | center | <length-percentage [0,∞]> |
Initial: | auto |
Applies to: | scroll containers |
Inherited: | no |
Percentages: | relative to the corresponding axis of the scroll container’s scrollport |
Computed value: | the keyword auto or a computed <length-percentage> value |
Canonical order: | per grammar |
Animation type: | by computed value type |
Logical property group: | scroll-start |
...
Flow-relative Longhands for scroll-start
Name: | scroll-start-inline, scroll-start-block |
---|---|
Value: | auto | start | end | center | <length-percentage [0,∞]> |
Initial: | auto |
Applies to: | scroll containers |
Inherited: | no |
Percentages: | relative to the corresponding axis of the scroll container’s scrollport |
Computed value: | the keyword auto or a computed <length-percentage> value |
Canonical order: | per grammar |
Animation type: | by computed value type |
Logical property group: | scroll-start |
...
Flow-relative Longhands for scroll-start-target
Name: | scroll-start-target-block, scroll-start-target-inline |
---|---|
Value: | auto | none |
Initial: | none |
Applies to: | all elements |
Inherited: | no |
Percentages: | n/a |
Computed value: | either of the keywords "none" or "auto" |
Canonical order: | per grammar |
Animation type: | not animatable |
Logical property group: | scroll-start-target |
...
Physical Longhands for scroll-start-target
Name: | scroll-start-target-x, scroll-start-target-y |
---|---|
Value: | none | auto |
Initial: | none |
Applies to: | all elements |
Inherited: | no |
Percentages: | n/a |
Computed value: | either of the keywords "none" or "auto" |
Canonical order: | per grammar |
Animation type: | not animatable |
Logical property group: | scroll-start-target |
...