@jsenv/navi 0.29.49 → 0.29.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -123,14 +123,17 @@ page moved a little, and it looked wrong":
123
123
 
124
124
  - **the leftovers of a scroll**, handed up the chain until something moves: a
125
125
  list inside the box reaches its end and the page scrolls behind the travel.
126
- `overscroll-behavior-<axis>: contain !important` on the travelling box and
127
- everything inside it and **written once and for all, never while the finger
128
- is down**: a browser decides what a gesture may do when the gesture BEGINS (at
129
- the touchstart, at the first wheel event), so a property written after that
130
- decision arrives too late for the gesture it was meant for. That is what
131
- "usually it does not move, sometimes it does" is made of. On the travelling
132
- axis only — the other one is the content's own scrolling, and containing does
133
- not stop scrolling anyway, it stops spilling;
126
+ `overscroll-behavior-<axis>: contain !important`, **written once and for all,
127
+ never while the finger is down**: a browser decides what a gesture may do when
128
+ the gesture BEGINS (at the touchstart, at the first wheel event), so a
129
+ property written after that decision arrives too late for the gesture it was
130
+ meant for. That is what "usually it does not move, sometimes it does" is made
131
+ of — and it is why none of this can be done in JS at the moment it is needed.
132
+ On the travelling axis only — the other one is the content's own scrolling,
133
+ and containing does not stop scrolling anyway, it stops spilling. WHERE that
134
+ property is written is a question of its own, and the engines do not answer it
135
+ the same way: see [What is contained, and what still
136
+ leaks](#what-is-contained-and-what-still-leaks);
134
137
  - **the elastic bounce** at the end of a page, and the swipe that goes back in
135
138
  history with it: `overscroll-behavior: none` on the document while a finger is
136
139
  down. Same lateness applies, so this is a last resort behind the rule above
@@ -144,6 +147,75 @@ Both are written by the gesture itself (`data-drag-travel-gesture` and
144
147
  time goes on bouncing. `preventDefault()` on each move says the same thing to
145
148
  the browser for what those two properties do not cover.
146
149
 
150
+ ### What is contained, and what still leaks
151
+
152
+ Containing is only ever read on a **scroll container** — an element that clips,
153
+ in the browser's sense, whether or not it has anything to scroll. Three places
154
+ could carry it: the travelling box, everything inside it, or the scrollers
155
+ themselves. Which of the three works is an engine question, and the answer
156
+ splits in two:
157
+
158
+ - **Blink** asks every scroll container between the pointer and the page whether
159
+ the gesture may go past it, _even one with nothing to scroll_. Containing the
160
+ travelling box is therefore the whole answer — and saying it to everything
161
+ inside is actively harmful: anything that happens to clip (a line with an
162
+ ellipsis, a rounded card, the invisible checkbox that covers a selectable row)
163
+ becomes a **dead zone under the wheel**, a container that stops the gesture
164
+ and has nothing to move with it. This is the bug that made a list refuse the
165
+ wheel inside a popup and only answer its scrollbar.
166
+ - **Gecko and WebKit** ask only the containers that actually scroll. The
167
+ travelling box is walked past — it travels, it does not scroll — so the
168
+ scroller itself has to be told, and saying it to everything is harmless there.
169
+
170
+ So `drag_to_travel.js` writes it on the box for everyone, and on everything
171
+ inside only outside Blink (`@supports not (-webkit-app-region: none)`, one of
172
+ the few properties that names an engine rather than a user agent string).
173
+
174
+ It also names the scrollers a browser makes on its own — `textarea`,
175
+ `select[multiple]`, `select[size]` — wherever they are inside the box. Nobody
176
+ declared those, so nothing can find them by looking; being native is exactly
177
+ what makes them nameable. `input` is deliberately not among them: it has
178
+ nothing to scroll on the axis anything travels on, and containing it is how a
179
+ row-wide invisible checkbox becomes a hole under the wheel. The cost of naming
180
+ them is that an empty `textarea` is contained too — a browser cannot be asked
181
+ "only if it scrolls" — so on Blink a wheel over one moves nothing rather than
182
+ the list around it.
183
+
184
+ On Blink this leaves the boxes that **do not clip**, since those are never
185
+ asked. Of navi's own: `SlideContainer` clips (`overflow: hidden`) and a
186
+ `SidePanel` is a `Dialog`, which scrolls (`overflow: auto`) — both are asked. A
187
+ `RouteTravel` box does not clip and must not be made to: a scroll container
188
+ there would become the nearest one for every `position: sticky` inside the pages
189
+ it holds. A row marked swipeable by `interactions` does not clip either.
190
+
191
+ Which is why navi contains what it KNOWS scrolls, in `box.jsx`:
192
+
193
+ ```css
194
+ [data-drag-travel*="y"] [data-scrollable] {
195
+ overscroll-behavior-y: contain !important;
196
+ }
197
+ ```
198
+
199
+ `[data-scrollable]` is worn by a `Box` that ASKED for `overflow: auto|scroll`,
200
+ never by one that merely clips — so this cannot make a dead zone the way `*`
201
+ does, and it puts the containment exactly where every engine reads it. A list, a
202
+ dialog body, a scrolling panel inside a travelling page are covered on Blink
203
+ again.
204
+
205
+ What still leaks, and only on Blink, and only under a box that does not clip: a
206
+ scroller **nobody declared and no tag names** — a bare
207
+ `<div style="overflow: auto">`, a widget from elsewhere. Its leftovers reach the
208
+ page, which is the old symptom in a much smaller corner. Two ways out, per case:
209
+ give the scroller a `Box` with an `overflow` (it is then declared), or contain it
210
+ by hand. The general fix belongs to Blink.
211
+
212
+ A note for whoever tests this: **Firefox cannot be measured with a synthetic
213
+ wheel**. Playwright dispatches one outside APZ, where Gecko enforces
214
+ `overscroll-behavior`, so containment never shows up there — inline, from a
215
+ stylesheet, headless or headed alike. The non-Blink branch is therefore left
216
+ exactly as it always was rather than tuned against a measurement that does not
217
+ exist.
218
+
147
219
  ### A hand reaching for something still moving is reaching for THAT thing
148
220
 
149
221
  A gesture arriving while a travel is playing takes **that travel** over — it does
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.49",
3
+ "version": "0.29.50",
4
4
  "type": "module",
5
5
  "description": "Library of components including navigation to create frontend applications",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "prepublishOnly": "npm run build"
30
30
  },
31
31
  "dependencies": {
32
- "@jsenv/dom": "0.17.15",
32
+ "@jsenv/dom": "0.17.16",
33
33
  "@jsenv/humanize": "1.7.8",
34
34
  "@jsenv/validity": "0.4.2"
35
35
  },