|
| 1 | + |
| 2 | +We'd like to make it easier and more reliable for developers to build user experiences |
| 3 | +where navigation can be done by moving parts of the UI |
| 4 | +in a way that can correspond to scrolling and then scroll snapping. |
| 5 | +This navigation should, in some cases, be able to change the URL, |
| 6 | +so that the resulting state can be linked to and shared. |
| 7 | + |
| 8 | +## Use cases |
| 9 | + |
| 10 | +Let's start by examining some use cases that this feature should address, |
| 11 | +and thus make it simpler for developers to address these use cases well and declaratively. |
| 12 | + |
| 13 | +### Panels or carousel within a single page app |
| 14 | + |
| 15 | +One use case is a set of panels or a carousel within a single page app, |
| 16 | +where swiping left or right moves to the adjacent panel |
| 17 | +(and typically each panel fills the entire width of the UI). |
| 18 | +It should be possible for navigating between panels to declaratively |
| 19 | +change the document URL (without a new document load, like `pushState`) |
| 20 | +to reflect the currently selected panel. |
| 21 | +In many cases there are also either buttons/tabs at the top or bottom, |
| 22 | +or arrows at the sides, |
| 23 | +to move between these panels. |
| 24 | + |
| 25 | +(This use case can also integrate with patching |
| 26 | +and thus with lazy loading of the resources |
| 27 | +needed for each panel. |
| 28 | +This integration can work particularly well |
| 29 | +because a browser can start preloading content |
| 30 | +at the beginning of a gesture |
| 31 | +if that gesture might lead to a snap that activates and loads content.) |
| 32 | + |
| 33 | +This use case is somewhat simpler since the activation here |
| 34 | +is intended to be nondestructive. |
| 35 | +(It does potentially cause resource loading, |
| 36 | +but that's normal for exploring additional pieces of an app's UI.) |
| 37 | + |
| 38 | +Here are two examples taken from native Android apps: |
| 39 | + |
| 40 | +> [!NOTE] |
| 41 | +> (INCLUDE VIDEO HERE from https://photos.app.goo.gl/9YonAy8GKg7vyQ1P6) |
| 42 | +> |
| 43 | +> In this case, the Android Photos app allows swiping from side to side |
| 44 | +> to navigate between photos. |
| 45 | +> If this were on the Web, |
| 46 | +> it would be expected for the URL to change |
| 47 | +> when the UI moves to a different photo. |
| 48 | +> This allows the URL of a single photo to be shared. |
| 49 | +
|
| 50 | +> [!NOTE] |
| 51 | +> (INCLUDE VIDEO HERE from https://photos.app.goo.gl/1ABe699gY39oiZmH9) |
| 52 | +> |
| 53 | +> In this case, the maps app allows similar swiping from side-to-side |
| 54 | +> to navigate between successive trains departing from a single subway station |
| 55 | +> in the same direction. |
| 56 | +> The trains can also be selected by tapping on the train departure time |
| 57 | +> at the top of the UI. |
| 58 | +> In this particular case URLs are probably less critical |
| 59 | +> since the information in this particular UI is extremely transient, |
| 60 | +> but in general this sort of UI may want to have shareable URLs to represent each item. |
| 61 | +
|
| 62 | +</div> |
| 63 | + |
| 64 | +### Pull to refresh |
| 65 | + |
| 66 | +A slightly more advanced use case is pull-to-refresh. |
| 67 | +This is a common UI pattern where scrolling a scrollable area |
| 68 | +(often but not always a list) |
| 69 | +to the top |
| 70 | +and then continuing to scroll a little bit past the top |
| 71 | +(where the scrolling goes slower than the user's finger |
| 72 | +to provide the feeling of resistance or pulling) |
| 73 | +causes the content to reload or to add new conent. |
| 74 | +This requires having UI in the overscroll area (the area past the end) |
| 75 | +of a scrollable container. |
| 76 | +When activated, the UI in the overscroll area can trigger the refresh. |
| 77 | + |
| 78 | +(This depends on a separate plan for rendering content in the overscroll area.) |
| 79 | + |
| 80 | +In this use case, |
| 81 | +snapping to and thus activating |
| 82 | +an element that is in the overscroll area |
| 83 | +would trigger the refresh. |
| 84 | + |
| 85 | +This use case requires more care because the operation is destructive. |
| 86 | +This means that this use case requires more careful consideration |
| 87 | +of the implications for accessibility issues, |
| 88 | +such as how it works with keyboard navigation, |
| 89 | +how it integrates with assistive technology, |
| 90 | +etc. |
| 91 | + |
| 92 | +> [!NOTE] |
| 93 | +> (INCLUDE VIDEO HERE from https://photos.app.goo.gl/JFuVWaXqnroUjeMH7) |
| 94 | +> |
| 95 | +> In this case, the Web interface of X (formerly Twitter) uses pull-to-refresh |
| 96 | +> to load new content if any is available |
| 97 | +> (which in this case it is not). |
| 98 | +
|
| 99 | +### Swipe actions (like swipe to dismiss/delete) |
| 100 | + |
| 101 | +Another use case that is similar in many ways to pull-to-refresh |
| 102 | +is swipe-to-dismiss or swipe-to-delete. |
| 103 | +Many user interfaces with dialogs or notifications |
| 104 | +allow dismissing that dialog or notification |
| 105 | +by swiping it to the side. |
| 106 | +Many user interfaces with lists of items that the user can delete |
| 107 | +(for example, lists of messages) |
| 108 | +allow swiping an individual item to the side to either delete an item |
| 109 | +or remove it from the particular list that it is in (for example, to archive it). |
| 110 | + |
| 111 | +This has many of the same concerns as pull-to-refresh: |
| 112 | +it depends on interaction with the overscroll area, |
| 113 | +and it is a destructive (perhaps more destructive than pull-to-refresh) |
| 114 | +action that requires the same level care regarding accessibility issues. |
| 115 | +(Many of the issues appear to be the same, |
| 116 | +although some aspects may also be different.) |
| 117 | + |
| 118 | +> [!NOTE] |
| 119 | +> (INCLUDE VIDEO HERE from https://photos.app.goo.gl/N7D5k5WaDDCeoDQ27) |
| 120 | +> |
| 121 | +> In this case, the Android Gmail app uses a swipe gesture to |
| 122 | +> remove a message from the list of messages being shown. |
| 123 | +
|
| 124 | +## Additional constraints |
| 125 | + |
| 126 | +Some things worth thinking about when designing a solution for this are: |
| 127 | + |
| 128 | +* An element, particularly one within a single-page app, |
| 129 | + may be activated in this way more than once in the lifetime of the page. |
| 130 | + (This implies that while integration |
| 131 | + with [declarative patching](patching-explainer.md) is useful, |
| 132 | + the integration shouldn't be so tight that a second activation is |
| 133 | + difficult or has poor developer ergonomics.) |
| 134 | + |
| 135 | +* While in many cases the snapping involved will be |
| 136 | + `scroll-snap-type: mandatory`, |
| 137 | + the design should probably consider `scroll-snap-type: proximity` as well. |
| 138 | + |
| 139 | +## Possible directions |
| 140 | + |
| 141 | +When considering possible technical solutions to these problems, |
| 142 | +we have to consider what goes on the HTML side and what goes on the CSS side. |
| 143 | +Keeping appropriate parts of the solution in HTML is likely necessary |
| 144 | +(although not sufficient) for ensuring good accessibility of the result. |
| 145 | + |
| 146 | +### Element ID activation |
| 147 | + |
| 148 | +One simple possibility that would address only a subset of the use cases |
| 149 | +is adding a mechanism so that snapping to an element |
| 150 | +navigates so that the URL's fragment is the element's ID. |
| 151 | +This could be a very simple solution to a subset of the use cases. |
| 152 | +It seems like it might be problematic |
| 153 | +because there is no other way to perform the navigation, |
| 154 | +and thus the ability to navigate to that URL |
| 155 | +(and thus be able to share the URL, etc.) |
| 156 | +might not be discoverable or accessible to |
| 157 | +users using assistive technologies or alternative input methods. |
| 158 | +It's also not clear how this could eventually be tied into |
| 159 | +[routemaps](route-matching-explainer.md), |
| 160 | +[declarative patching](patching-explainer.md), |
| 161 | +or more advanced features. |
| 162 | + |
| 163 | +### Snapping to an element that is activated |
| 164 | + |
| 165 | +Another possibility is that the element being snapped to is an element that |
| 166 | +can be activated, such as a link or a button. |
| 167 | +Activating this element could then lead to |
| 168 | +[declarative patching](patching-explainer.md) that replaces the element |
| 169 | +(or its contents) with the expected content. |
| 170 | + |
| 171 | +This approach seems problematic for three reasons. |
| 172 | +First, users who need to use methods other than swiping |
| 173 | +(for example, keyboard or assistive technology) |
| 174 | +might be unable to discover or activate the element. |
| 175 | +Second, it might be problematic (in a carousel type situation) |
| 176 | +for activations other than the first, |
| 177 | +since the declarative patching has already happened and the button or link |
| 178 | +might now be gone. |
| 179 | +Third, the developer ergonomics are rather weird |
| 180 | +and probably too tied to declarative patching. |
| 181 | + |
| 182 | +### Connecting to button or link |
| 183 | + |
| 184 | +Another possibility that seems like it might |
| 185 | +address a larger portion of the use cases (perhaps all, or perhaps not) |
| 186 | +is a mechanism that indicates that snapping to an element *S* |
| 187 | +activates a different (link or button) element *A*. |
| 188 | +This mechanism could be a new HTML attribute |
| 189 | +(that goes on the element *S*) |
| 190 | +referencing the ID of another element (the element *A*). |
| 191 | +The HTML attribute could have an IDL reflection that returns an element |
| 192 | +(like the existing IDL attributes |
| 193 | +`ariaActiveDescendantElement` and `popoverTargetElement`). |
| 194 | + |
| 195 | +We've talked to developers who have taken this sort of approach |
| 196 | +in their own site-specific code, |
| 197 | +so it seems likely to be viable for at least some uses. |
| 198 | + |
| 199 | +This approach may be problematic in cases where it doesn't make sense |
| 200 | +to link to a separate element that can be activated. |
| 201 | +However, this might be a good thing, |
| 202 | +since that seems like a sign of a design that is likely to be inaccessible. |
| 203 | +Further investigation is needed to determine whether |
| 204 | +there are significant use cases |
| 205 | +where this design wouldn't work |
| 206 | +and where the fact that it doesn't work |
| 207 | +isn't a sign of a significant accessibility issue. |
| 208 | + |
| 209 | +That the element being activated would be a link or button |
| 210 | +(which would effectively go through its normal activation process) |
| 211 | +seems likely to mean that this approach would integrate well with |
| 212 | +other ongoing work like |
| 213 | +[routemaps](route-matching-explainer.md) and |
| 214 | +[declarative patching](patching-explainer.md). |
| 215 | + |
| 216 | +There are still some interesting accessibility questions with this approach. |
| 217 | +In particular, given an approach where there's always an alternative way to do the same activation, |
| 218 | +it's not clear how desirable it is to expose the snap-to-activate concepts to assistive technology. |
| 219 | +Further, if we do want to expose them to assistive technology, |
| 220 | +we need to figure out an appropriate way to do that |
| 221 | +(that makes the right tradeoffs between producing good experiences |
| 222 | +for users of assistive technology |
| 223 | +in cases where developers do think about assistive technology users |
| 224 | +and test with assistive technology, |
| 225 | +and producing at least minimally acceptible experiences |
| 226 | +in the cases where developers don't do those things |
| 227 | +but do follow general best practices in their code). |
0 commit comments