@jsenv/navi 0.29.122 → 0.29.124

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.
@@ -99,9 +99,11 @@ consistency across the app, not from any single call site.
99
99
  before writing a constraint of your own: if the sentence would make sense in a
100
100
  server's response, the rule belongs in validity and the constraint is only its
101
101
  browser-side caller.
102
- - `docs/css_architecture.md` — how Navi's CSS layering works, and the
103
- supported ways to override component styles (props > CSS variables > direct
104
- rule overrides, in that preference order).
102
+ - `docs/css_architecture.md` — how Navi's CSS layering works, the supported
103
+ ways to override component styles (props > CSS variables > direct rule
104
+ overrides, in that preference order), and what a popup inherits from the
105
+ element that opened it: which tokens a surface takes back, the color
106
+ keywords and their `-mix` ratios, and why a ratio is a `:root` knob only.
105
107
  - `docs/safe_area.md` — where the app is in the window and what covers it:
106
108
  the two inset families (`--navi-app-inset-*` for what is pinned to an edge,
107
109
  `--navi-safe-area-inset-*` for what flows inside), how an app declares itself
@@ -171,6 +171,82 @@ Note that scoping to an ancestor is not enough: `.my-sidebar { --link-color-pres
171
171
 
172
172
  When a component default deserves to be themed globally, promote it: declare a `--navi-<component>-<thing>` in [navi_css_vars.js](../src/navi_css_vars.js) and make the component default read `var(--navi-…)`.
173
173
 
174
+ #### A surface is a new paper: what reaches a popup from its opener
175
+
176
+ A popup (`Dialog`, `Popover`, everything built on them) and a callout are
177
+ painted in the top layer but live in the DOM subtree of what opened them. For
178
+ the cascade they are descendants of that element: every inherited property and
179
+ every custom property declared on an ancestor reaches them, the top layer
180
+ changing nothing about it. A dark card that writes in white, declared on the
181
+ card, is the color a dialog opened from that card starts with.
182
+
183
+ Navi takes back what it knows a surface needs of its own — declared on the
184
+ surface element itself, so it beats whatever an ancestor declared, whatever
185
+ the layer (see the table above):
186
+
187
+ | taken back | where |
188
+ | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
189
+ | the ink (`color`) | `--navi-popup-color` on `.navi_popover` / `.navi_dialog`; `revert` (the UA's `CanvasText`) on `.navi_callout` |
190
+ | text properties that belong to the opener (alignment, transform, shadow, spacing, wrapping) | `surface_text_css.js`, which also says what is deliberately kept |
191
+ | the five color keywords `--navi-color-primary/secondary/emphasis/discrete/hint` | re-declared on each surface in `navi_css_vars.js` |
192
+
193
+ Everything else declared on a container is inherited by the popup it opens.
194
+ That is the shape of the bug to expect: a token an app pinned on a container
195
+ for that container's paper — a background, a border color, a spacing, one of
196
+ its own `--app-*` — arriving on a popup that has a different paper. When it
197
+ hits, the answers are, in order:
198
+
199
+ 1. the token is about the paper and navi owns it: re-declare it on the surface,
200
+ next to the color keywords;
201
+ 2. the token is the app's: the app declares it on the popup too
202
+ (`.navi_dialog { --app-thing: … }`, unlayered), or writes it against the ink
203
+ (`currentColor`) so it follows whatever ink the surface writes in.
204
+
205
+ ##### Ink, ratio, paper: the color keywords
206
+
207
+ `primary` is an absolute (the surface's ink, `--navi-surface-text-color`). The
208
+ other four are formulas on `currentColor` — `secondary` is
209
+ `color-mix(in srgb, currentColor 80%, transparent)` — so they follow the ink of
210
+ whatever writes them: a dark card sets `color: white` and nothing else, and its
211
+ secondary is white at 80%. Which is also what lets a surface re-declare the
212
+ same formulas and have them come out right: on a popup writing in black, they
213
+ mix black.
214
+
215
+ The share of ink in each is a token: `--navi-color-secondary-mix` (80%),
216
+ `--navi-color-emphasis-mix` (50%), `--navi-color-discrete-mix` (60%),
217
+ `--navi-color-hint-mix` (25%). A theme that wants a fainter secondary sets the
218
+ ratio on `:root`, and the page and its popups agree:
219
+
220
+ ```css
221
+ :root {
222
+ --navi-color-secondary-mix: 70%;
223
+ }
224
+ ```
225
+
226
+ The ratio is a `:root` knob only, and this is the trap to know about. A `var()`
227
+ inside a custom property is substituted where **that** property is declared,
228
+ not where it is read: `--navi-color-secondary` is declared on `:root` (and on
229
+ each surface), so the `80%` is baked in there and a card inherits the
230
+ already-mixed formula. A ratio set on a container changes nothing for the
231
+ container's own text — and it _is_ read by the next surface opened from it,
232
+ which re-declares the formula and resolves the `var()` against the inherited
233
+ ratio:
234
+
235
+ ```css
236
+ .card {
237
+ /* ❌ the card stays at 80%; the dialog it opens goes to 88% */
238
+ --navi-color-secondary-mix: 88%;
239
+ /* ✅ the card's paper — stops at the next surface */
240
+ --navi-color-secondary: rgb(255 255 255 / 88%);
241
+ }
242
+ ```
243
+
244
+ So a theme is a number on `:root`; a paper is a color, pinned on the container,
245
+ and it stops at the surface. What must not be done to make container ratios
246
+ work is declaring the formulas on `*` so they resolve on every element: the
247
+ pinned keyword above would then be overwritten on each of the card's children,
248
+ and a paper could no longer say anything.
249
+
174
250
  #### An app narrower than the screen
175
251
 
176
252
  An app that never spans the whole window — a phone-shaped column centered in a
@@ -246,11 +322,13 @@ Overriding the actual CSS rules (not the variables) is intentionally hard — th
246
322
 
247
323
  ## Summary
248
324
 
249
- | What you want to change | How to do it |
250
- | ---------------------------- | -------------------------------------------------------------------------- |
251
- | One component instance | Component prop or `style` attribute |
252
- | All instances of a component | `--component-*` in unlayered app CSS, on a selector matching the component |
253
- | A global design token | `--navi-*` on `:root` |
254
- | How wide popups may ever get | `--navi-app-max-width` on `:root` |
255
- | A structural layout rule | Expose a new CSS variable (contribute) |
256
- | What a variant decided | A prop — a variant only ever moves defaults, so props keep winning |
325
+ | What you want to change | How to do it |
326
+ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
327
+ | One component instance | Component prop or `style` attribute |
328
+ | All instances of a component | `--component-*` in unlayered app CSS, on a selector matching the component |
329
+ | A global design token | `--navi-*` on `:root` |
330
+ | The share of ink in `secondary`/`emphasis`/`discrete`/`hint` | `--navi-color-*-mix` on `:root` — never on a container |
331
+ | A container's own paper (a dark card) | `color` on the container, plus a `--navi-color-*` keyword pinned if its formula reads wrong there; both stop at the next popup |
332
+ | How wide popups may ever get | `--navi-app-max-width` on `:root` |
333
+ | A structural layout rule | Expose a new CSS variable (contribute) |
334
+ | What a variant decided | A prop — a variant only ever moves defaults, so props keep winning |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.122",
3
+ "version": "0.29.124",
4
4
  "type": "module",
5
5
  "description": "Library of components including navigation to create frontend applications",
6
6
  "repository": {