@jsenv/navi 0.29.15 → 0.29.16

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.
@@ -40,6 +40,9 @@ consistency across the app, not from any single call site.
40
40
  `header`/`body`/`footer` on, `FixedBar` space, `List`'s `scroller`, and
41
41
  scroll inside a `Dialog`/`Popover`. Read it before writing CSS to make
42
42
  something scroll — navi almost certainly already has the prop.
43
+ - `docs/z_index.md` — stacking: why DOM order is the first tool, what a
44
+ `z-index` without `isolation: isolate` actually competes against, and the
45
+ values navi's own popups/bars/tables use. Read it before writing a `z-index`.
43
46
  - `docs/MOBILE_LAYOUT_PITFALLS.md` — mobile-specific layout gotchas (viewport
44
47
  units, virtual keyboard, safe areas).
45
48
  - Source code on GitHub: https://github.com/jsenv/core/tree/main/packages/frontend/navi/src
@@ -0,0 +1,111 @@
1
+ # Stacking (z-index)
2
+
3
+ What we want: **an element that must paint in front of another one, without
4
+ that decision reaching anything else on the page.** A `z-index` written without
5
+ a stacking context does the opposite — it is a claim against the whole
6
+ document, so a card's own detail ends up in front of the top bar.
7
+
8
+ Reach for the tools in this order.
9
+
10
+ ## 1. DOM order first
11
+
12
+ Between positioned elements that all have `z-index: auto`, the last one written
13
+ paints in front. Moving a tag is the cheapest way to reorder, and it can never
14
+ affect anything outside its parent.
15
+
16
+ ```jsx
17
+ // The stamp paints over the content because it comes after it. No z-index.
18
+ <Box position="relative">
19
+ <CardContent />
20
+ <Stamp />
21
+ </Box>
22
+ ```
23
+
24
+ If the element that must be in front cannot move in the DOM (it is a slot, it
25
+ is written by a consumer), that is a real reason to go further — "I did not
26
+ think about the order" is not.
27
+
28
+ ## 2. A `z-index` without a stacking context is compared against the page
29
+
30
+ `z-index: 5` does not mean "in front of my siblings". It means "in front of
31
+ everything painted lower **in the nearest stacking context**", and when no
32
+ ancestor opens one, that context is the document root — including `FixedBar`,
33
+ sticky list-group labels, and popups.
34
+
35
+ This is the failure that keeps happening: a small `z-index` inside a card wins
36
+ against a bar written at the other end of the page, because both are competing
37
+ in the same, page-wide context.
38
+
39
+ ## 3. If a `z-index` is genuinely needed, isolate
40
+
41
+ `isolation: isolate` on the common parent makes its descendants' `z-index`
42
+ values local to it — they order among themselves and the parent as a whole
43
+ takes its place among its own siblings.
44
+
45
+ ```css
46
+ .my_card {
47
+ /* z-index values inside the card mean "inside the card" */
48
+ isolation: isolate;
49
+ }
50
+ ```
51
+
52
+ A `z-index` inside a reusable component without this is a bug waiting for its
53
+ call site: the component behaves differently depending on where it is dropped.
54
+
55
+ ## 4. What creates a stacking context without you asking
56
+
57
+ `opacity` below 1, `transform`, `filter`, `backdrop-filter`, `will-change`,
58
+ `contain: paint`, `mix-blend-mode`, and a positioned element with a `z-index`
59
+ other than `auto` all open one. Two consequences:
60
+
61
+ - something you faded or moved suddenly paints as a block, in front of or
62
+ behind a sibling it used to interleave with;
63
+ - a `z-index` you wrote deeper inside stops reaching where you expected,
64
+ because one of these ancestors now caps it.
65
+
66
+ The answer is still DOM order — write the layer that must be on top last —
67
+ not a `z-index` "to repair it". Adding one on top of an unnoticed stacking
68
+ context is how a value ends up tuned to a symptom.
69
+
70
+ ## 5. The values navi plays with
71
+
72
+ | What | Value | Notes |
73
+ | ----------------------------------------------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
74
+ | Top layer (`Dialog`/`Popover` with `layer="top"`) | above everything | Browser top layer — no `z-index` involved, nothing in the page can beat it |
75
+ | `Dialog`/`Popover` with `layer="local"`, and their backdrop | `--navi-popup-z-index` (1000) `+ stack order` | The stack order increments per open, so the last opened wins |
76
+ | Callout (validation messages) | `--callout-z-index` (1000) | |
77
+ | `FixedBar` | 1 | `position: fixed` — it opens its own stacking context, but competes in the root one at 1, which is exactly why a stray `z-index: 2` anywhere on the page lands in front of it |
78
+ | `List` sticky group labels, `List` footer | 1 | Local to the list |
79
+ | `Table` (sticky cells, drag, resize) | 1–7, see `src/control/table/z_indexes.js` | Derived from each other, never literals |
80
+
81
+ Two things to read from this table:
82
+
83
+ - navi itself keeps its values low and relative, except for popups, which sit
84
+ at 1000 precisely so nothing has to guess;
85
+ - an app that writes a number above 1 is already competing with `FixedBar`.
86
+ Write `isolation: isolate` on the parent instead, and the number stops
87
+ meaning anything outside it.
88
+
89
+ ## A card that stacks three layers with no `z-index`
90
+
91
+ A cover link that makes the whole card clickable, content above it, and a stamp
92
+ above everything — DOM order alone, in painting order:
93
+
94
+ ```jsx
95
+ <Box relative isolation="isolate">
96
+ {/* Painted first, fills the card, catches the clicks */}
97
+ <Link href={href} absolute inset aria-label={title} />
98
+ {/* After it, so text and buttons are on top and remain interactive */}
99
+ <Box relative>
100
+ <Text bold>{title}</Text>
101
+ <Text>{description}</Text>
102
+ </Box>
103
+ {/* Last, so it covers the two others */}
104
+ <Stamp />
105
+ </Box>
106
+ ```
107
+
108
+ The only positioning trick here is `position: relative` on the content: a
109
+ positioned element paints above a non-positioned one regardless of order, so
110
+ the content has to be positioned too to stay above the cover link. `isolation`
111
+ is there for what the card's children may do later, not for this file.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.15",
3
+ "version": "0.29.16",
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.4",
32
+ "@jsenv/dom": "0.17.5",
33
33
  "@jsenv/humanize": "1.7.8",
34
34
  "@jsenv/validity": "0.4.2"
35
35
  },