├── css-scroll-marker └── readme.md ├── css-scroll-start └── readme.md ├── css-snap-target └── readme.md ├── js-scrollToOptions_Snap-Additions └── readme.md ├── js-snapChanged └── readme.md ├── js-snapChanging └── readme.md └── js-snapTo() └── readme.md /css-scroll-marker/readme.md: -------------------------------------------------------------------------------- 1 | # CSS `::scroll-marker` 2 | 3 | ### Status of this Document 4 | This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to 5 | problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the 6 | most current standards venue and content location of future work and discussions. 7 | * This document status: **Active** 8 | * Expected venue: [CSSWG](https://drafts.csswg.org/) 9 | * Current version: this document 10 | 11 | ## Introduction 12 | Scroll is found everywhere, from carousels to horizontal product grids that flow offscreen to virtual scrollers and cyclical scrollers. A common user experience and ask is to get rid of the "bar" that shows the scroll, and replace it with another kind of scroll indicator (like dots). Scrolling is not always tied to a bar, and combined with snap points, a marker feature is especially useful. 13 | 14 | ![Scroll bar vs. scroll markers](https://developer-chrome-com.imgix.net/image/HodOHWjMnbNw56hvNASHWSgZyAf2/0gNU49FcXulOlQ3q5Voe.png) 15 | 16 | ### Goals 17 | Reduce Javascript responsibility and enable a declarative pattern for styling scroll/snap points that doesn't rely on a bar. 18 | 19 | ### Use Cases 20 | - Carousels (hero carousel, iTunes-inspired album flow carousels) 21 | - Product lists (i.e. App store-like experiences) 22 | - Scrollable lists with corresponding markers 23 | - Galleries 24 | 25 |
26 | 27 | ## Proposed Solution 28 | A new CSS property on scroll containers named `scroll-display` which converts the scroll bar area into a marker area, paired with a new CSS pseudo element `::scroll-marker` that enables styling of said markers in a similar manner to list `::marker`. This means that you could create custom counter styles, and adjust the spacing of the items. 29 | 30 | | | | 31 | |:----------|:-------------| 32 | | Name: | `scroll-display` | 33 | | Values: | `auto` `marker` `bar` | 34 | | Default: | `bar` | 35 | 36 |
37 | 38 | ```css 39 | .carousel { 40 | scroll-display: marker; /* set scroll display type */ 41 | } 42 | 43 | .carousel::scroll-marker { 44 | color: yellow; 45 | } 46 | 47 | .carousel::scroll-marker:focus { 48 | color: red; 49 | } 50 | ``` 51 | 52 |
53 | 54 | **Features:** 55 | - Allows setting marker-like styles instead of bar style for scroll 56 | - Allows styling markers 57 | - Enables positioning markers within the scroll area 58 | 59 | 60 |
61 | 62 | ## Additional Thoughts / Open Questions 63 | 64 | ### How to set/determine scroll points 65 | 66 | List item = scroll child for the sake of this conversation, but a scroll child could be any element: 67 | 68 | - Scroll points could be set one of two ways: 69 | - Set on the parent scroller for each list item (dynamic scroll points) 70 | - Manually set on each list item by the author (scrollable attribute) 71 | - Keeping correct state for the active list item and list marker 72 | - Can we also include previous/next buttons, like oldschool scrollbars had at the top and bottom? 73 | 74 |
75 | 76 | ## Examples 77 | 78 | TBD 79 | 80 |
81 | 82 | ## Privacy and Security Considerations 83 | 84 | ### Privacy 85 | 86 | There are no known privacy impact of this feature. 87 | 88 | ### Security 89 | 90 | There are no known security impacts of this feature. 91 | 92 | ## Contributing 93 | For now, please use this [Discussion](https://github.com/argyleink/ScrollSnapExplainers/discussions/16) for comments and questions, or a Pull Request to offer changes 🙏 94 | -------------------------------------------------------------------------------- /css-scroll-start/readme.md: -------------------------------------------------------------------------------- 1 | # CSS `scroll-start` 2 | 3 | ### Status of this Document 4 | This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to 5 | problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the 6 | most current standards venue and content location of future work and discussions. 7 | * This document status: **Active** 8 | * Expected venue: [CSSWG](https://drafts.csswg.org/) 9 | * Current version: this document 10 | 11 | ## Introduction 12 | 13 | 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 x or y position, both users, page authors and browsers benefit: 14 | - Colocate initial scroll position with other scroll styles 15 | - JavaScript can be removed 16 | - Post page load scroll shift mitigated 17 | 18 | ### Goals 19 | Reduce Javascript responsibility and enable a declarative pattern for interaction models that need to start somewhere other than the beginning. 20 | 21 | ### Use Cases 22 | - Carousels 23 | - Pull to refresh 24 | - Pull to search 25 | - ListViews [starting at a certain item](https://codepen.io/FelipePS/pen/mdqbqaE) or group 26 | - Galleries 27 | - Swipe interactions (left to archive, right to delete) 28 | 29 |
30 | 31 | ## Proposed Solution 32 | A new CSS property on scroll containers `scroll-start` which sets the initial scroll position and a CSS property on scroll container children `scroll-start-target`. Once the user has interacted with the scroll area, `scroll-start` and `scroll-start-target` has no effect. The styles must be present during [first layout](#first-layout) or they have otherwise missed their timing. For example, an instable layout that shifts after page load, can disrupt `scroll-start` by causing a scroll position update. 33 | 34 | | | | 35 | |:----------|:-------------| 36 | | Name: | `scroll-start` or `scroll-start-x` `scroll-start-y` `scroll-start-inline` `scroll-start-block` | 37 | | Value: | `auto` `` `start` (default) `end` `center` | 38 | 39 | > Shorthands do not autoexpand like `scroll-start: 200px` is not `scroll-start: 200px 200px`, it's `scroll-start: 200px 0` 40 | 41 | | | | 42 | |:----------|:-------------| 43 | | Name: | `scroll-start-target` or `scroll-start-target-x` `scroll-start-target-y` `scroll-start-target-inline` `scroll-start-target-block` | 44 | | Value: | `auto` `none` (default) | 45 | 46 |
47 | 48 | **Features:** 49 | - Allows setting an absolute scroll value on either axis 50 | - Allows setting both axes start positions at the same time 51 | - Allows setting an element as the target for an axis 52 | - Persistant after layout 53 | 54 |
55 | 56 | ## FAQ / Questions / Exceptions 57 | ### Interactions with scroll padding and margin 58 | These should all apply just like when a snap child is positioned. 59 | 60 | ### Interactions with [fragment navigation](https://html.spec.whatwg.org/multipage/browsing-the-web.html#scroll-to-fragid) 61 | 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. 62 | 63 | ### Interactions with `scrollTo()` options 64 | Similar to the additions [proposed here](https://github.com/argyleink/ScrollSnapExplainers/tree/main/js-scrollToOptions_Snap-Additions), `scroll-start` should be a valid scroll target. For example, the following `scrollTo({top: 'scroll-start'})` scrolls the container to the value specified in the property. 65 | 66 | ### Interactions with a snap container with only 1 snap child 67 | This effectively will layout and start scroll at the snapped child, thus negating / cancelling this styles effect. `scroll-start` will only work if nothing else has effected the scroll position. 68 | 69 | ### How to resolve multiple `scroll-start-target`'s 70 | The first in the DOM order will be chosen. 71 | 72 | ### How to resolve `scroll-start` and `scroll-start-target` if both used 73 | The target child will be used instead of the lengths in `scroll-start`. 74 | 75 | ### What happens if display is toggled? 76 | Same behavior that animations follow with [first layout](#first-layout). 77 | 78 | ### Scroller inside a scroller? 79 | Should follow patterns that scroll snap has laid down. 80 | 81 | ### RTL/LTR 82 | Logical properties are offered for length offsets that should be flow relative. Furthermore, the `end` and `start` keywords are always logical. 83 | 84 | ### Interactions with `place-content` 85 | TODO 86 | 87 | ### What happens if the element moves around or the scroller gets resized? 88 | Just like scroll-snap, the scroll-start position should track with the scroller, performing relayout and assigning the scroll-start position. Note, if the user has interacted with the scroller at all before resize or movement, the start position does not track along anymore. 89 | 90 | ### What happens if the position is beyond the scroll area? 91 | The `scroll-start` position is clamped to the scrollport minimums and maximums. 92 | 93 |
94 | 95 | ## Examples 96 | #### 1. Set the start position to a snap child 97 | ```css 98 | .snap-scroll-inline { 99 | overflow-inline: scroll; 100 | scroll-snap-type: inline mandatory; 101 | } 102 | .snap-scroll-inline > #snap-start { 103 | scroll-start-target-inline: auto; 104 | } 105 | ``` 106 | 107 | #### 2. Set the start position to an absolute value 108 | ```css 109 | :root { --nav-height: 100px } 110 | 111 | .snap-scroll-y { 112 | scroll-start-y: var(--nav-height); 113 | } 114 | ``` 115 | 116 | #### 3. Set both with a shorthand (inline then block) 117 | ```css 118 | .scroll { 119 | scroll-start: 200px 400px; 120 | } 121 | ``` 122 | 123 | #### 4. `
` element will be already scrolled to, without requiring a URL hash 124 | 125 | ```css 126 | html > main { 127 | scroll-start-target-block: auto; 128 | } 129 | ``` 130 | 131 |
132 | 133 | ## Terminology 134 | 135 | #### First Layout 136 | 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. 137 | 138 |
139 | 140 | ## Privacy and Security Considerations 141 | 142 | ### Privacy 143 | 144 | There are no known privacy impact of this feature. 145 | 146 | ### Security 147 | 148 | There are no known security impacts of this feature. 149 | 150 | ## Contributing 151 | For now, please use this [Discussion](https://github.com/argyleink/ScrollSnapExplainers/discussions/4) for comments and questions, or a Pull Request to offer changes 🙏 152 | -------------------------------------------------------------------------------- /css-snap-target/readme.md: -------------------------------------------------------------------------------- 1 | # CSS Snapped Elements pseudo-classes 2 | > `:snapped` `:snapped-x` `:snapped-y` `:snapped-inline` `:snapped-block` 3 | 4 | ### Status of this Document 5 | This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. 6 | * This document status: **Active** 7 | * Expected venue: [CSSWG](https://drafts.csswg.org/) 8 | * Current version: this document 9 | 10 | ## Introduction 11 | 12 | User Agents privately maintain scroll snap state, leaving web developers unable to update complimentary interfaces accordingly. Scroll snapping is often used for "picker UX", [made popular by iOS](https://miro.medium.com/max/700/1*LJujvSAe2lGVdY2ETmg5ew.png). 13 | 14 | ![Screen Shot 2021-09-13 at 1 09 52 PM](https://user-images.githubusercontent.com/1134620/133149663-b3cd3f6e-7541-4cc0-a325-7fe7ade2e4cb.png) 15 | 16 | A scroll snap container can have any nested child (**just one**) snapped to either the x or y axes (or inline/block respectively). In a 2-dimensional snapping layout, a UA may match 2 separate elements with `:snapped`. Adding axes to the pseudo-class makes the selector more specific, as to match design or developer intent. 17 | 18 | In the case where more than 1 element appear snapped or are snapped on an axis, the 1st of the DOM order is the `:snapped` child. An exception is if a child is snapped on the cross axis, in which case it should be `:snapped` even if it's not the 1st of the DOM order. 19 | 20 | The selector matching timing should synchronize with the same frame the UA has decided on a new snap child. Sooner is better than later, but UA's can decide. 21 | 22 | > A [gallery of scroll snap interactions](https://snap-gallery.netlify.app/) can help illuminate the CSS UI need, here's [the source](https://github.com/argyleink/snapping-gallery). Specifically see the `:snapped` demo's for an attempt at deriving the snapped element. 23 | 24 | ### Preventing infinite or expensive browser loops 25 | Like `:hover`, a developer may find they have caused a loop by changing a property on the snapped item that causes it to unsnap, then potentially snap again. This type of behavior is a concern mentioned by the CSSWG in [selectors that depend on layout](https://wiki.csswg.org/faq#selectors-that-depend-on-layout) and is top of mind here in this feature. 26 | 27 | This can be mitigated by limiting the loops to a single frame, just like hover. This avoids "tight loops" that could cause major browser problems, it instead is a loose loop which hover has proved for 20 years can be mitigated and avoided through diligence. It's understood there's a challenge here, and the enablement via CSS will pacify all the much worse JavaScript solutions folks attempt today. Hover is a cherished and respected property that has proven to not be as destructive than anticipated. 28 | 29 | Worth considering too, only evaluating selectors with `:snapped` if the containing scrollport is actually scrolling. If no scrolling is happening, the selectors won't continue firing. Consider it like the `:hover` hit test, rather a scroll test. The user, or programmatic scroll from JS, are events that will trigger the selector evaluation, not a constant frame by frame evaluation. 30 | 31 | ### Common Gotcha 32 | Depending on the scroll snap styles, some (many) snap children may never become snapped. This is a recurring UX and API question, as a scroll gesture may be limited or at the end, and the desired snap target cannot be brought to the snap axis alignment area. 33 | 34 | Take the following example, where figure elements want to align to `start` but the container is scrolled to the end. Essentially, the last 2 figures will never be snapped: 35 | 36 | ![Screen Shot 2021-08-26 at 2 29 18 PM](https://user-images.githubusercontent.com/1134620/131038651-8adfd69d-806b-4915-ba6e-827cde2054f5.png) 37 | 38 | **Unless!** 39 | Styles are added so the user can scroll to the last item, aka enough padding in the container so items at the end can align on the axis: 40 | 41 | ![Screen Shot 2021-08-26 at 2 32 49 PM](https://user-images.githubusercontent.com/1134620/131039093-38946e38-a664-4fa0-a9da-f0222cf7b423.png) 42 | 43 | ### Goals 44 | Reduce Javascript responsibility and enable a declarative pattern for updating interface children if they are currently snapped. 45 | 46 | ### Use Cases 47 | - Carousels 48 | - ListViews (great for making selections in long lists) 49 | - Galleries (almost always an active item with additional UI to show) 50 | 51 |
52 | 53 | ## Proposed Solution 54 | 5 new CSS pseudo-class selectors. 4 for higher specificity targetting, 1 for loose matching: 55 | 56 | | | | 57 | |:----------|:-------------| 58 | | Name: | `:snapped` or `:snapped-block` `:snapped-y` `:snapped-inline` `:snapped-x` | 59 | 60 |
61 | 62 | The loose example, estimated to be the most commonly used, will match all scroll snap targets, regardless of axis. Assuming folks aren't overlapping scroll snap children, this should be quite reliable. 63 | 64 | ```css 65 | :snapped { 66 | outline: hotpink; 67 | } 68 | ``` 69 | 70 | **Features:** 71 | - Allows loose targeting shorthand for authors with distinct axis snap targets 72 | - Allows specific axis selecting in 2D cases 73 | 74 |
75 | 76 | ## Examples 77 | 78 | #### 1: Lifted card snap child 79 | 80 | ```css 81 | .card:snapped { 82 | --shadow-distance: 30px; /* raised size */ 83 | } 84 | ``` 85 | 86 | ### 2: Accessible outline on snap child 87 | 88 | ```css 89 | :snapped-inline { 90 | outline: 3px solid hotpink; 91 | } 92 | ``` 93 | 94 | ### 3: Lifted sticky section header navigation 95 | 96 | ```css 97 | section:snapped-y > header { 98 | box-shadow: 0 .5em 1em .5em lch(5% 5% 200); 99 | border-inline-start-color: hotpink; 100 | } 101 | ``` 102 | 103 |
104 | 105 | ## Cyclical concerns, like with `:hover` 106 | 107 | We ([Robert Flack](https://github.com/flackr), [Tab Atkins](https://github.com/tabatkins) and [Adam Argyle](https://github.com/argyleink/)) believe the value of adding this feature exceeds the implementation difficulties. The lengths at which people go to try and derive the snapped child are never 100% effective and require complex JavaScript algorithms. 108 | 109 | We are OK letting `:snapped` cycle like `:hover`, as long as it's only once per lifecycle update. We believe developers will be responsible with this, as they have with `:hover`. 110 | 111 |
112 | 113 | ## Privacy and Security Considerations 114 | 115 | ### Privacy 116 | 117 | There are no known privacy impact of this feature. 118 | 119 | ### Security 120 | 121 | There are no known security impacts of this feature. 122 | 123 | ## Contributing 124 | For now, please use this [Discussion](https://github.com/argyleink/ScrollSnapExplainers/discussions/5) for comments and questions, or a Pull Request to offer changes 🙏 125 | -------------------------------------------------------------------------------- /js-scrollToOptions_Snap-Additions/readme.md: -------------------------------------------------------------------------------- 1 | # JS `scrollToOptions` "snap" Additions 2 | 3 | ### Status of this Document 4 | This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to 5 | problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the 6 | most current standards venue and content location of future work and discussions. 7 | * This document status: **Active** 8 | * Expected venue: [W3C](https://www.w3.org) 9 | * Current version: this document 10 | 11 | ## Introduction 12 | 13 | [Scroll snap points](https://www.w3.org/TR/css-scroll-snap-1/) are an important and powerful CSS feature. They enable the creation of pointer agnostic stepped scroll experiences, and do so very succinctly. The problem is, they're often used alongside adjacent elements which need to represent this stepped state of scroll, aka the user needs a way to see the state of the snap. Developers today write Javascript observers or create custom functions to monitor and drive this state. 14 | 15 | Wiring up "snap to next", "snap to last" or "snap to the clicked item" **should be trivial methods to call on a snap container**. 16 | 17 | ### Goals 18 | 19 | To empower the scroll snapping container with an API for iterating through snap children in a reasonable way, for all snapping containers, including [2-dimensional matrix layouts](https://codepen.io/argyleink/pen/MWWpOmz) and support for all document directions and writing modes. 20 | 21 | ### Use Cases 22 | 23 | 1. Carousels 24 | 2. Sliders 25 | 3. Tabousels or Carotabs 26 | 4. Media galleries 27 | 5. Slides 28 | 6. Click/tap and snap to item 29 | 30 |
31 | 32 | ## Proposed Additions 33 | New values for the [`scrollToOptions`](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions) and [`scrollIntoViewOptions`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView) of the [CSSOM View](https://drafts.csswg.org/cssom-view/#dictdef-scrolltooptions). This would enable APIs like `Window.scrollTo()`, `Element.scrollBy()` and `Element.scrollIntoView()` to be aware and offer APIs for programmatic snapping. 34 | 35 |
36 | 37 | **New values for scrollToOptions `left` and `top` properties:** 38 | - `snap-next` 39 | Depending on the axis, the UA is to scroll towards "end" to the next valid snap target. If the next snap target is at the same scroll position as the current snap target, the UA is to find the next snap target that has a different scroll position. If there is no next snap target, as in the current snap target is the last valid target, the UA is to look at the `cyclic` property to see if it should scroll back to the beginning, aka `snap-first`, or do nothing. 40 | 41 | - `snap-previous` 42 | Depending on the axis, the UA is to scroll towards "start" to the previous valid snap target. If the previous snap target is at the same scroll position as the current snap target, the UA is to find the next snap target that has a different scroll position. If there is no previous snap target, as in the current snap target is the 1st valid target, the UA is to look at the `cyclic` property to see if it should scroll to the end, aka `snap-last`, or do nothing. 43 | 44 | - `snap-first` 45 | Depending on the axis, the UA is to scroll to the snap target closest to scroll position 0. 46 | 47 | - `snap-last` 48 | Depending on the axis, the UA is to scroll to the snap target closest to scroll end. 49 | 50 |
51 | 52 | **New values for scrollIntoViewOptions `block` and `inline` properties:** 53 | - `snap-self` 54 | Depending on the axis, the UA is to scroll to and snap the contextual element. Snap positioning is determined by CSS 55 | 56 |
57 | 58 | ## Examples 59 | 60 | #### 1: basic 61 | ```js 62 | container.scrollTo({ 63 | left: 'snap-next', 64 | cyclic: true, 65 | }) 66 | ``` 67 | 68 | #### 2: Control animation 69 | ```js 70 | container.scrollTo({ 71 | left: 'snap-prev', 72 | behavior: 'smooth', 73 | }) 74 | ``` 75 | 76 | #### 3: 2D 77 | ```js 78 | container.scrollTo({ 79 | left: 'snap-first', 80 | top: 'snap-first', 81 | behavior: 'instant', 82 | }) 83 | ``` 84 | 85 | #### 4: To a tapped element 86 | ```js 87 | container.addEventListener('click', event => { 88 | event.target.scrollIntoView({ 89 | left: 'snap-self', 90 | top: 'snap-self', 91 | behavior: 'smooth', 92 | }) 93 | }) 94 | ``` 95 | 96 |
97 | 98 | ## Privacy and Security Considerations 99 | 100 | ### Privacy 101 | 102 | There are no known privacy impact of this feature. 103 | 104 | ### Security 105 | 106 | There are no known security impacts of this feature. 107 | 108 | ## Contributing 109 | For now, please use this [Discussion](https://github.com/argyleink/ScrollSnapExplainers/discussions/13) for comments and questions, or a Pull Request to offer changes 🙏 110 | -------------------------------------------------------------------------------- /js-snapChanged/readme.md: -------------------------------------------------------------------------------- 1 | # JS Snap Events 2 | 3 | ### Status of this Document 4 | This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. 5 | * This document status: **Active** 6 | * Expected venue: [CSSWG](https://drafts.csswg.org/) 7 | * Current version: this document 8 | 9 | ## Introduction 10 | 11 | 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. 12 | 13 | ## Goals 14 | 15 | Help developers sychronize a snapped scroll item with the rest of their interface elements efficiently and effectively. 16 | 17 |
18 | 19 | ## Use Cases 20 | 21 | Scroll centric selection: 22 | - Date / Time picker 23 | - Carousel 24 | - Tabs 25 | - State / Country picker 26 | 27 |
28 | 29 | ## Example 30 | 31 | In a scroll-snapping carousel of images, a developer might want to style or animate the 32 | image that the carousel is snapped to differently from other images in the 33 | carousel or perform some other action on the page in sync with what is snapped to. 34 | 35 | For example, given the following HTML: 36 | 37 | ``` 38 | 48 | 49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | 57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | ``` 65 | where `snapcarousel` contains a set of images which can be viewed by 66 | scrolling horizontally and `snapcarouselctrl` gives an alternative way to scroll 67 | `snapcarousel` via buttons which, when clicked, scrolls their associated image 68 | to `snapcarousel`'s center. 69 | 70 | A developer might want to synchronize the styles of both the 71 | image selected in `snapcarousel` and its corresponding button in `snapcarouselctrl`, 72 | like in the following image when the user drags the scrollbar so as to cause a 73 | change in snap targets from the third image to the fourth image: 74 | 75 | ![Snap Carousel Explainer](https://i.imgur.com/6BtkRIt.png) 76 | 77 | To do this, the developer will need to know when the image the carousel is 78 | snapped to has changed and what it has changed to. 79 | 80 | To know when it has changed, they might rely on scroll and scrollend events but 81 | this means their script must run on every scroll. This also limits their 82 | awareness to cases where the change in snap targets occured due to a scroll but 83 | a layout change in the page might also trigger a change in which element the 84 | carousel is snapped to without causing a scroll. 85 | 86 | To know what it has changed to, the developer would have to examine the all the 87 | snap areas of the carousel and try to identify which element is snapped to based 88 | on the current layout and style of the page, so they might write something like: 89 | 90 | ``` 91 | // Get the offset, relative to the viewport, at which |area|'s left edge needs 92 | // to be in order to be snap-aligned to |container|. 93 | function GetAlignedOffset(container, area) { 94 | const areaStyle = getComputedStyle(area); 95 | const alignment = areaStyle.scrollSnapAlign; 96 | const scrollMargin = parseInt(areaStyle.scrollMargin); 97 | const containerOffsetLeft = container.offsetLeft; 98 | 99 | let alignedOffset; 100 | if (alignment === "start") { 101 | alignedOffset = containerOffsetLeft; 102 | } else if (alignment === "center") { 103 | alignedOffset = containerOffsetLeft + container.clientWidth / 2 - area.clientWidth / 2; 104 | } else { // "end" alignment 105 | alignedOffset = containerOffsetLeft + container.clientWidth - area.clientWidth; 106 | } 107 | 108 | return alignedOffset + scrollMargin; 109 | } 110 | 111 | // Get the distance of |area|'s left edge, relative to the viewport from its 112 | // snap-aligned position. 113 | function GetSnapDistance(container, area) { 114 | const alignedOffset = GetAlignedOffset(container, area); 115 | 116 | return Math.abs(parseInt(area.getBoundingClientRect().x) - alignedOffset); 117 | } 118 | 119 | // Update the UI to highlight the item that is snapped-to if it has changed. 120 | function UpdateCurrentSnapTarget() { 121 | const snapAreas = carousel.querySelectorAll(".slide_thumbnail"); 122 | let newSnapTarget = currentSnapTarget; 123 | let min_distance = Infinity; 124 | 125 | for (const snapArea of snapAreas) { 126 | const distance = GetSnapDistance(carousel, snapArea); 127 | if (distance < min_distance) { 128 | newSnapTarget = snapArea; 129 | min_distance = distance; 130 | } 131 | } 132 | 133 | DeEmphasize(hintSnapTarget, /*hint*/true); 134 | if (newSnapTarget !== currentSnapTarget) { 135 | DeEmphasize(currentSnapTarget); 136 | } 137 | 138 | currentSnapTarget = newSnapTarget; 139 | Emphasize(currentSnapTarget); 140 | } 141 | 142 | // Update the UI to highlight, as a hint, the item that the current scrolling 143 | // is intending to snap to, unless the item is the last snapped-to thing. 144 | function UpdateHintSnapTarget() { 145 | const snapAreas = carousel.querySelectorAll(".slide_thumbnail"); 146 | let newHintTarget = hintSnapTarget; 147 | let min_distance = Infinity; 148 | 149 | for (const snapArea of snapAreas) { 150 | const distance = GetSnapDistance(carousel, snapArea); 151 | if (distance < min_distance) { 152 | newHintTarget = snapArea; 153 | min_distance = distance; 154 | } 155 | } 156 | 157 | if (newHintTarget === hintSnapTarget) { 158 | return; 159 | } 160 | 161 | DeEmphasize(hintSnapTarget, /*hint*/true); 162 | 163 | // Don't override the current snap target's style with hint styling. 164 | if (newHintTarget === currentSnapTarget) { 165 | return; 166 | } 167 | 168 | hintSnapTarget = newHintTarget; 169 | Emphasize(hintSnapTarget, /*hint*/true); 170 | } 171 | 172 | carousel.addEventListener("scrollend", () => { 173 | UpdateCurrentSnapTarget(); 174 | }); 175 | 176 | carousel.addEventListener("scroll", () => { 177 | UpdateHintSnapTarget(); 178 | }); 179 | ``` 180 | which is trying to determine which element the browser has selected as the snap 181 | target based on the scroll position but may not align with what the browser 182 | actually picks. [This slideshow example](https://davmila.github.io/SnapEventExamples/carousel-2/proxy.html) uses scroll and scrollend events to synchronize the styles of the images in the carousel and the smaller thumbnails below. 183 | One thing to note in this example is that when clicking on a thumbnail below to scroll 184 | the main carousel, all the thumbnails between the one corresponding to the 185 | current snap target and the one corresponding to the new one are momentarily highlighted 186 | as the carousel scrolls to its new snap target. This happens because the `scroll` event 187 | is not aware of the eventual snap target, causing the page to invoke 188 | the style change unnecessarily several times. `scrollsnapchanging` 189 | is proposed as an event that lets the page know as early as possible what the 190 | eventual snap target is. Alternatively, an author might use [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API), 191 | but that only goes as far as telling them that a snap area is visible within the 192 | scroll port. So, if multiple snap areas are within the snap port, they would still 193 | need to rely on script similar to the above to determine which snap area is the 194 | snap target. 195 | 196 | In cases where multiple elements could be considered snap-aligned, e.g. grid of 197 | snap areas, the developer would need to write a bit more code to figure out 198 | which, among the aligned areas, the user-agent has selected. They would need to 199 | either: 200 | * implement the user agents' algorithm to 201 | [select between multiple aligned areas](https://drafts.csswg.org/css-scroll-snap/#multiple-aligned-snap-areas), or 202 | * test for which element the user agent has snapped to by shifting layout to see 203 | which element the snap container follows. 204 | 205 | A robust implementation would also need to account for several other details that affect the 206 | user-agent's choice of snap targets, such as: 207 | 208 | * [proximity strictness](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type#proximity) scroll snapping, 209 | * snap areas at the extremes of their snap containers such that their theoretical aligned position cannot be reached, 210 | * snap areas the user-agent considers snapped-to by virtue of their being larger than the scroll port, 211 | * out-of-flow-positined snap areas which may be within the scroll container's subtree in the DOM but not be within the scrollable overflow area of the scroll container, 212 | * scroll-margin; this is accounted for in the sample code above, but any future CSS property which affects scroll-snapping like scroll-margin does could render any existing snap-point calculating code incorrect. 213 | 214 | It's also worth noting that user-agents already handle all of this. 215 | 216 | ## Proposed Solution 217 | 218 | We propose new JavaScript events for scroll snap containers 219 | called `scrollsnapchange` and `scrollsnapchanging`. `scrollsnapchange` is 220 | dispatched when a new snap target has been snapped to. `scrollsnapchanging` is 221 | dispatched as soon as the UA has determined a new snap target during an ongoing 222 | scroll operation. 223 | 224 | The `scrollsnapchanging` event fires while scrolling is happening, 225 | allowing the page to communicate to the user that their scrolling gesture has 226 | triggered the selection of a new snap point which the user-agent will settle on 227 | when the user's gesture is complete. 228 | 229 | The `scrollsnapchange` event fires when scrolling is complete and the snap 230 | point that has been settled on is different from the last snap point that was 231 | settled on. 232 | 233 | Both events would be triggered by a layout shift resulting in a change in the 234 | selected snap target. 235 | 236 | In the case of the layout example above, the developer would only need to write 237 | the following to detect changes in snap targets: 238 | 239 | ``` 240 | carousel.addEventListener("scrollsnapchange", (evt) => { 241 | DeEmphasize(hintSnapTarget, /*hint*/true); 242 | DeEmphasize(currentSnapTarget); 243 | currentSnapTarget = evt.snapTargetInline; 244 | Emphasize(currentSnapTarget); 245 | }); 246 | 247 | carousel.addEventListener("scrollsnapchanging", (evt) => { 248 | DeEmphasize(hintSnapTarget, /*hint*/true); 249 | hintSnapTarget = evt.snapTargetInline; 250 | // Ensure to undo any effects snapchanging away from the 251 | // currentSnapTarget may have done. 252 | Emphasize(hintSnapTarget, /*hint*/evt.snapTargetInline !== currentSnapTarget); 253 | }); 254 | ``` 255 | without the risk of computing the wrong thing. 256 | [This slideshow example](https://davmila.github.io/SnapEventExamples/carousel-2/real.html) 257 | is visually identical to the slideshow example mentioned earlier but it uses the 258 | `scrollsnapchanging` and `scrollsnapchange` event listeners above instead of 259 | scroll/scrollend events.* 260 | 261 | Additionally [here](https://codepen.io/argyleink/pen/oNOWwKq) is a date-time 262 | picker example which uses snap events to update the displayed date/time.* 263 | 264 | *At the moment, examples using `scrollsnapchanging` and `scrollsnapchange` 265 | are only functional in Chrome with experimental web features enabled. 266 |
267 | 268 | ## Event Interfaces 269 | 270 | **Type**: scrollsnapchange (inspired by [`snapped` comment](https://github.com/w3c/csswg-drafts/issues/156#issuecomment-695085852)) 271 | **Interface**: SnapEvent 272 | **Sync / Async**: Async 273 | **Bubbles**: No, except at document 274 | **Trusted Targets**: Element, Document 275 | **Cancelable**: No 276 | **Default action**: None 277 | **Context (trusted events):** 278 | 279 |
280 | 281 | **Type**: scrollsnapchanging (inspired by [`snapped` comment](https://github.com/w3c/csswg-drafts/issues/156#issuecomment-695085852)) 282 | **Interface**: SnapEvent 283 | **Sync / Async**: Async 284 | **Bubbles**: No, except at document 285 | **Trusted Targets**: Element, Document 286 | **Cancelable**: No 287 | **Default action**: None 288 | **Context (trusted events):** 289 |
290 | 291 | 292 | These events implement a `SnapEvent` interface. 293 | 294 | ``` 295 | interface SnapEvent : Event { 296 | readonly attribute EventTarget target; 297 | 298 | readonly attribute Node? snappedTargetBlock; 299 | readonly attribute Node? snappedTargetInline; 300 | }; 301 | ``` 302 | 303 | - `Event.target`: scroll container the snap target is in (this is inherited from the generic DOM [Event](https://dom.spec.whatwg.org/#interface-event) interface). 304 | - `SnapEvent.snappedTargetBlock`: the element which is currently snapped to in the block axis. 305 | - `SnapEvent.snappedTargetInline`: the element which is currently snapped to in the inline axis. 306 | 307 | ## Alternatives and Other Considerations 308 | 309 | The SnapEvent interface described above reflects the minimum amount of information 310 | which satisfies many of the scenarios expressed by developers as can be seen in 311 | the following links: 312 | 313 | ### Interest in Snap Events 314 | 315 | * https://stackoverflow.com/questions/66852102/css-scroll-snap-get-active-item 316 | * https://stackoverflow.com/questions/71007514/css-scroll-snap-focusing-on-the-element-which-got-snapped-to 317 | * https://github.com/w3c/csswg-drafts/issues/7430 318 | * https://stackoverflow.com/questions/75471179/how-to-identify-the-element-currently-visible-on-the-screen-i-am-using-scroll 319 | * https://github.com/tailwindlabs/tailwindcss/discussions/6450 320 | * https://stackoverflow.com/questions/54797620/css-scroll-snap-api 321 | * https://webwewant.fyi/wants/65/ 322 | * https://stackoverflow.com/questions/57326493/how-can-i-snap-scroll-and-trigger-the-event-when-snapped?rq=3 323 | * https://stackoverflow.com/questions/53355384/indicators-dots-with-css-scroll-snap 324 | 325 | Common to all these examples is the desire to know which element was snapped to, 326 | which the above-described API provides. 327 | 328 | ### Additional SnapEvent Fields 329 | 330 | #### SnapEvent.previousSnapTarget{Block|Inline} 331 | 332 | In many use cases, when a change in snap targets occurs, a developer would want 333 | to de-emphasize the element that was previously snapped to and emphasize the 334 | element that is currently snapped to. `previousSnapTarget` would provide a 335 | convenient way to do this by indicating the element that was previously snapped to. 336 | This might be purely a convenience for developers as they could track 337 | this across different occurrences of the snap events. However, in a scenario with an 338 | arbitrary number of snap containers, this convenience could prove to have 339 | substantial value as, without it, the developer would need to track the 340 | currently snapped element for each snap container. 341 | 342 | #### SnapEvent.snapAreas{Block|Inline} 343 | 344 | Present but less common is the desire to know all the elements which are 345 | considered snap areas, which could be useful for the use case of building a 346 | progress indicator. This is one additional piece of information that could be 347 | added to the SnapEvent interface if there appears to be strong enough desire for 348 | it. 349 | 350 | #### SnapEvent.alignedSnapAreas{Block|Inline} 351 | 352 | It is possible for multiple snap areas within a snapport to appear visually 353 | snap-aligned, even though user-agents only select one element as they must know 354 | which element to track during layout shifts. For this 355 | case, it might be worth considering whether to expose the set of elements which 356 | are so aligned. 357 | 358 | Adopting the above ideas would result in the following SnapEvent interface: 359 | 360 | ``` 361 | interface SnapEvent : Event { 362 | readonly attribute EventTarget target; 363 | 364 | readonly attribute Node? snapTargetBlock; 365 | readonly attribute Node? snappTargetInline; 366 | 367 | readonly attribute Node? previousSnapTargetBlock; 368 | readonly attribute Node? previousSnapTargetInline; 369 | 370 | readonly attribute sequence? alignedSnapAreasBlock; 371 | readonly attribute sequence? alignedSnapAreasInline; 372 | 373 | readonly attribute sequence? snapAreasBlock; 374 | readonly attribute sequence? snapAreasInline; 375 | }; 376 | ``` 377 | 378 | ### Dispatching SnapEvents to the snap target rather than the snap container 379 | 380 | Instead of dispatching the snap events to the snap container, we could dispatch 381 | them to the element that was snapped to. This makes the events 382 | less similar to scroll and scrollend events which are dispatched to the container. 383 | 384 | If a page has several snap containers and a developer wanted to style the 385 | snapped-to element according to the container it belonged to, the event would need 386 | to contain information about which scroll container the snapped-to element belongs to and in which axis it 387 | was snapped to. (Although in theory a developer could write some JavaScript to 388 | figure this out, it would seem less than ideal and quite possibly prone to 389 | errors). Catering to this style-per-container scenario would be roughly equivalent to 390 | dispatching the event to the container as proposed and perhaps suggests giving preference to 391 | maintaining similarity with scroll and scrollend events. 392 | 393 | Additionally, this might not work naturally for pseudo-elements which can 394 | be snap targets as there is not currently a way to interact with pseudo-elements 395 | via JavaScript. 396 | 397 | ### One Event vs. Two Events 398 | 399 | Much of what can be achieved with `scrollsnapchanging` and `scrollsnapchange` can 400 | probably be achieved with `scrollsnapchanging` and the existing `scrollend` event. 401 | If a developer wants to make certain style adjustments when a `scrollsnapchange` 402 | event occurs, they could simply use `scrollsnapchanging` to track the most recent 403 | choice of snap targets and, upon `scrollend`, perform their style adjustments 404 | according to the tracked information as desired. 405 | 406 | One edge case this might not cover is the case where a developer wants to style 407 | `scrollsnapchange` targets differently from `scrollsnapchanging` targets. 408 | Specifically, they would not be able to make this differentiation for a layout 409 | change that would lead to a `scrollsnapchange` event without a scroll occuring 410 | (in which case no `scroll` or `scrollend` event would be dispatched, like in 411 | [this scroll/scrollend example](https://davmila.github.io/SnapEventExamples/carousel/proxy.html) where you can delete slides. Its snap event counterpart 412 | is [this example](https://davmila.github.io/SnapEventExamples/carousel/real.html)). 413 | It might be possible to 414 | bridge this particular gap by having `scroll` events fire before 415 | `scrollsnapchanging` events. By doing so, if a developer encounters a 416 | `scrollsnapchanging` event without having observed a `scroll` event (like in the 417 | layout change scenario described) they would know not to expect a `scrollend` 418 | event and treat that `scrollsnapchanging` event like a `scrollsnapchange` event. 419 | 420 | While bridging this gap with `scroll` events seems feasible, it does leave the 421 | developer with a little bit more state to track and more edge cases to handle across JavaScript event listeners. It seems preferable to be able to rely on a dedicated `scrollsnapchange` event. 422 | 423 | Additionally, the pattern of changing/changed events could offer some ergonomic 424 | value to developers given such analogous precedents as `scroll`/`scrollend` events, 425 | and [change](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event)/[input](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) events. 426 | 427 |
428 | 429 | ## Privacy and Security Considerations 430 | 431 | ### Privacy 432 | 433 | There are no known privacy impact of this feature. 434 | 435 | ### Security 436 | 437 | There are no known security impacts of this feature. 438 | 439 | ## Contributing 440 | For now, please use this [Discussion](https://github.com/argyleink/ScrollSnapExplainers/discussions/1) for comments and questions, or a Pull Request to offer changes 🙏 441 | -------------------------------------------------------------------------------- /js-snapChanging/readme.md: -------------------------------------------------------------------------------- 1 | # JS `snapchanging` event 2 | 3 | ### Status of this Document 4 | This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. 5 | * This document status: **Active** 6 | * Expected venue: [CSSWG](https://drafts.csswg.org/) 7 | * Current version: this document 8 | 9 | ## Introduction 10 | 11 | CSS scroll snap points are often used as a mechanism to create scroll interactive "selection" components, where selection is determined with javascript with 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. 12 | 13 | ## Goals 14 | 15 | Help developers sychronize a snapped scroll item with the rest of their interface elements efficiently and effectively. 16 | 17 |
18 | 19 | ## Use Cases 20 | 21 | Scroll centric selection: 22 | - Date / Time picker 23 | - Carousel 24 | - Tabs 25 | - State / Country picker 26 | 27 |
28 | 29 | ## Proposed Solution 30 | 31 | A new javascript event for scroll snap container elements called `snapchanging` 32 | 33 | - Should fire every time, and as soon as, the UA has determined a new snap child during ongoing scroll operation. 34 | 35 |
36 | 37 | **Type**: snapchanging (inspired by [`snapped` comment](https://github.com/w3c/csswg-drafts/issues/156#issuecomment-695085852)) 38 | **Interface**: SnapEvent 39 | **Sync / Async**: Async 40 | **Bubbles**: No 41 | **Trusted Targets**: Element, Document 42 | **Cancelable**: No 43 | **Composed**: Yes 44 | **Default action**: None 45 | **Context (trusted events):** 46 | 47 |
48 | 49 | - `Event.target`: scroll container the event target is in 50 | - `SnapEvent.snappedList`: an object with 2 keys for each axis, each key returns an array of snapped targets 51 | - `SnapEvent.snappedTargetsList`: an object with 2 keys for each axis, each key returns an array of the aggregated snap children 52 | - `SnapEvent.invokedProgrammatically`: a boolean informing developers if a user or script invoked scroll that caused `snapchanged` 53 | - `SnapEvent.smoothlyScrolled`: a boolean informing developers if the snap change was instant or interpolated 54 | 55 |
56 | 57 | - `Event.target`: scroll container the event target is in 58 | - `SnapEvent.snappedTargetBlock`: element which has been newly selected to be snapped to in the block axis. 59 | - `SnapEvent.snappedTargetInline`: element which has been newly selected to be snapped to in the inline axis. 60 |
61 | 62 | ``` 63 | interface SnapEvent { 64 | readonly attribute EventTarget target; 65 | 66 | readonly attribute Node? snappedTargetBlock; 67 | readonly attribute Node? snappedTargetInline; 68 | }; 69 | ``` 70 | 71 |
72 | 73 | ## Examples 74 | 75 | #### 1: Carousel is about to change selection 76 | 77 | ```js 78 | carouselSnapContainer.addEventListener('snapchanging', event => { 79 | console.info(`will snap to ${event.snapTargetBlock.id}`); 80 | }) 81 | ``` 82 | 83 | #### 2: Tabs have begun snapping to a new element 84 | 85 | ```js 86 | tabsSnapContainer.onsnapchanging = event = > { 87 | console.info(`will snap to ${event.snapTargetInline.id}`); 88 | } 89 | ``` 90 | 91 |
92 | 93 | ## Privacy and Security Considerations 94 | 95 | ### Privacy 96 | 97 | There are no known privacy impact of this feature. 98 | 99 | ### Security 100 | 101 | There are no known security impacts of this feature. 102 | 103 | ## Contributing 104 | For now, please use this [Discussion](https://github.com/argyleink/ScrollSnapExplainers/discussions/1) for comments and questions, or a Pull Request to offer changes 🙏 105 | -------------------------------------------------------------------------------- /js-snapTo()/readme.md: -------------------------------------------------------------------------------- 1 | # JS `snapTo()` 2 | 3 | ### Status of this Document 4 | Closed. The [adding options](https://github.com/argyleink/ScrollSnapExplainers/tree/main/js-scrollToOptions_Snap-Additions) to `scrollTo()` is being persued instead. 5 | 6 | This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to 7 | problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the 8 | most current standards venue and content location of future work and discussions. 9 | * This document status: **Active** 10 | * Expected venue: [W3C](https://www.w3.org) 11 | * Current version: this document 12 | * Alternative proposal: [`scrollToOptions` "snap" Additions](https://github.com/argyleink/ScrollSnapExplainers/tree/main/js-scrollToOptions_Snap-Additions) 13 | 14 | ## Introduction 15 | 16 | [Scroll snap points](https://www.w3.org/TR/css-scroll-snap-1/) are an important and powerful CSS feature. They enable the creation of pointer agnostic stepped scroll experiences, and do so very succinctly. The problem is, they're often used alongside adjacent elements which need to represent this stepped state of scroll, aka the user needs a way to see the state of the snap. Developers today write Javascript observers or create custom functions to monitor and drive this state. 17 | 18 | ### Goals 19 | 20 | To empower the scroll snapping container with an API for iterating through snap children in a reasonable way, for all snapping containers, including [2-dimensional matrix layouts](https://codepen.io/argyleink/pen/MWWpOmz) and support for all document directions and writing modes. 21 | 22 | ### Use Cases 23 | 24 | Wiring up "snap to next", "snap to last" or "snap to the clicked item" should be trivial methods to call on a snap container: 25 | 26 | 1. Carousels 27 | 2. Sliders 28 | 3. Tabousels or Carotabs 29 | 4. Media galleries 30 | 5. Slides 31 | 6. Click/tap and snap to item 32 | 33 |
34 | 35 | ## Proposed Solution 36 | 37 | ### snapTo(``, `` or ``) 38 | - `` accepts 39 | - strings (x | y | inline | block | both) 40 | - `` accepts 41 | - strings (next | prev | first | last) 42 | - node 43 | - must be a child of the scroller and a registered snap child of the snapport 44 | 45 | Example usage: 46 | ```js 47 | scrollSnapContainer.snapTo('x', 'next') 48 | scrollSnapContainer.snapTo('y', 'prev') 49 | scrollSnapContainer.snapTo('inline', 'first') 50 | scrollSnapContainer.snapTo('block', 'last') 51 | scrollSnapContainer.snapTo('x', scrollSnapContainer.querySelector('.child-2')) 52 | ``` 53 | 54 |
55 | 56 | ### Edge Cases 57 | #### `"next"` is requested when at the end 58 | If current snapped element is `node.lastElementChild`, then no snapping occurs. The "next" element is the current element, yielding no change in snap position. 59 | 60 | #### `"prev"` is requested when at the end 61 | If current snapped element is `node.firstElementChild`, then no snapping occurs. The "prev" element is the current element, yielding no change in snap position. 62 | 63 | 64 |
65 | 66 | ## Examples 67 | ### Example 1 `.snapTo('x', 'next')` 68 | The browser should scroll in that direction the same amount as if the `right arrow key` was pressed and perform it's routine regarding proximity and mandatory next snap target discovery. 69 | 70 | ### Example 2 `.snapTo('inline', 'next')` 71 | If the browser is set to `rtl`, then the browser should scroll left the same amount as if the `left arrow key` was pressed and perform it's routine regarding proximity and mandatory next snap target discovery. 72 | 73 |
74 | 75 | ## Privacy and Security Considerations 76 | 77 | ### Privacy 78 | 79 | There are no known privacy impact of this feature. 80 | 81 | ### Security 82 | 83 | There are no known security impacts of this feature. 84 | 85 | ## Contributing 86 | For now, please use this [Discussion](https://github.com/argyleink/ScrollSnapExplainers/discussions/6) for comments and questions, or a Pull Request to offer changes 🙏 87 | --------------------------------------------------------------------------------