@jsenv/navi 0.29.417 → 0.29.419

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.
Files changed (45) hide show
  1. package/README.md +1 -1
  2. package/dist/dev/jsenv_navi.js +347 -83
  3. package/dist/dev/jsenv_navi.js.map +12 -12
  4. package/dist/jsenv_navi.js +345 -81
  5. package/dist/jsenv_navi.js.map +12 -12
  6. package/docs/AI_INSTRUCTIONS.md +177 -258
  7. package/docs/actions.md +164 -251
  8. package/docs/autofocus.md +66 -83
  9. package/docs/control_object.md +117 -214
  10. package/docs/control_value.md +121 -185
  11. package/docs/create_and_edit.md +159 -254
  12. package/docs/css_architecture.md +196 -260
  13. package/docs/data_states.md +41 -44
  14. package/docs/dialog_shape.md +91 -134
  15. package/docs/drag_interactions.md +291 -454
  16. package/docs/drag_to_travel.md +152 -748
  17. package/docs/dynamic_import.md +119 -136
  18. package/docs/error_handling.md +75 -127
  19. package/docs/field_validation.md +49 -89
  20. package/docs/form_changed.md +61 -78
  21. package/docs/group.md +141 -0
  22. package/docs/i18n.md +31 -76
  23. package/docs/interactions.md +170 -326
  24. package/docs/list_action.md +36 -51
  25. package/docs/list_refresh.md +73 -115
  26. package/docs/mobile_touch.md +56 -62
  27. package/docs/navigation.md +377 -723
  28. package/docs/network_policy.md +61 -75
  29. package/docs/pan_zoom.md +62 -103
  30. package/docs/popup_backdrop.md +128 -168
  31. package/docs/popup_lift.md +179 -268
  32. package/docs/popup_open.md +380 -651
  33. package/docs/resource.md +228 -408
  34. package/docs/route_transitions.md +333 -473
  35. package/docs/safe_area.md +102 -89
  36. package/docs/scroll.md +175 -317
  37. package/docs/state_binding.md +40 -84
  38. package/docs/testid.md +18 -23
  39. package/docs/typography.md +111 -141
  40. package/docs/view_transitions.md +106 -104
  41. package/docs/z_index.md +61 -109
  42. package/package.json +2 -2
  43. package/docs/badge_list.md +0 -83
  44. package/docs/control_group.md +0 -168
  45. package/docs/mobile_layout_pitfalls.md +0 -145
@@ -1,312 +1,231 @@
1
1
  # @jsenv/navi — context for AI assistants
2
2
 
3
- This file gives context for using `@jsenv/navi` as intended, useful whether
4
- you're reading the source directly or the built `dist/jsenv_navi.js` (e.g.
5
- inside `node_modules/@jsenv/navi/`).
3
+ This file gives context for using `@jsenv/navi` as intended, whether you read
4
+ the source or the built `dist/jsenv_navi.js` (e.g. inside
5
+ `node_modules/@jsenv/navi/`).
6
6
 
7
- `dist/jsenv_navi.js` is the bundled build of navi's actual source, not an
8
- opaque blob — JSDoc comments on individual functions/exports are preserved
9
- and carry real, useful information. What's lost in bundling is only
10
- file-level comments and anything attached to an import/re-export statement
11
- (the bundler only keeps a comment that sits directly above a declaration it
12
- retains as-is).
7
+ `dist/jsenv_navi.js` is navi's actual source, bundled — not an opaque blob. The
8
+ JSDoc on functions and exports is kept and carries real information; what
9
+ bundling loses is only file-level comments and comments attached to an
10
+ import/re-export statement.
13
11
 
14
12
  ## Where the answer to "how do I use X" is
15
13
 
16
- There is deliberately **no per-export reference page** here, and there never
17
- will be: a page per component drifts from the code the day after it is written,
18
- and the code is right there. Each source of knowledge has one job:
14
+ There is deliberately **no per-export reference page**, and there never will
15
+ be: a page per component drifts from the code the day after it is written. Each
16
+ source of knowledge has one job:
19
17
 
20
18
  - **These `docs/*.md` files** — decisions, concepts and invariants: what a
