@celestia-island/hikari 0.55.165 → 0.55.167

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/hikari",
3
- "version": "0.55.165",
3
+ "version": "0.55.167",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Hikari Vue 3 component library — production-grade UI components based on shittim-chest design system",
@@ -1,9 +1,12 @@
1
1
  // HkErrorReportingOverlay — fixed full-viewport host for the error landing.
2
- // Sits above every popup band (modal 1000 / dropdown 2000 / tooltip 3000 /
3
- // toast 4000): an uncaught-error takeover must never be covered.
2
+ // Mounts on document.body (NOT the overlay root — the takeover must survive
3
+ // even a broken floating tree) and sits on --hk-z-error, ABOVE the fenced
4
+ // overlay root (--hk-z-overlay-root): an uncaught-error takeover must never
5
+ // be covered — not by app chrome, not by any floating layer. Only the
6
+ // window titlebar (--hk-z-titlebar) stays above it in the Tauri shell.
4
7
 
5
8
  .hk-error-reporting-overlay {
6
9
  position: fixed;
7
10
  inset: 0;
8
- z-index: var(--hk-z-modal);
11
+ z-index: var(--hk-z-error);
9
12
  }
@@ -80,6 +80,17 @@ export const HkErrorReportingOverlay = defineComponent({
80
80
  if (!err) return null;
81
81
  const options = getErrorReportingOptions();
82
82
 
83
+ // Host-owned card: everything inside the takeover is the host's —
84
+ // content, actions, dismissal. The host keeps the capture channels
85
+ // and the crash-surviving mount; only the card is swapped.
86
+ if (options.landing) {
87
+ return (
88
+ <div class="hk-error-reporting-overlay" role="alertdialog" aria-live="assertive" aria-modal="true">
89
+ {options.landing(err)}
90
+ </div>
91
+ );
92
+ }
93
+
83
94
  const description = options.describe
84
95
  ? options.describe(err)
85
96
  : err.message || t("hikari::errors.unexpectedDesc", "An unhandled error occurred");
@@ -207,4 +207,75 @@ describe("createErrorReporting", () => {
207
207
  clearGlobalError();
208
208
  expect(overlayHost()).toBeNull();
209
209
  });
210
+
211
+ it("renders the host landing card instead of the built-in one", async () => {
212
+ vi.spyOn(console, "error").mockImplementation(() => {});
213
+ installPlugin({
214
+ landing: (err) =>
215
+ h("div", { class: "host-card", "data-code": err.name }, [
216
+ h("p", { class: "host-card__msg" }, err.message),
217
+ h(
218
+ "button",
219
+ { class: "host-card__dismiss", onClick: () => clearGlobalError() },
220
+ "Dismiss",
221
+ ),
222
+ ]),
223
+ });
224
+ reportGlobalError(new TypeError("host boom"));
225
+ await nextTick();
226
+
227
+ const host = overlayHost();
228
+ expect(host).not.toBeNull();
229
+ // The built-in card is fully replaced by the host's.
230
+ expect(host!.querySelector(".hk-error-landing")).toBeNull();
231
+ expect(host!.querySelector(".host-card")).not.toBeNull();
232
+ expect(host!.querySelector(".host-card")!.getAttribute("data-code")).toBe("TypeError");
233
+ expect(host!.querySelector(".host-card__msg")!.textContent).toBe("host boom");
234
+ // The host owns the actions: the built-in Home/Retry pair is gone.
235
+ expect(host!.textContent).not.toContain("Back to home");
236
+ expect(host!.textContent).not.toContain("Retry");
237
+ // The takeover shell stays (fixed, alert-dialog semantics).
238
+ expect(host!.querySelector(".hk-error-reporting-overlay")!.getAttribute("role")).toBe(
239
+ "alertdialog",
240
+ );
241
+ // The host card rides INSIDE the takeover shell (same fixed z-band), not
242
+ // beside it: a sibling placement would float outside --hk-z-error. The
243
+ // quadrant of these assertions that pins "same shell" is the ancestor
244
+ // relationship — descendants of the shell, not just of the data node.
245
+ expect(host!.querySelector(".hk-error-reporting-overlay .host-card")).not.toBeNull();
246
+ // The host's own dismissal tears the takeover down.
247
+ (host!.querySelector(".host-card__dismiss") as HTMLButtonElement).click();
248
+ await nextTick();
249
+ expect(overlayHost()).toBeNull();
250
+ });
251
+
252
+ it("lets landing step over title/describe and the built-in actions", async () => {
253
+ vi.spyOn(console, "error").mockImplementation(() => {});
254
+ installPlugin({
255
+ title: "ignored headline",
256
+ describe: () => "ignored description",
257
+ homeHref: "/should-not-be-used",
258
+ onHome: vi.fn(),
259
+ onRetry: vi.fn(),
260
+ landing: (err) =>
261
+ h("div", { class: "host-card", "data-msg": err.message }, [
262
+ h("p", { class: "host-card__msg" }, err.message),
263
+ ]),
264
+ });
265
+ reportGlobalError(new TypeError("own content"));
266
+ await nextTick();
267
+
268
+ const host = overlayHost()!;
269
+ // The host's card won outright; the built-in card (and its translated
270
+ // headline / description) never mounted.
271
+ expect(host.querySelector(".hk-error-landing")).toBeNull();
272
+ expect(host.querySelector(".host-card__msg")!.textContent).toBe("own content");
273
+ expect(host.textContent).not.toContain("ignored headline");
274
+ expect(host.textContent).not.toContain("ignored description");
275
+ expect(host.textContent).not.toContain("Back to home");
276
+ expect(host.textContent).not.toContain("Retry");
277
+ // The host card still receives the raw accepted record, not a
278
+ // title/describe-shaped one — the overrides never leak in.
279
+ expect(host.querySelector(".host-card")!.getAttribute("data-msg")).toBe("own content");
280
+ });
210
281
  });
