@jsenv/navi 0.29.10 → 0.29.11

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.
@@ -31,15 +31,15 @@ or completely invisible.
31
31
  which triggers the browser's native `scrollIntoView`. So after opening, the page automatically
32
32
  scrolls to bring the dialog into view — scrolling the user away from where they were.
33
33
 
34
- ### The fix: wrap the app content in a scroll container
34
+ ### The fix: clip the horizontal overflow in a wrapper
35
35
 
36
36
  The root cause is the document overflowing horizontally. The fix is to **never let the
37
- document itself overflow in X** — instead, contain horizontal scroll inside a child wrapper.
37
+ document itself overflow in X** — instead, contain the overflow inside a child wrapper.
38
38
 
39
39
  ```html
40
40
  <body>
41
- <!-- This wrapper is the scroll container for the whole app -->
42
- <div style="overflow-x: auto;">
41
+ <!-- This wrapper absorbs the horizontal overflow of the whole app -->
42
+ <div style="overflow-x: clip;">
43
43
  <!-- all app content goes here -->
44
44
  </div>
45
45
 
@@ -54,20 +54,74 @@ With this structure:
54
54
  - `position: fixed; margin: auto` centers the dialog correctly
55
55
  - No ghost empty space at the bottom
56
56
 
57
- As a safety net, add `overflow-x: hidden` on `html` and `body` to prevent any content
57
+ As a safety net, add `overflow-x: clip` on `html` and `body` to prevent any content
58
58
  that forgets to use a wrapper from inflating the layout viewport:
59
59
 
60
60
  ```css
61
61
  html,
62
62
  body {
63
- overflow-x: hidden;
63
+ overflow-x: clip;
64
64
  }
65
65
  ```
66
66
 
67
- Note: `overflow: hidden` on `<html>` does create a new containing block, which could
68
- break `position: fixed` in edge cases but in practice this safety net is worth having,
69
- and any element that needs correct `position: fixed` behavior (like `<dialog>`) should
70
- be moved to `document.body` directly anyway (which `dialog.jsx` already does).
67
+ What it costs: content wider than the screen is cut off instead of reachable by dragging.
68
+ When some element genuinely needs to be scrolled horizontally (a wide table, a carousel),
69
+ give **that element** its own `overflow-x: auto` the wrapper stays `clip`.
70
+
71
+ ### Why `clip` and not `auto` or `hidden`
72
+
73
+ `clip` is the only value that clips without turning the box into a **scroll container**.
74
+ `auto`, `scroll` and `hidden` all create one, and that has two consequences that show up
75
+ far from the wrapper:
76
+
77
+ - **Every `position: sticky` in the app sticks to that wrapper**, because sticky resolves
78
+ against the nearest scroll container in the DOM — not against the box the app considers
79
+ its scroller. The wrapper grows with its content and never scrolls, so nothing sticks
80
+ anymore: sticky headers and `<List groupBy>` group labels just scroll away with the
81
+ content.
82
+ - **Worse than not sticking: the sticky element is offset downwards.** The rectangle a
83
+ sticky element sticks within is the scroll container's box shrunk by its
84
+ `scroll-padding` (CSS Position L3), and Chromium applies that for an element scroll
85
+ container. A wrapper carrying `data-navi-fixed-bar-space` has
86
+ `scroll-padding-top: var(--navi-fixed-bar-space-top)`, so labels come to rest at
87
+ `scroll-padding-top + top` — a group label floating a hundred pixels below the bar,
88
+ covering the content above it.
89
+
90
+ `overflow-x: clip` also lets `overflow-y` stay `visible`, where `hidden`/`auto` force the
91
+ other axis to `auto`.
92
+
93
+ Note: `overflow: hidden` on `<html>` would additionally create a new containing block,
94
+ which could break `position: fixed` in edge cases. Any element that needs correct
95
+ `position: fixed` behavior (like `<dialog>`) should be moved to `document.body` directly
96
+ anyway (which `dialog.jsx` already does).
97
+
98
+ ### The wrapper is a net, not a fix: find what overflows
99
+
100
+ Clipping makes the symptom disappear, and with it the signal. Something wider than
101
+ the screen is a layout bug wherever it happens — a width in px, a `min-width`, a grid
102
+ of fixed columns, an unbreakable string coming from the data. The wrapper only keeps
103
+ that bug from taking the whole mobile viewport down with it.
104
+
105
+ So in dev, ask who overflows:
106
+
107
+ ```js
108
+ import { detectHorizontalOverflow } from "@jsenv/navi";
109
+
110
+ if (import.meta.dev) {
111
+ detectHorizontalOverflow({ root: document.querySelector("#main") });
112
+ }
113
+ ```
114
+
115
+ It outlines the culprits in red and names them in the console, at load and whenever
116
+ the layout changes. It reports the **outermost** box that sticks out (its children
117
+ stick out because it does), and stays quiet about what cannot reach the document:
118
+ anything inside a box that scrolls or clips on its own — a wide table in its own
119
+ `overflow-x: auto` container is doing the right thing — and anything `position: fixed`
120
+ or in the top layer.
121
+
122
+ Measuring against the wrapper matters here: once it is in `clip` there is no scrollable
123
+ overflow left to read, so `scrollWidth > clientWidth` reports nothing. The rectangles of
124
+ the descendants are what tells.
71
125
 
72
126
  ### Also: place `<dialog>` before content in the DOM
73
127
 
@@ -83,7 +137,8 @@ before opening for this reason.
83
137
 
84
138
  ### Summary
85
139
 
86
- | Cause | Symptom | Fix |
87
- | ------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------ |
88
- | Document overflows horizontally | Layout viewport inflated → ghost space below → dialog miscentered | Wrap app content in `overflow-x: auto` container |
89
- | `<dialog>` at end of DOM | Page scrolls to dialog on `showModal()` | Place `<dialog>` first in `<body>` |
140
+ | Cause | Symptom | Fix |
141
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
142
+ | Document overflows horizontally | Layout viewport inflated → ghost space below → dialog miscentered | Wrap app content in `overflow-x: clip` container |
143
+ | `<dialog>` at end of DOM | Page scrolls to dialog on `showModal()` | Place `<dialog>` first in `<body>` |
144
+ | Wrapper uses `auto`/`hidden` | It becomes a scroll container: sticky headers and group labels stick to it (and get offset by its `scroll-padding`) | Use `clip`; put `overflow-x: auto` on the wide element itself |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.10",
3
+ "version": "0.29.11",
4
4
  "type": "module",
5
5
  "description": "Library of components including navigation to create frontend applications",
6
6
  "repository": {