21
- mechanism is for, what it costs, what not to hand-write beside it. They are
22
- what you cannot deduce from a signature. Read the relevant one BEFORE writing
23
- code in its area; they are listed below.
24
- - **JSDoc on an export** (`@type`, `@param`) — a hint at the call site: what a
25
- prop means and what it accepts, for autocompletion and for you. It is not
26
- exhaustive and does not pretend to be: props that flow through to `Box`, the
27
- interplay between two props, everything a component composes are not repeated
19
+ mechanism is for, what it costs, what not to hand-write beside it — what a
20
+ signature cannot say. Read the relevant one BEFORE writing code in its area;
21
+ they are listed below.
22
+ - **JSDoc on an export** (`@type`, `@param`) — what a prop means and accepts. A
23
+ hint at the call site, not exhaustive: the props that flow through to `Box`,
24
+ the interplay between two props, what a component composes are not repeated
28
25
  there.
29
- - **The built code, `dist/jsenv_navi.js`** — the exhaustive truth about the API.
30
- It is navi's real source, bundled, with the JSDoc kept (see above). When a
31
- signature, a default, an accepted value or a prop nobody documented is what
32
- you need, read it there rather than guessing. It ships in the npm package, so
33
- it is always available under `node_modules/@jsenv/navi/`.
34
- - **Sources, demos and tests** — how an export is really used, and the closest
35
- thing to an example gallery: `src/**/demos/*_demo.html` exercise one component
36
- per page, prop by prop. They are NOT published to npm (the whole source tree
37
- would dwarf the package), so they live only in the repo:
38
- https://github.com/jsenv/core/tree/main/packages/frontend/navi. A project that
39
- wants an AI to work with navi seriously is better off with a local clone of
40
- that repo alongside it — the demos answer "how is this used" faster than any
41
- page could.
42
-
43
- So the loop, when the JSDoc did not answer: read the built export, then find a
44
- demo using it. Write the answer down in one of these `docs/*.md` files only if
45
- what you learned is a decision or an invariant — never as a reference page for
46
- one component.
26
+ - **The built code, `dist/jsenv_navi.js`** — the exhaustive truth about the API,
27
+ JSDoc included, always under `node_modules/@jsenv/navi/`. When a signature, a
28
+ default or an accepted value is what you need, read it there rather than
29
+ guessing.
30
+ - **Sources, demos and tests** — how an export is really used:
31
+ `src/**/demos/*_demo.html` exercise one component per page, prop by prop. They
32
+ are not published to npm, so they live only in the repo
33
+ (https://github.com/jsenv/core/tree/main/packages/frontend/navi); a project
34
+ that wants an AI to work with navi seriously keeps a local clone beside it.
35
+
36
+ So when the JSDoc did not answer: read the built export, then find a demo using
37
+ it. Write what you learned into a `docs/*.md` only if it is a decision or an
38
+ invariant — never as a reference page for one component.
47
39
 
48
40
  ## Library, but also a framework
49
41
 
50
- Navi is a library in the sense that every export is independently usable —
51
- pick just `stateSignal` or just `Table` if that's all you need, no
52
- all-or-nothing adoption required.
53
-
54
- But it's also meant as a framework: it provides low-level primitives for
55
- things most apps otherwise reinvent inconsistently — routing, async data
56
- lifecycle, CSS layering/design tokens, focus/keyboard handling, and more.
57
- When building something Navi already has a primitive for, prefer that
58
- primitive over a custom one, even if the custom one would be quicker to
59
- write for this one case — the value of using Navi as a framework comes from
60
- consistency across the app, not from any single call site.
42
+ Every export is usable on its own — just `stateSignal`, just `Table`. But navi
43
+ also provides the primitives most apps otherwise reinvent inconsistently:
44
+ routing, the async data lifecycle, CSS layering and tokens, focus and keyboard
45
+ handling, and more. When navi has a primitive for what you are building, prefer
46
+ it over a custom one, even when the custom one would be quicker to write for this
47
+ one case: the value of navi as a framework is the consistency across the app,
48
+ not any single call site.
61
49
 
62
50
  ## The docs, and when to read each
63
51
 
64
- One line per file: what it decides, and the moment to open it. The file itself
65
- holds the reasoning; nothing here substitutes for it.
52
+ One line per file: what it decides, and the moment to open it.
66
53
 
67
- `README.md` (package root) names what navi provides, area by area — the place
68
- to start when unsure which export solves a problem.
54
+ `README.md` (package root) names what navi provides, area by area — the place to
55
+ start when unsure which export solves a problem.
69
56
 
70
57
  ### Routing and movement
71
58
 
72
- - `navigation.md` — the position of the user belongs in the URL by default,
73
- and that decision is not retrofittable: routes, params, search params,
74
- `<Route>`, tab rows, `navBack`, `RouteTravel`, where a navigation lands (a
75
- `#id` landing on the element it names), a `SlideContainer` read from the URL,
76
- and what a layer drawn over the screen may say in its address. Read before
77
- writing any routing code, and before scrolling to an element on arrival.
78
- - `route_transitions.md` — a transition states a relation the user reads as a
79
- map; a pair of routes is animated by `RouteTravel` or by a transition, never
80
- both; a test waits for the page arriving, never for its address; the page
81
- being left stays mounted, hidden, until the movement is over (so what it
82
- registers globally must let the last arrival win, and an action it reads
83
- goes through `useAsyncData`, which leaves a page no longer shown alone —
84
- `.value` on the action's signals re-renders it hidden); what a transition
85
- costs.
86
- Read before animating a navigation, before writing a custom movement, and
87
- before believing a symptom that only appears once a pair is animated.
88
- - `view_transitions.md` — what the browser does to any transition an app
89
- starts itself: names unique per document, the fallback fade, rendering
90
- suspended for the whole callback, `ready` rejecting on a skip while
91
- `finished` fulfills, the top layer painted only through the root's picture,
92
- the two frames that show the live document, what makes the start of a
93
- transition restyle the whole document (an unscoped `::highlight()`, a value
94
- changed on `:root`), and a hidden popup photographed on one side only. Read
95
- before calling `document.startViewTransition`, and before putting a value on
96
- `:root` for a movement.
97
- - `drag_to_travel.md` — a pointer or a wheel pushing a whole screen aside, and
98
- who owns a press several boxes want. Read before putting anything that reads the pointer
59
+ - `navigation.md` — the position of the user belongs in the URL by default:
60
+ routes and which branch renders, search params, redirections, the back arrow
61
+ (`navBack`), where a navigation lands (a `#id` for an element), a
62
+ `SlideContainer` read from the URL, what a layer drawn over the screen may say
63
+ in its address. Before any routing code.
64
+ - `route_transitions.md` — a transition states a relation between two pages;
65
+ `RouteTravel` or a transition, never both on one pair; fixed bars and
66
+ `RouteTransitionArea`; the page being left stays mounted, hidden, until the
67
+ movement is over; a test waits for the page, never for its address; what a
68
+ transition costs. Before animating a navigation, and before believing a
69
+ symptom that only appears once a pair is animated.
70
+ - `view_transitions.md` — what the browser does to any view transition: unique
71
+ names (and none inside a page that moves), the fallback fade, a callback in
72
+ which no frame ticks, `ready` and `finished`, the top layer, what restyles the
73
+ whole document, what cannot be pointed at while one plays. Before
74
+ `document.startViewTransition`, and before putting a value on `:root` for a
75
+ movement.
76
+ - `drag_to_travel.md` — a pointer or a wheel pushing a screen aside, and who owns
77
+ a press several boxes want. Before putting anything that reads the pointer
99
78
  inside a box that travels.
100
79
 
101
80
  ### Data
102
81
 
103
- - `actions.md` — an action and its params: calling versus binding, a failing
104
- run rejects, reading is not running (`{ run: true }` is the fallback),
105
- `action` versus `uiAction`, an action and a command on one press, and a
106
- debounced binding following where its signal settles. Read before running an
107
- action from a component, and before putting a delay on params.
108
- - `resource.md` — REST state: `resource()`, the callback contract of each
109
- relationship method (not guessable), `GET_RANGE`, a search as the same
110
- `GET_MANY`, `withParams()` scopes and `dependencies`, `persist` (the last
111
- `GET` answer drawn again after a reload). Read before writing a resource, and
112
- before caching a response in localStorage; never encode a sub-route as an
113
- `op` discriminator.
114
- - `data_states.md` — `data`, `loading` and `error` are three questions; the
115
- four combinations of the first two; `loading: true` never suspends; a skeleton
116
- is told whether it is loading. Read before drawing a skeleton on `!data` or
117
- hiding content because `error` is set.
118
- - `list_refresh.md` — what a write sends back and what stays on screen
119
- meanwhile; `rerunOn` and its defaults; a paginated list re-reading its window;
120
- on the way back, the source decides the re-read and never the navigation.
121
- Read before adding verbs to `rerunOn` or remounting a list to refresh it.
82
+ - `actions.md` — calling versus binding, a failing run rejects, reading is not
83
+ running (`{ run: true }` is the fallback), `action` versus `uiAction`, an
84
+ action and a command on one press. Before running an action from a component.
85
+ - `resource.md` — `resource()` and its relations (never an `op` discriminator),
86
+ `GET_RANGE`, a search as the same `GET_MANY`, `withParams()` scopes and
87
+ `dependencies`, `persist`, a function versus the verb's instance. Before
88
+ writing a resource, and before caching a response yourself.
89
+ - `data_states.md` — `data`, `loading` and `error` are three questions;
90
+ `loading: true` never suspends; a skeleton is told whether it is loading.
91
+ Before drawing a skeleton on `!data` or hiding content because `error` is set.
92
+ - `list_refresh.md` — what a write sends back and what stays on screen meanwhile;
93
+ `rerunOn` and its defaults; coming back to a list. Before adding verbs to
94
+ `rerunOn` or remounting a list to refresh it.
122
95
  - `list_action.md` — where the `action` lives decides who waits; a row whose
123
- button works is `readOnly`, not `loading`; `parallelGuard`. Read before
96
+ button works is `readOnly`, not `loading`; `parallelGuard`. Before
124
97
  `<List readOnly={pending}>` or a second spinner beside a control's own.
125
- - `error_handling.md` — two kinds of error kept apart; where each is shown;
126
- what a failing run rejects with; the `__handled_by__` mark; the two rules of
127
- a boundary. Read before displaying an error by hand or writing a boundary.
128
- - `network_policy.md` — `setNetworkPolicy`: hold the writes, and answer the
129
- reads from the store (`reads: "store"`, no network) or let them go out
130
- (`reads: "network"`). Read before caching responses in the app, and before
131
- guarding writes in the app's own request layer.
132
- - `dynamic_import.md` — a screen's code arrives like its data: a page's
133
- import is a `routeAction` read with `loading: true` (a branch, not a
134
- `<Loading>`), a component's is started by what asks — `actionRunEffect` on a
135
- state, `useAsyncData(() => import(…))` only as the fallback;
136
- links prerun on intent what asks nothing of the address; a transition
137
- photographs the pending screen; a chunk's `import.meta.css`; a chunk that
138
- does not come is never a bug and never comes twice in one document — offer
139
- `reload()`; why not preact's `lazy`. Read before an `import()` in a component
140
- or a page, and before `prefetch={false}`.
98
+ - `error_handling.md` — a refusal versus a bug, where each is shown, displaying
99
+ is claiming, the rules of a boundary. Before displaying an error by hand or
100
+ writing a boundary.
101
+ - `network_policy.md` — `setNetworkPolicy`: writes held before the press, reads
102
+ answered from the store or let through. Before caching responses in the app, or
103
+ guarding writes in its own request layer.
104
+ - `dynamic_import.md` — code arrives like data: a page's import is a route
105
+ action, a component's is started by what asks; links prefetch on intent; a
106
+ chunk that does not come needs a fresh document; no `lazy()`. Before an
107
+ `import()` in a component or a page.
141
108
 
142
109
  ### Controls and forms
143
110
 
144
- - `state_binding.md` — the rule the API rests on: state navi shows is BOUND
145
- (`signal`), not copied back by a callback; the three shapes (`signal`,
146
- `action`, `command` + `commandFor`). Read before writing any handler whose
147
- body only assigns state.
148
- - `control_value.md` — who holds a control's value; `--navi-update` for a
149
- button proposing a value; a `defaultValue` follows the record it was read
150
- from (no `key` to remount); drawing what a picker holds (`ui={Component}`,
151
- not a signal of your own); empty keeps the shape of the question; what a
152
- `stateSignal` brings; a time of day typed (`TimeSpin`) or turned
153
- (`TimeWheel`). Read before wiring a value with `value` + `uiAction`.
154
- - `control_object.md` — one value made of several controls: `ControlGroup`
155
- versus `Form`, naming, `<Picker type="object">`, `distributeChildStates`, a
156
- settings sheet. Read before putting anything in a picker popup.
157
- - `control_group.md` — `<Group>`: several controls reading as one framed
158
- object. Read before negative margins or `border-radius: 0` by hand.
111
+ - `state_binding.md` — state navi shows is bound (`signal`), not copied back by a
112
+ callback; the three shapes (`signal`, `action`, `command` + `commandFor`).
113
+ Before any handler whose body only assigns state.
114
+ - `control_value.md` — who holds a control's value; `--navi-update` for a button
115
+ proposing one; a `defaultValue` follows the record it was read from; empty
116
+ keeps the shape of the question. Before wiring a value with `value` +
117
+ `uiAction`.
118
+ - `control_object.md` — one value made of several controls: `ControlGroup` versus
119
+ `Form`, naming, `<Picker type="object">`, a settings sheet. Before putting
120
+ anything in a picker popup.
121
+ - `group.md` — `<Group>`: several controls reading as one framed object. Before
122
+ negative margins or `border-radius: 0` by hand.
159
123
  - `form_changed.md` — a form sends nothing when nothing changed; what "changed"
160
- is measured against; `pristineKey`; `standalone`; what follows a send, and
161
- `command="--navi-void"` for a form that keeps its popup open. Read before
162
- `canSendWhileUnchanged`, and before a control inside a group whose value it
163
- has no business joining.
124
+ is measured against; `pristineKey`; `standalone`; what follows a send
125
+ (`command`, `--navi-void`). Before `canSendWhileUnchanged`, and before a control
126
+ inside a group whose value it has no business joining.
164
127
  - `field_validation.md` — what only a browser can answer versus "is this value
165
- acceptable" (validity's); constraints as props; `singleSpace="autoFix"`
166
- correcting a value instead of refusing it; `charGuard`/`maxLengthGuard`. Read
167
- before writing a constraint: if the sentence would make sense in a server's
168
- response, the rule belongs in validity.
128
+ acceptable" (@jsenv/validity's); constraints as props; `singleSpace="autoFix"`;
129
+ guards. Before writing a constraint.
169
130
  - `create_and_edit.md` — the create/edit loop assembled from `route`,
170
- `resource`, `Form` and `RouteTravel`. Read before writing a create or edit
171
- screen.
172
- - `badge_list.md` — how a `BadgeList` counts its badges, `maxLines` inside a
173
- `Picker`, why it needs a `fallback` there.
131
+ `resource`, `Form` and `RouteTravel`. Before writing a create or edit screen.
174
132
 
175
133
  ### Popups
176
134
 
177
- - `popup_open.md` — a popup owns its open state; `command` + `commandFor`;
178
- `triggerNaviCommand` as the last resort, with the event forwarded and never
179
- invented; opening ON something; a press that opens and acts is a `Picker`,
180
- and its trigger wears the wait and the error (a card that lifts, `openOn`);
181
- Escape cancels; the close cross; `signal`/`navState`/`open` and what each
182
- costs; one panel fed by a slot (`createSlot`: one `SlotFill` where the choice
183
- is made, the panel's `signal` bound to the screen's state, never
184
- `open={isFilled}`); a popup that loads data; `mount`. Read before passing `open`,
185
- calling `triggerNaviCommand`, or writing a close button.
186
- - `popup_backdrop.md` — three independent questions: a wall or not
187
- (`backdrop={false}`), what an outside press does, how far the page withdraws;
188
- `data-navi-popup-outside`, and `data-navi-popup-inside` for a box of the
189
- page whose press is not a dismissal. Read before writing CSS for a backdrop.
190
- - `popup_lift.md` — `animation="lifting"`: the pressed card brought to the
191
- front. `data-lift` names what is lifted and is rendered at once; the
192
- trigger's box is the card's box; `lift="box"` for a card, `"scene"` for a
193
- drawing framed the same way; one popup for a whole row, walked from the front
194
- through a `SlideContainer`, `liftAnchor` naming where the closing comes back
195
- to; `animation={{ open, close: "lifting" }}` for a popup that did not come out of what it
196
- lands in, the landing rendered by `onClose`; what an opening costs and where
197
- the time goes;
198
- the wall arriving from nothing, over the movement. Read before
199
- giving a `Dialog` or a `Picker` `animation="lifting"`, or before measuring
200
- why one opens slowly.
135
+ - `popup_open.md` — a popup owns its open state; `command` + `commandFor`,
136
+ `triggerNaviCommand` as the last resort; opening ON something; a press that
137
+ opens and acts is a `Picker`; Escape cancels; the close cross;
138
+ `signal`/`navState`/`open`; a popup that loads data; `mount`; a closed
139
+ `Dialog` costs one element until asked (a row may hold the dialogs of its
140
+ own actions). Before passing `open`, calling `triggerNaviCommand`, or
141
+ writing a close button.
142
+ - `popup_backdrop.md` — three independent questions: a wall or not, what an
143
+ outside press does, how far the page withdraws. Before CSS for a backdrop.
144
+ - `popup_lift.md` — `animation="lifting"`: the pressed card brought to the front.
145
+ Before giving a `Dialog` or a `Picker` `animation="lifting"`.
201
146
  - `dialog_shape.md` — bounds rather than a width, the container ceiling, the
202
- centered box versus the docked sheet (top edge by default, `"bottom"` opt-in),
203
- `marginWithContainer`. Read before
204
- deriving `smallTouchScreenSignal` in an app or writing CSS to make a dialog
205
- fit.
147
+ docked sheet. Before deriving `smallTouchScreenSignal` in an app or writing CSS
148
+ to make a dialog fit.
206
149
  - `autofocus.md` — the ladder that hands out the keyboard, `autoFocus` on a
207
- surface versus on a field, the ring decided by the modality of what asked,
208
- `moveFocusTo`. Read before calling `element.focus()` anywhere.
150
+ surface versus a field, the ring, `moveFocusTo`. Before calling
151
+ `element.focus()` anywhere.
209
152
 
210
153
  ### Gestures
211
154
 
212
155
  - `interactions.md` — a gesture is named, not read by hand: the `interactions`
213
- prop, swipes, holds and the two counted taps (`double_click`,
214
- `single_click`), the gate, `selfInteractions` for an affordance inside
215
- somebody else's box, `actionStandalone` for a run nothing above waits on,
216
- registering a detector. Read before a `pointerdown` listener of your own, and
217
- before stopping propagation to keep a popup shut.
156
+ prop, swipes, holds, counted taps, the gate, `selfInteractions`, registering a
157
+ detector. Before a `pointerdown` listener of your own, and before stopping
158
+ propagation to keep a popup shut.
218
159
  - `drag_interactions.md` — an element carried: `move`, `reorder`, `land`,
219
- `toss`, `leave`, `moving`, the `grab`/`release`/`refuse` moments, dressing the
220
- clone, and the machinery for a gesture whose product is a value. Read before
221
- moving anything with the pointer.
222
- - `pan_zoom.md` — a surface under the hand: `pan`, `zoom`, and what a touch or
223
- a wheel over it may do to the page around it.
224
- - `mobile_touch.md` — what Chrome and Safari do with a finger a gesture also
225
- reads, measured: they commit it to their own pan ~8px in (read a finger's
226
- intent before that), `touch-action` restarts at every scroller, when the
227
- click survives, the tap Chrome drops after a touch drag, the capture every
228
- finger lands with (which `hasPointerCapture` misses on a field), and how to
229
- reproduce all of it without a device (Chrome's mobile touch emulator, Safari
230
- in the iOS simulator). Read before reading a finger yourself, and before concluding a
231
- gesture works because it works with a mouse.
160
+ `toss`, `leave`, `moving`, the `grab`/`release`/`refuse` moments,
161
+ `--navi-grab`, dressing the clone. Before moving anything with the pointer.
162
+ - `pan_zoom.md` — a surface under the hand: `pan`, `zoom`, and what a touch or a
163
+ wheel over it may do to the page around it.
164
+ - `mobile_touch.md` — what Chrome and Safari do with a finger, measured, and how
165
+ to reproduce it without a device. Before reading a finger yourself, and before
166
+ concluding a gesture works because it works with a mouse.
232
167
 
233
168
  ### Layout, CSS and text
234
169
 
235
- - `css_architecture.md` — navi wins by default, defaults sit in `@layer navi`,
236
- props first; `--navi-*` versus `--component-*`; what a popup inherits from its
237
- opener; a popup and a callout are element children your `:last-child` counts;
238
- `import.meta.css` and what a `${}` costs; browsers are the consuming app's
239
- target. Read before overriding a component style, before any
240
- `import.meta.css`, and before a structural selector (`:last-child`,
241
- `:nth-child`, `:empty`) on a container holding navi components.
242
- - `safe_area.md` — the two inset families, an app narrower than the window
243
- (`--navi-app-max-width`), `data-navi-safe-area`, which viewport is which under
244
- a virtual keyboard (overlaid on Chromium only with
245
- `enableVirtualKeyboardOverlay()`: `--navi-keyboard-inset-bottom`). Read before hand-writing an offset to clear a `FixedBar`.
246
- - `scroll.md` — where scrolling happens: `header`/`body`/`footer`, `List`'s
247
- `scroller`, where a list opens and what a search does to where it is, a popup
248
- that scrolls, hover while scrolling, how a list clips at its rounded corners
249
- and why a borderless list has none; many rows: `<List.Items>` and the render
250
- window (`renderBudget`, its `{ initial, after }` form for a list opening in
251
- a click, `virtualItemSize`, `findText` for Cmd/Ctrl + F beyond the window).
252
- Read before writing CSS to make something scroll, and before rendering a
253
- collection as `<List.Item>` children.
254
- - `mobile_layout_pitfalls.md` — a horizontal overflow inflates the layout
255
- viewport on Chrome Android and miscenters every dialog; `overflow-x: clip`.
170
+ - `css_architecture.md` — navi wins by default, defaults in `@layer navi`, props
171
+ first; `--navi-*` versus `--component-*`; what a popup inherits from its
172
+ opener; structural selectors (`:last-child`…) and navi's out-of-flow children;
173
+ `import.meta.css` and what a `${}` costs; browser support. Before overriding a
174
+ component style or writing `import.meta.css`.
175
+ - `safe_area.md` — the app's rectangle and the band left free inside it, an app
176
+ narrower than the window (`--navi-app-max-width`), `data-navi-safe-area`, the
177
+ viewport under a virtual keyboard, a document wider than the screen. Before
178
+ hand-writing an offset to clear a `FixedBar`.
179
+ - `scroll.md` — where scrolling happens: `header`/`body`/`footer`, a `List`'s
180
+ `scroller`, where a list opens (a position handed back whole, `visibleCount`
181
+ included), a popup that scrolls, many rows (`<List.Items>`, the render window
182
+ and its first picture, `findText`), hover while scrolling. Before writing CSS
183
+ to make something scroll, and before rendering a collection as `<List.Item>`
184
+ children.
256
185
  - `z_index.md` — DOM order first; a `z-index` without `isolation: isolate`
257
- competes with the page; navi's bands. Read before writing a `z-index`.
258
- - `typography.md` — text is a component; `maxLines` is the one truncation
259
- (never `lineClamp`/`overflowEllipsis`/`text-overflow` by hand), on `Text`
260
- and its family, a `Picker`'s value, a `Binder`'s tabs, a `BadgeList`'s rows;
261
- `attachLastChild`; one line height for everything (`--navi-line-height`,
262
- 1.25). Read before an overflowing label, and before touching a
263
- `line-height`.
186
+ competes with the page; navi's bands. Before writing a `z-index`.
187
+ - `typography.md` — text is a component; `maxLines` is the one truncation; one
188
+ line height for everything; `BadgeList`. Before an overflowing label, and
189
+ before touching a `line-height`.
264
190
  - `i18n.md` — `interpolateText`/`<Interpolate>` for one sentence, `createI18n`
265
- for the app's texts, `naviI18n` for navi's own. Read before writing a
266
- user-visible sentence.
267
- - `testid.md` — role and accessible name first; `data-testid` lands on the
268
- control's host (on a selectable `List.Item`, its hidden input); what not to
269
- target. Read before a selector in a test.
191
+ for the app's texts, `naviI18n` for navi's own. Before writing a user-visible
192
+ sentence.
193
+ - `testid.md` — role and accessible name first; where `data-testid` lands; what
194
+ not to target. Before a selector in a test.
270
195
 
271
196
  ## Key concepts to know before guessing an API
272
197
 
273
- - **Routing is signal-based**: URL state (including search params) two-way
274
- syncs with signals. Don't build parallel state for what a route/query
275
- signal already tracks.
198
+ - **Routing is signal-based**: URL state, search params included, two-way syncs
199
+ with signals. Don't build parallel state for what a route or query signal
200
+ already tracks.
276
201
  - **A link lands on an element by its fragment**: give the element an `id` and
277
- link to `#id`. Never a search param plus `scrollIntoView` in an effect (see
278
- `navigation.md`).
279
- - **Actions** model async operations with a lifecycle (idle, running,
280
- completed, failed, aborted). Components read an action's state via
281
- `useAsyncData`, not by manually tracking loading/error booleans. Reading does
282
- not run it: a `routeAction`, a control's `action`, or
283
- `useAsyncData(action, { run: true })` starts it.
202
+ link to `#id` — never a search param plus `scrollIntoView` in an effect.
203
+ - **Actions** model async operations with a lifecycle (idle, running, completed,
204
+ failed, aborted). Components read an action's state with `useAsyncData`, not
205
+ hand-tracked loading/error booleans. Reading does not run it: a `routeAction`,
206
+ a control's `action`, or `useAsyncData(action, { run: true })` starts it.
284
207
  - **REST state is modelled with `resource()`**, and parent/child relations with
285
- `.one` / `.many` / `.scopedOne` / `.scopedMany`. Never encode a backend
286
- sub-resource (`/games/:id/candidates`) as an `op`/`type` discriminator inside
287
- one verb's callback. Searching a collection is that collection's `GET_MANY`
288
- with one more param, never a resource or an action of its own.
289
- - **`Box`** is the element every component is drawn with: `flex` (side by
290
- side), `flex="y"` (stacked), `grid`, `inline`, `alignX`/`alignY` (horizontal
291
- and vertical whatever the direction), `spacing` for the gap (there is no `gap`
292
- prop), sizes from the `"xxs"`…`"xxl"` scale. Prefer it over raw CSS for layout
293
- inside Navi apps.
208
+ `.one` / `.many` / `.scopedOne` / `.scopedMany` — never a backend sub-resource
209
+ (`/games/:id/candidates`) encoded as an `op`/`type` discriminator inside one
210
+ verb's callback. Searching a collection is that collection's `GET_MANY` with
211
+ one more param.
212
+ - **`Box`** is the element every component is drawn with: `flex` (side by side),
213
+ `flex="y"` (stacked), `grid`, `inline`, `alignX`/`alignY`, `spacing` for the gap
214
+ (there is no `gap` prop), sizes from the `"xxs"`…`"xxl"` scale. Prefer it over
215
+ raw CSS for layout.
294
216
  - **A control holds its own value.** `Input` (checkbox and radio are
295
- `<Input type="checkbox">`/`<Input type="radio">`, there is no `Checkbox`
296
- component), `Select`, `Picker` and the others work with nothing wired at all:
297
- inside a `Form`, the form reads them when it sends. When the app needs the
298
- value, bind it with `signal`. `action` is one way to react to a change, for
299
- work that can fail or take time; it is not how a control is wired. Never
300
- `onChange` + manual state (see `control_value.md`).
217
+ `<Input type="checkbox">`/`<Input type="radio">`; there is no `Checkbox`),
218
+ `Select`, `Picker` and the others work with nothing wired: inside a `Form`, the
219
+ form reads them when it sends. When the app needs the value, bind it with
220
+ `signal`. `action` reacts to a change that can fail or take time; it is not how
221
+ a control is wired. Never `onChange` + manual state.
301
222
  - **A gesture is named, not read by hand**: `interactions={{ swipe_right: … }}`
302
- on any `Box` (so on any component). Never a `pointerdown` listener of your own.
303
- - **Texts**: a user-visible sentence containing a value is written as one
304
- template with `[placeholder]`s (`interpolateText` / `<Interpolate>`), not cut
305
- into JSX fragments or concatenations. Beyond a handful of texts, an app
306
- declares them in its own `createI18n()` instance — using the English text
307
- itself as the key, whereas navi's `naviI18n` uses opaque keys. Application
308
- texts never go into `naviI18n`.
223
+ on any `Box`, so on any component — never a `pointerdown` listener of your own.
224
+ - **Texts**: a user-visible sentence containing a value is one template with
225
+ `[placeholder]`s (`interpolateText` / `<Interpolate>`), not JSX fragments or
226
+ concatenations. Beyond a handful of texts, the app declares them in its own
227
+ `createI18n()` instance, the English text as the key; navi's `naviI18n` uses
228
+ opaque keys, and application texts never go into it.
309
229
  - **View transitions**: navi components animate their own changes and never
310
- decide for the whole document. What an app starting its own transition has
311
- to know — unique names, the fallback fade, a callback in which no frame ever
312
- ticks — is `view_transitions.md`.
230
+ decide for the whole document. What an app starting its own transition has to
231
+ know is `view_transitions.md`.