@@ -32,6 +32,11 @@ export { HkErrorReportingOverlay } from "./HkErrorReportingOverlay";
32
32
  * with Home / Retry actions. Because the overlay lives on its own tiny app
33
33
  * instance, it still shows when the host tree itself is what crashed.
34
34
  *
35
+ * Hosts whose error landing carries app-specific semantics pass
36
+ * `options.landing(err)`: the takeover then renders the host's card inside
37
+ * the same crash-surviving shell — capture channels and the takeover
38
+ * guarantee stay, the built-in card and its actions step aside.
39
+ *
35
40
  * Any pre-existing `app.config.errorHandler` is chained, not discarded.
36
41
  * All capture channels are optional; `shouldReport` filters, `onError` is
37
42
  * the telemetry hook.
@@ -1,4 +1,4 @@
1
- import { ref, type Ref } from "vue";
1
+ import { ref, type Ref, type VNodeChild } from "vue";
2
2
 
3
3
  import { reportHkRuntime, type HkRuntimeHandle } from "../runtime/registry";
4
4
 
@@ -17,11 +17,23 @@ export interface HkReportedError {
17
17
  }
18
18
 
19
19
  export interface HkErrorReportingOptions {
20
- /** Headline override; defaults to `hikari::errors.defaultTitle`. */
20
+ /** Headline override; defaults to `hikari::errors.defaultTitle`.
21
+ * Ignored when `landing` is set (the host owns the whole card). */
21
22
  title?: string;
22
23
  /** Description builder; defaults to the error's message, falling back to
23
- * `hikari::errors.unexpectedDesc` when the error carries none. */
24
+ * `hikari::errors.unexpectedDesc` when the error carries none.
25
+ * Ignored when `landing` is set. */
24
26
  describe?: (err: HkReportedError) => string;
27
+ /** Host-provided takeover card — the unification hatch for hosts whose
28
+ * error landing carries app-specific semantics (localized per-code
29
+ * headlines, raw wire payloads, a Dismiss action). When set, the overlay
30
+ * renders `landing(err)` INSTEAD of the built-in HkErrorLanding, inside
31
+ * the same crash-surviving host (dedicated body app, --hk-z-error): the
32
+ * host keeps every capture channel and the takeover guarantee and owns
33
+ * the card's content and actions outright — `title`, `describe`,
34
+ * `homeHref`, `onHome` and `onRetry` are all ignored. Resolve app-shaped
35
+ * payloads from `err.error` (e.g. an Error subclass carrying them). */
36
+ landing?: (err: HkReportedError) => VNodeChild;
25
37
  /** Target of the "back to home" action. `false` hides the button.
26
38
  * Ignored when `onHome` is set. Default `/`. */
27
39
  homeHref?: string | false;
@@ -9,9 +9,21 @@
9
9
  */
10
10
  #hk-overlay-root {
11
11
  /* Fences the root's z-index space from the global stacking order —
12
- * the entire reason this container exists. */
12
+ * the entire reason this container exists. The fence disciplines the
13
+ * root's INTERNALS only; the elevation below is what lifts the root
14
+ * itself above app chrome. */
13
15
  isolation: isolate;
14
16
 
17
+ /* Document-space elevation — without it, any fixed/sticky chrome with
18
+ * a positive z-index (the admin status bar sits at
19
+ * --hk-z-toolbar: 50) painted over EVERY floating layer: the
20
+ * connection popover rendered behind the panel footer (2026-10-15
21
+ * defect report). The band lives in scale.scss between the old
22
+ * floating bands and --hk-z-error, preserving the pre-isolation
23
+ * order: content chrome < floating layers < error landing < titlebar.
24
+ * The fallback must match overlayRoot.ts's OVERLAY_ROOT_DOCUMENT_Z. */
25
+ z-index: var(--hk-z-overlay-root, 90000);
26
+
15
27
  /* The root itself doesn't intercept pointer events; each teleported
16
28
  * child restores its own pointer-events. */
17
29
  pointer-events: none;
@@ -0,0 +1,185 @@
1
+ import { readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { join, dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { compile } from "sass";
6
+ import { describe, expect, it } from "vitest";
7
+
8
+ import { OVERLAY_ROOT_DOCUMENT_Z, ensureOverlayRoot } from "./overlayRoot";
9
+
10
+ /**
11
+ * Compiled-stylesheet + cross-file contract for the overlay root's
12
+ * document-space elevation. `isolation` fences the root's INTERNAL
13
+ * ordinals but does not lift the root above app chrome — without the
14
+ * elevation, any fixed/sticky bar with a positive z-index (the admin
15
+ * status bar's --hk-z-toolbar: 50) painted over every floating layer
16
+ * and the connection popover ended up behind the panel footer
17
+ * (2026-10-15 defect report). These assertions pin the elevation in
18
+ * all three places it must agree: scale.scss (the band), the compiled
19
+ * sheet, and the inline self-sufficient cssText.
20
+ */
21
+ const overlayDir = resolve(dirname(fileURLToPath(import.meta.url)));
22
+ const vueSrc = resolve(overlayDir, "..");
23
+
24
+ function sourcesIn(dir: string): string[] {
25
+ const out: string[] = [];
26
+ for (const entry of readdirSync(dir)) {
27
+ if (entry === "node_modules" || entry === "__tests__") continue;
28
+ const path = join(dir, entry);
29
+ if (statSync(path).isDirectory()) out.push(...sourcesIn(path));
30
+ else if (/\.(scss|tsx)$/.test(entry)) out.push(path);
31
+ }
32
+ return out;
33
+ }
34
+
35
+ describe("overlay root document-space elevation contract", () => {
36
+ const css = compile(resolve(overlayDir, "overlayRoot.scss"), {
37
+ style: "expanded",
38
+ }).css;
39
+ const scale = readFileSync(resolve(vueSrc, "scale.scss"), "utf8");
40
+ const source = readFileSync(resolve(overlayDir, "overlayRoot.ts"), "utf8");
41
+
42
+ it("declares the elevation in the sheet, with the fence kept", () => {
43
+ expect(css).toContain("z-index: var(--hk-z-overlay-root, 90000);");
44
+ expect(css).toContain("isolation: isolate;");
45
+ expect(css).toContain("position: relative;");
46
+ });
47
+
48
+ it("keeps scale.scss, the sheet fallback and the TS fallback on one value", () => {
49
+ const band = scale.match(/--hk-z-overlay-root:\s*(\d+)/);
50
+ // A missing match is a broken contract, not an empty pass.
51
+ expect(band).not.toBeNull();
52
+ expect(Number(band![1])).toBe(OVERLAY_ROOT_DOCUMENT_Z);
53
+ // The inline cssText interpolates the same constant.
54
+ expect(source).toContain(
55
+ "z-index:var(--hk-z-overlay-root,${OVERLAY_ROOT_DOCUMENT_Z})",
56
+ );
57
+ });
58
+
59
+ it("parks the band between the floating bands and error/titlebar", () => {
60
+ const value = (name: string) => {
61
+ const m = scale.match(new RegExp(`--hk-z-${name}:\\s*(\\d+)`));
62
+ expect(m, `--hk-z-${name} must stay defined in scale.scss`).not.toBeNull();
63
+ return Number(m![1]);
64
+ };
65
+ const band = value("overlay-root");
66
+ // Above every content-chrome band (the defect: footer at 50 won).
67
+ expect(band).toBeGreaterThan(value("tooltip"));
68
+ expect(band).toBeGreaterThan(value("modal"));
69
+ expect(band).toBeGreaterThan(value("toolbar"));
70
+ // Below the two surfaces that intentionally stay on top, exactly as
71
+ // in the pre-isolation global scale.
72
+ expect(band).toBeLessThan(value("error"));
73
+ expect(band).toBeLessThan(value("titlebar"));
74
+ });
75
+
76
+ it("inlines a self-sufficient pre-stylesheet elevation", () => {
77
+ const root = ensureOverlayRoot();
78
+ expect(root.id).toBe("hk-overlay-root");
79
+ expect(root.style.getPropertyValue("z-index")).toContain(
80
+ "var(--hk-z-overlay-root",
81
+ );
82
+ expect(root.style.getPropertyValue("position")).toBe("relative");
83
+ });
84
+
85
+ it("lets no z-index consumer land between the root band and the error band", () => {
86
+ // The exact gap class the 2026-10-15 fix closed: a document-space
87
+ // surface that must own the top (hikari's error landing) sat at
88
+ // --hk-z-modal = 5000, and the elevated root silently jumped over
89
+ // it. Every --hk-z-* consumer must resolve STRICTLY below the root
90
+ // band (content chrome) or be one of the two intentional
91
+ // top-owners, which must stay ABOVE it — nothing ties the band
92
+ // (a tie would be DOM-order roulette) and nothing lands between
93
+ // the two bands. Consumers are matched anywhere INSIDE a
94
+ // z-index-bearing line, so calc()/nested-fallback wrappers
95
+ // (`var(--x, var(--hk-z-scrollbar))`) are still inventoried; their
96
+ // ARITHMETIC is not evaluated (a calc() offset off a top-owner
97
+ // token is textual guessing — same out-of-scope class as raw
98
+ // numbers, audited by hand at ≤ 51 in this package). Single-line
99
+ // declarations only.
100
+ const bandValues = new Map<string, number>();
101
+ for (const [, name, num] of scale.matchAll(/--hk-z-([a-z-]+):\s*(\d+)/g)) {
102
+ bandValues.set(name, Number(num));
103
+ }
104
+ // The ladder itself must be present — a broken parse must not
105
+ // masquerade as an empty pass.
106
+ expect(bandValues.size).toBeGreaterThanOrEqual(10);
107
+ expect(bandValues.get("overlay-root")).toBe(OVERLAY_ROOT_DOCUMENT_Z);
108
+
109
+ const sources = sourcesIn(vueSrc);
110
+ // Positive floor for the walk: hundreds of sheets/components live
111
+ // under src/ — fewer means the glob broke, not that the code did.
112
+ expect(sources.length).toBeGreaterThanOrEqual(200);
113
+
114
+ const found = new Map<string, Set<string>>(); // token name -> files
115
+ for (const file of sources) {
116
+ for (const line of readFileSync(file, "utf8").split("\n")) {
117
+ if (!/\bz-index\s*[:=]|\bzIndex\s*[:=]/.test(line)) continue;
118
+ for (const [, name] of line.matchAll(/var\(\s*--hk-z-([a-z-]+)/g)) {
119
+ if (!found.has(name)) found.set(name, new Set());
120
+ found.get(name)!.add(file);
121
+ }
122
+ }
123
+ }
124
+ // Positive controls: the scan must see the known top-owners and the
125
+ // root itself, or the line patterns stopped matching.
126
+ for (const required of ["overlay-root", "error", "titlebar", "toolbar"]) {
127
+ expect(
128
+ found.has(required),
129
+ `scan lost the --hk-z-${required} consumer — patterns rotted`,
130
+ ).toBe(true);
131
+ }
132
+
133
+ // File binding, not just name presence: the error takeover's
134
+ // "never covered" promise lives in ONE sheet — assert IT consumes
135
+ // the top-owner band, so the promise survives even if a second
136
+ // --hk-z-error consumer appears elsewhere.
137
+ const errorSheet = join(vueSrc, "errorReporting", "HkErrorReportingOverlay.scss");
138
+ expect(readFileSync(errorSheet, "utf8")).toMatch(
139
+ /z-index:\s*var\(\s*--hk-z-error[\s,)]/,
140
+ );
141
+ const titlebarSheet = join(vueSrc, "components", "HkTitleBar.scss");
142
+ expect(readFileSync(titlebarSheet, "utf8")).toMatch(
143
+ /z-index:\s*var\(\s*--hk-z-titlebar[\s,)]/,
144
+ );
145
+
146
+ const topOwners = new Set(["error", "titlebar"]);
147
+ const rootSheet = resolve(overlayDir, "overlayRoot.scss");
148
+ for (const [name, files] of found) {
149
+ const value = bandValues.get(name);
150
+ expect(
151
+ value,
152
+ `--hk-z-${name} consumed (e.g. ${files.values().next().value}) but not defined in scale.scss`,
153
+ ).not.toBeUndefined();
154
+ if (topOwners.has(name)) {
155
+ // A top-owner demoted to/below the root band would be painted
156
+ // over by the floating tree (the error landing sat at
157
+ // --hk-z-modal = 5000 when this guard was born).
158
+ expect(
159
+ value!,
160
+ `--hk-z-${name} (${value}) must stay above the overlay root band ` +
161
+ `(${OVERLAY_ROOT_DOCUMENT_Z}) — consumers: ${[...files].join(", ")}`,
162
+ ).toBeGreaterThan(OVERLAY_ROOT_DOCUMENT_Z);
163
+ } else if (name === "overlay-root") {
164
+ // The band itself: ONLY the root's own sheet may sit at it —
165
+ // any other consumer at exactly 90000 races the root by DOM
166
+ // order.
167
+ for (const file of files) {
168
+ expect(
169
+ file,
170
+ `--hk-z-overlay-root consumed at ${file} — only ${rootSheet} may tie the band`,
171
+ ).toBe(rootSheet);
172
+ }
173
+ } else {
174
+ expect(
175
+ value!,
176
+ `--hk-z-${name} (${value}) at ${[...files].join(", ")} must resolve ` +
177
+ `strictly below the overlay root band (${OVERLAY_ROOT_DOCUMENT_Z}) ` +
178
+ `or above the error band — ties and in-between values get painted ` +
179
+ `over by (or paint over) the floating tree`,
180
+ ).toBeLessThan(OVERLAY_ROOT_DOCUMENT_Z);
181
+ }
182
+ }
183
+ });
184
+ });
185
+
@@ -5,10 +5,21 @@
5
5
  *
6
6
  * Inside this root, z-index values only compete with each other (the
7
7
  * `isolation` property creates a new stacking context), so overlay types
8
- * use simple ordinals (1 = lowest … 10 = highest) instead of the old
9
- * global thousand-scale bands. A tooltip at z-index 8 inside the root
10
- * always paints above a modal at z-index 3 inside the root, regardless
11
- * of what the global stacking space looks like outside.
8
+ * use simple ordinal bands instead of the old global thousand-scale. A
9
+ * tooltip at ordinal 300 inside the root always paints above a modal at
10
+ * ordinal 10 inside the root, regardless of what the global stacking
11
+ * space looks like outside.
12
+ *
13
+ * The fence only disciplines the root's INTERNALS, though — by itself
14
+ * `isolation` does not lift the root above app chrome. A fixed/sticky
15
+ * bar with a positive document-space z-index (the admin status bar sits
16
+ * at `--hk-z-toolbar: 50`) painted over EVERY floating layer, dropping
17
+ * the connection popover behind the panel footer (2026-10-15 defect
18
+ * report). The root therefore carries ONE document-space z-index of its
19
+ * own — `--hk-z-overlay-root` (fallback 90000) — parked between the old
20
+ * floating bands and `--hk-z-error`, so the pre-isolation global order
21
+ * is preserved: content chrome < floating layers < error landing <
22
+ * window titlebar.
12
23
  *
13
24
  * The root is created lazily on first use (`ensureOverlayRoot()`), so
14
25
  * consumers don't need to include a bootstrap component — any Teleport
@@ -19,14 +30,14 @@
19
30
  *
20
31
  * | z-index | Layer | Examples |
21
32
  * |---------|----------------|---------------------------------------|
22
- * | 1 | modal backdrop | HkModal's fixed backdrop |
23
- * | 2 | modal frame | HkModal body, dialogs |
24
- * | 3 | blocking toast | HkBlockingToast |
25
- * | 4 | toast | HkToast, persistent toasts |
26
- * | 5 | loading veil | HkLoadingVeil |
27
- * | 6 | anchored popup | HkSelectPanel popout, menus, popover |
28
- * | 7 | breadcrumb | HkModalBreadcrumb strip |
29
- * | 8 | tooltip | HkTooltip (always on top) |
33
+ * | 5 | modal backdrop | HkModal's fixed backdrop |
34
+ * | 10 | modal frame | HkModal body, dialogs |
35
+ * | 50 | blocking toast | HkBlockingToast |
36
+ * | 100 | toast | HkToast, persistent toasts |
37
+ * | 150 | loading veil | HkLoadingVeil |
38
+ * | 200 | anchored popup | HkSelectPanel popout, menus, popover |
39
+ * | 250 | breadcrumb | HkModalBreadcrumb strip |
40
+ * | 300 | tooltip | HkTooltip (always on top) |
30
41
  *
31
42
  * Components that need to layer within the SAME band (e.g. multiple
32
43
  * stacked modals) use their own local stacking mechanisms (the modal
@@ -48,6 +59,15 @@ export const OVERLAY_Z = {
48
59
  tooltip: 300,
49
60
  } as const;
50
61
 
62
+ /**
63
+ * The root's document-space elevation fallback — must match the
64
+ * `--hk-z-overlay-root` value in `scale.scss` (kept honest by
65
+ * overlayRoot.styles.test.ts). Inlined because the root is created
66
+ * before any stylesheet loads; the var() indirection lets the sheet
67
+ * retune the band without touching this file.
68
+ */
69
+ export const OVERLAY_ROOT_DOCUMENT_Z = 90000;
70
+
51
71
  /**
52
72
  * Idempotently create and append the overlay root to `<body>`.
53
73
  * Called on module load — the first `import` of this module guarantees
@@ -61,10 +81,16 @@ export function ensureOverlayRoot(): HTMLElement {
61
81
  root.id = "hk-overlay-root";
62
82
  root.setAttribute("aria-hidden", "true");
63
83
  // Inline styles — this element is created BEFORE any stylesheet
64
- // loads, so it must be self-sufficient. The `isolation` is the
65
- // whole point: it fences the root's internal z-index space from
66
- // the document's global stacking order.
67
- root.style.cssText = "isolation:isolate;pointer-events:none;";
84
+ // loads, so it must be self-sufficient. `isolation` fences the
85
+ // root's internal z-index space from the document's global stacking
86
+ // order; `position` + `z-index` then lift the fenced root above
87
+ // every content-chrome band (see the module doc) — `position` is
88
+ // repeated here so the elevation also applies pre-stylesheet. The
89
+ // var() name is the sheet's retuning path; the fallback constant
90
+ // must equal scale.scss's --hk-z-overlay-root.
91
+ root.style.cssText =
92
+ "isolation:isolate;pointer-events:none;position:relative;" +
93
+ `z-index:var(--hk-z-overlay-root,${OVERLAY_ROOT_DOCUMENT_Z});`;
68
94
  document.body.appendChild(root);
69
95
  }
70
96
  return root;
package/src/scale.scss CHANGED
@@ -69,7 +69,11 @@
69
69
  * base content (0) to the window titlebar (100001); a higher band
70
70
  * always paints above a lower one. Tooltips sit above toasts AND
71
71
  * modals so their text stays readable and copyable while any overlay
72
- * is showing (user direction 2026-10-15). */
72
+ * is showing (user direction 2026-10-15). --hk-z-overlay-root is the
73
+ * ONE band consumed by the fenced floating-layer root itself rather
74
+ * than by a component: it lifts #hk-overlay-root above every
75
+ * content-chrome band while keeping the pre-isolation order (floating
76
+ * layers < error landing < window titlebar). */
73
77
  --hk-z-base: 0; /* base content stacking context */
74
78
  --hk-z-above: 1; /* local bump: checkbox marks, switch thumbs */
75
79
  --hk-z-scrollbar: 10; /* scrollbar tracks and corner rulers */
@@ -85,6 +89,9 @@
85
89
  --hk-z-modal: 5000; /* modal backdrop + frame */
86
90
  --hk-z-tooltip: 6000; /* tooltip — above toasts and modals so its text
87
91
  is always readable and copyable */
92
+ --hk-z-overlay-root: 90000; /* #hk-overlay-root — every floating layer's
93
+ document-space elevation (overlay/overlayRoot.ts);
94
+ above all content chrome, below error + titlebar */
88
95
  --hk-z-error: 95000; /* error landing overlay (covers everything) */
89
96
  --hk-z-titlebar: 100001; /* window titlebar (Tauri shell) */
90
97
 
@@ -71,7 +71,11 @@
71
71
  * base content (0) to the window titlebar (100001); a higher band
72
72
  * always paints above a lower one. Tooltips sit above toasts AND
73
73
  * modals so their text stays readable and copyable while any overlay
74
- * is showing (user direction 2026-10-15). */
74
+ * is showing (user direction 2026-10-15). --hk-z-overlay-root is the
75
+ * ONE band consumed by the fenced floating-layer root itself rather
76
+ * than by a component: it lifts #hk-overlay-root above every
77
+ * content-chrome band while keeping the pre-isolation order (floating
78
+ * layers < error landing < window titlebar). */
75
79
  --hk-z-base: 0; /* base content stacking context */
76
80
  --hk-z-above: 1; /* local bump: checkbox marks, switch thumbs */
77
81
  --hk-z-scrollbar: 10; /* scrollbar tracks and corner rulers */
@@ -87,6 +91,9 @@
87
91
  --hk-z-modal: 5000; /* modal backdrop + frame */
88
92
  --hk-z-tooltip: 6000; /* tooltip — above toasts and modals so its text
89
93
  is always readable and copyable */
94
+ --hk-z-overlay-root: 90000; /* #hk-overlay-root — every floating layer's
95
+ document-space elevation (overlay/overlayRoot.ts);
96
+ above all content chrome, below error + titlebar */
90
97
  --hk-z-error: 95000; /* error landing overlay (covers everything) */
91
98
  --hk-z-titlebar: 100001; /* window titlebar (Tauri shell) */
92
99