@mkbabb/glass-ui 3.5.0 → 3.6.0

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 (80) hide show
  1. package/dist/{DataTable-DyKc_26a.js → DataTable-BGVXx0Ww.js} +1 -1
  2. package/dist/{NumberFieldContent-jO-zGOHf.js → NumberFieldContent-Bhhmk95p.js} +1 -1
  3. package/dist/animated-digit.js +1 -1
  4. package/dist/api/index.d.ts +2 -1
  5. package/dist/api.js +1 -1
  6. package/dist/aurora.js +259 -81
  7. package/dist/{button-i8Ek5vvg.js → button-BcMzVrqs.js} +2 -2
  8. package/dist/button.js +1 -1
  9. package/dist/carousel.js +1 -1
  10. package/dist/color.js +1 -1
  11. package/dist/components/custom/aurora/composables/color.d.ts +35 -2
  12. package/dist/components/custom/aurora/composables/glSetup.d.ts +1 -1
  13. package/dist/components/custom/aurora/composables/uniformBridge.d.ts +6 -0
  14. package/dist/components/custom/aurora/constants/presets.d.ts +9 -0
  15. package/dist/components/custom/aurora/constants/shaders/composition.glsl.d.ts +1 -1
  16. package/dist/components/custom/aurora/index.d.ts +3 -2
  17. package/dist/components/custom/constellation/Constellation.vue.d.ts +45 -0
  18. package/dist/components/custom/constellation/constellationField.d.ts +85 -0
  19. package/dist/components/custom/constellation/index.d.ts +3 -0
  20. package/dist/components/custom/dock/GlassDock.vue.d.ts +34 -3
  21. package/dist/composables/glass/canvas2d/index.d.ts +2 -0
  22. package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +48 -0
  23. package/dist/composables/glass/index.d.ts +2 -0
  24. package/dist/composables/motion/core/index.d.ts +1 -0
  25. package/dist/composables/motion/index.d.ts +1 -0
  26. package/dist/composables/motion/useAnimatedNumberMap.d.ts +29 -0
  27. package/dist/composables/motion/useStagger.d.ts +50 -0
  28. package/dist/confirm-dialog.js +1 -1
  29. package/dist/constellation.d.ts +1 -0
  30. package/dist/constellation.js +182 -0
  31. package/dist/controls.js +1 -1
  32. package/dist/dark.js +1 -1
  33. package/dist/data-table.js +1 -1
  34. package/dist/dock.js +121 -102
  35. package/dist/expandable-container.js +1 -1
  36. package/dist/forms.js +1 -1
  37. package/dist/glass-ui.css +1 -1
  38. package/dist/glass-ui.js +79 -78
  39. package/dist/goo-blob.js +3 -3
  40. package/dist/icon-tooltip.js +1 -1
  41. package/dist/keyboard.js +1 -1
  42. package/dist/labeled-field.js +2 -2
  43. package/dist/motion-core.js +90 -49
  44. package/dist/motion.js +31 -21
  45. package/dist/number-field.js +1 -1
  46. package/dist/responsive-tabs.js +1 -1
  47. package/dist/search.js +1 -1
  48. package/dist/sortable-list.js +1 -1
  49. package/dist/styles/dock.css +185 -1
  50. package/dist/styles/glass-refract.css +60 -0
  51. package/dist/styles/glass-specular-track.css +43 -130
  52. package/dist/styles/glass.css +243 -27
  53. package/dist/styles/index.css +1 -0
  54. package/dist/styles/theme.css +6 -0
  55. package/dist/styles/tokens.css +33 -0
  56. package/dist/tabs.js +2 -2
  57. package/dist/{useAnimatedNumber-BF6r64lA.js → useAnimatedNumber-DfVgP4te.js} +1 -1
  58. package/dist/useCanvas2D-BeNJqqIS.js +95 -0
  59. package/dist/watercolor-dot.js +1 -1
  60. package/package.json +21 -1
  61. package/src/styles/dock.css +185 -1
  62. package/src/styles/glass-refract.css +60 -0
  63. package/src/styles/glass-specular-track.css +43 -130
  64. package/src/styles/glass.css +243 -27
  65. package/src/styles/index.css +1 -0
  66. package/src/styles/theme.css +6 -0
  67. package/src/styles/tokens.css +33 -0
  68. /package/dist/{IconTooltip-BTyYn4mr.js → IconTooltip-DZlCkU-0.js} +0 -0
  69. /package/dist/{Input-CU9CNKmo.js → Input-CRJ2owjN.js} +0 -0
  70. /package/dist/{UnderlineTabs-Cq_AD03t.js → UnderlineTabs-CuIK16JI.js} +0 -0
  71. /package/dist/{color-rkK4RMx2.js → color-DS-iLRVM.js} +0 -0
  72. /package/dist/{compile-DVgAxagk.js → compile-C62K_FpC.js} +0 -0
  73. /package/dist/{constants-DsCdlK9I.js → constants-C8NMonuo.js} +0 -0
  74. /package/dist/{presets-1OhFpaIC.js → presets-Dyv9Zl7W.js} +0 -0
  75. /package/dist/{prng-Bz_1Tydc.js → prng-CjVgzjQT.js} +0 -0
  76. /package/dist/{useGlobalDark-CWiaCoEw.js → useGlobalDark-D7R9OypB.js} +0 -0
  77. /package/dist/{useIntersectionPause-DAdVPVp4.js → useIntersectionPause-DsQYGc8V.js} +0 -0
  78. /package/dist/{useKeyboardShortcuts-BQfnAHHW.js → useKeyboardShortcuts-i8tHd5WO.js} +0 -0
  79. /package/dist/{useSortable-DnyGXKKY.js → useSortable-muI29agz.js} +0 -0
  80. /package/dist/{useViewTransition-CJnqg6wl.js → useViewTransition-DI0Rs2rV.js} +0 -0
@@ -700,6 +700,32 @@
700
700
  --glass-edge-light: inset 0 0 0 0.75px hsl(0 0% 100% / 0.18);
701
701
  --glass-edge-light-dark: inset 0 0 0 0.75px hsl(0 0% 100% / 0.10);
702
702
 
703
+ /* AW.W23 — content-aware adaptive tint (the Baseline-2025 "tint informed by
704
+ surrounding content" axis, WWDC25 sess. 219). A surface can tint toward a
705
+ consumer-provided dominant backdrop color via a `color-mix(in oklab, …)`
706
+ recipe (oklab is the mwg-preferred space for tint generation; NEVER a
707
+ lightness-shift in `oklch(from …)` — browsers don't gamut-map yet).
708
+
709
+ DEFAULT is zero delta: `--glass-tint-source` is the warm-white card itself
710
+ and `--glass-tint-strength` is 0%, so the rung background mixes 0% toward
711
+ the source = byte-identical to today. A consumer sets `--glass-tint-source`
712
+ to a sampled aurora hue + a strength ≤30% to bias the surface. Alpha-safe:
713
+ the mix is bounded ≤30% and never re-points the card's own alpha. */
714
+ --glass-tint-source: var(--card);
715
+ --glass-tint-strength: 0%;
716
+
717
+ /* AW.W23 — chromatic edge dispersion (the "polished glass" rim fringe Apple
718
+ shows). A warm fringe on one edge + a cool fringe on the other, low-alpha,
719
+ biased warm-cream/NCSU-red (NOT iOS-blue). Composed OVER the W22 uniform
720
+ rim under `prefers-reduced-transparency: no-preference`; dropped under
721
+ `reduce` (it is a transparency garnish). The two directional inset rings
722
+ read as light refracting through the glass edge. */
723
+ --glass-fringe-warm: oklch(0.92 0.06 40 / 0.22);
724
+ --glass-fringe-cool: oklch(0.90 0.05 230 / 0.16);
725
+ --glass-edge-dispersion:
726
+ inset 0.75px 0 0 0 var(--glass-fringe-warm),
727
+ inset -0.75px 0 0 0 var(--glass-fringe-cool);
728
+
703
729
  /* Chassis curvature illusion — a subtle radial gradient at the chassis
704
730
  top centre that lifts the surface without committing to literal 3D.
705
731
  Composed into <InstrumentChassis>'s background stack. */
@@ -1586,6 +1612,13 @@
1586
1612
  `.dark` (mirroring the `--glass-specular` remap above). The lit token
1587
1613
  resolves to the `-dark` alpha on the deeper canvas. */
1588
1614
  --glass-edge-light: var(--glass-edge-light-dark);
1615
+
1616
+ /* AW.W23 — the chromatic-fringe + adaptive-tint dark arm. The fringe drops
1617
+ to a lower-luminance warm/cool pair on the deep canvas; `--glass-tint-
1618
+ source` auto-flips with `--card` (no override needed — the default already
1619
+ resolves the dark card). */
1620
+ --glass-fringe-warm: oklch(0.78 0.07 40 / 0.18);
1621
+ --glass-fringe-cool: oklch(0.74 0.06 230 / 0.14);
1589
1622
  --glass-curvature-overlay: radial-gradient(
1590
1623
  ellipse at 50% -20%,
1591
1624
  hsl(0 0% 100% / 0.06),
package/dist/tabs.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as e } from "./cn-DJXf4yaB.js";
2
2
  import { t } from "./_plugin-vue_export-helper-C1je1s0u.js";
3
3
  import { i as n, n as r, r as i, t as a } from "./TooltipProvider-D-JrSqDu.js";
4
- import { t as o } from "./UnderlineTabs-Cq_AD03t.js";
4
+ import { t as o } from "./UnderlineTabs-CuIK16JI.js";
5
5
  import { Fragment as s, computed as c, createBlock as l, createCommentVNode as u, createElementBlock as d, createElementVNode as f, createTextVNode as p, createVNode as m, defineComponent as h, mergeModels as g, nextTick as _, normalizeClass as v, normalizeStyle as y, onMounted as b, onUnmounted as x, openBlock as S, ref as C, renderList as w, renderSlot as T, toDisplayString as E, unref as D, useModel as O, watch as k, withCtx as A } from "vue";
6
6
  //#region src/components/custom/tabs/composables/useBouncySlider.ts
7
7
  function j(e) {
@@ -208,7 +208,7 @@ var M = [
208
208
  }, () => [p(E(e.label), 1)], !0)], 10, N))], 64))), 128))
209
209
  ], 2));
210
210
  }
211
- }), [["__scopeId", "data-v-ba2dfb91"]]), F = /* @__PURE__ */ h({
211
+ }), [["__scopeId", "data-v-45c0ed13"]]), F = /* @__PURE__ */ h({
212
212
  __name: "BouncyTabs",
213
213
  props: /* @__PURE__ */ g({
214
214
  options: {},
@@ -1,4 +1,4 @@
1
- import { n as e, t } from "./constants-DsCdlK9I.js";
1
+ import { n as e, t } from "./constants-C8NMonuo.js";
2
2
  import { getCurrentScope as n, onScopeDispose as r, readonly as i, ref as a, shallowRef as o, toValue as s, watch as c } from "vue";
3
3
  import { SmoothProgress as l } from "@mkbabb/keyframes.js";
4
4
  //#region src/composables/motion/useAnimatedNumber.ts
@@ -0,0 +1,95 @@
1
+ import { getCurrentScope as e, onScopeDispose as t, toValue as n } from "vue";
2
+ //#region src/composables/glass/canvas2d/useCanvas2D.ts
3
+ var r = "200px";
4
+ function i(i) {
5
+ let { setup: a, autoStart: o = !0, contextAttrs: s, rootMargin: c = r } = i, l = i.respectReducedMotion ?? !0, u = () => n(i.canvas), d = /* @__PURE__ */ new Set(), f = () => d.size === 0, p = 0, m = null, h = null, g = !1, _ = !1, v = typeof window < "u", y = typeof document < "u", b = l && v && window.matchMedia ? window.matchMedia("(prefers-reduced-motion: reduce)") : null, x = b?.matches ?? !1;
6
+ function S() {
7
+ if (!f() || !h || !m) {
8
+ p = 0;
9
+ return;
10
+ }
11
+ h.render(m, v ? performance.now() : 0), p = x ? 0 : requestAnimationFrame(S);
12
+ }
13
+ function C() {
14
+ g && f() && !p && h && m && (p = requestAnimationFrame(S));
15
+ }
16
+ function w() {
17
+ g && h && m && h.render(m, v ? performance.now() : 0);
18
+ }
19
+ function T() {
20
+ let e = b?.matches ?? !1;
21
+ e !== x && (x = e, C());
22
+ }
23
+ b?.addEventListener("change", T);
24
+ function E(e = "manual") {
25
+ let t = f();
26
+ d.add(e), t && !f() && (cancelAnimationFrame(p), p = 0);
27
+ }
28
+ function D(e = "manual") {
29
+ let t = !f();
30
+ d.delete(e), t && f() && g && h && (x ? w() : C());
31
+ }
32
+ function O() {
33
+ document.hidden ? E("tab-hidden") : D("tab-hidden");
34
+ }
35
+ y && document.addEventListener("visibilitychange", O), y && document.hidden && d.add("tab-hidden");
36
+ let k = null;
37
+ function A(e) {
38
+ e.skipped ? E("off-screen") : D("off-screen");
39
+ }
40
+ function j(e) {
41
+ if (k) return;
42
+ let t = e.parentElement;
43
+ t && (k = t, t.addEventListener("contentvisibilityautostatechange", A));
44
+ }
45
+ function M() {
46
+ k?.removeEventListener("contentvisibilityautostatechange", A), k = null;
47
+ }
48
+ let N = null;
49
+ function P(e) {
50
+ N || typeof IntersectionObserver > "u" || (N = new IntersectionObserver((e) => {
51
+ for (let t of e) t.isIntersecting || t.intersectionRatio > 0 ? D("off-screen") : E("off-screen");
52
+ }, { rootMargin: c }), N.observe(e));
53
+ }
54
+ let F = null, I = !1;
55
+ function L(e) {
56
+ if (!m) return;
57
+ let t = e.clientWidth || e.offsetWidth || 0, n = e.clientHeight || e.offsetHeight || 0;
58
+ if (!t || !n) return;
59
+ let r = Math.min(v && window.devicePixelRatio || 1, 2);
60
+ e.width = Math.round(t * r), e.height = Math.round(n * r), m.setTransform(r, 0, 0, r, 0, 0), !I && !p && w();
61
+ }
62
+ function R(e) {
63
+ F || typeof ResizeObserver > "u" || (F = new ResizeObserver(() => {
64
+ let e = u();
65
+ e && L(e);
66
+ }), F.observe(e));
67
+ }
68
+ function z() {
69
+ if (g || _) return;
70
+ let e = u();
71
+ if (!e) return;
72
+ let t = e.getContext("2d", s);
73
+ if (!t) throw Error("[useCanvas2D] 2D context unavailable");
74
+ m = t, h = a(m), g = !0, R(e), j(e), P(e), I = !0, L(e), I = !1, x ? w() : f() && (p = requestAnimationFrame(S));
75
+ }
76
+ function B() {
77
+ _ || (_ = !0, cancelAnimationFrame(p), p = 0, y && document.removeEventListener("visibilitychange", O), b?.removeEventListener("change", T), M(), N?.disconnect(), N = null, F?.disconnect(), F = null, h?.teardown?.(), h = null, m = null, d.clear());
78
+ }
79
+ return o && z(), e() && t(B), {
80
+ arm: z,
81
+ suspend: E,
82
+ resume: D,
83
+ wake: C,
84
+ dispose: B,
85
+ isRunning: f,
86
+ get ctx() {
87
+ return m;
88
+ },
89
+ get reducedMotion() {
90
+ return x;
91
+ }
92
+ };
93
+ }
94
+ //#endregion
95
+ export { i as t };
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "./_plugin-vue_export-helper-C1je1s0u.js";
2
- import { n as t, t as n } from "./prng-Bz_1Tydc.js";
2
+ import { n as t, t as n } from "./prng-CjVgzjQT.js";
3
3
  import { computed as r, createBlock as i, createElementVNode as a, defineComponent as o, normalizeClass as s, normalizeStyle as c, onUnmounted as l, openBlock as u, ref as d, renderSlot as f, resolveDynamicComponent as p, toRef as m, useId as h, watch as g, withCtx as _ } from "vue";
4
4
  //#region src/components/custom/watercolor-dot/prng.ts
5
5
  function v(e, t, n) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mkbabb/glass-ui",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "Glassmorphic design system — Vue 3.5 components, reka-ui primitives, Tailwind CSS v4 tokens",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -118,6 +118,9 @@
118
118
  "header-ribbon": [
119
119
  "dist/header-ribbon.d.ts"
120
120
  ],
121
+ "constellation": [
122
+ "dist/constellation.d.ts"
123
+ ],
121
124
  "scrolling-text": [
122
125
  "dist/scrolling-text.d.ts"
123
126
  ],
@@ -306,6 +309,10 @@
306
309
  "types": "./dist/header-ribbon.d.ts",
307
310
  "import": "./dist/header-ribbon.js"
308
311
  },
312
+ "./constellation": {
313
+ "types": "./dist/constellation.d.ts",
314
+ "import": "./dist/constellation.js"
315
+ },
309
316
  "./metric-badge": {
310
317
  "types": "./dist/metric-badge.d.ts",
311
318
  "import": "./dist/metric-badge.js"
@@ -551,6 +558,7 @@
551
558
  "proof:vt-names": "node scripts/proof-vt-names.mjs",
552
559
  "proof:dock-animation-live": "node scripts/proof-dock-animation-live.mjs",
553
560
  "proof:dock-clip-reveal": "node scripts/proof-dock-clip-reveal.mjs",
561
+ "proof:dock-layering-polish": "node scripts/proof-dock-layering-polish.mjs",
554
562
  "proof:dock-motion-parity": "node scripts/proof-dock-motion-parity.mjs",
555
563
  "proof:dock-motion-single-source": "node scripts/proof-dock-motion-single-source.mjs",
556
564
  "proof:dock-opacity-lockstep": "node scripts/proof-dock-opacity-lockstep.mjs",
@@ -585,6 +593,8 @@
585
593
  "proof:no-value-default": "node scripts/proof-no-value-default.mjs",
586
594
  "proof:blob-space-gamma": "node scripts/proof-blob-space-gamma.mjs",
587
595
  "proof:aurora-space-gamma": "node scripts/proof-aurora-space-gamma.mjs",
596
+ "proof:aurora-oklch-interp": "node scripts/proof-aurora-oklch-interp.mjs",
597
+ "proof:aurora-derive-gamut": "vitest run tests/components/custom/aurora/derive-color.test.ts",
588
598
  "proof:shader-shared-source": "node scripts/proof-shader-shared-source.mjs",
589
599
  "proof:blob-color-equivalence": "vitest run tests/components/custom/goo-blob/blob-color-equivalence.test.ts",
590
600
  "proof:fail-explicit": "node scripts/proof-fail-explicit.mjs",
@@ -597,9 +607,19 @@
597
607
  "proof:no-test-in-src": "node scripts/proof-no-test-in-src.mjs",
598
608
  "proof:spring-tokens-synced": "node scripts/proof-spring-tokens-synced.mjs",
599
609
  "proof:offscreen-pause": "node scripts/proof-offscreen-pause.mjs",
610
+ "proof:constellation-substrate-single": "node scripts/proof-constellation-substrate-single.mjs",
611
+ "proof:canvas2d-substrate": "vitest run tests/composables/glass/canvas2d/useCanvas2D.test.ts",
612
+ "proof:constellation-field": "vitest run tests/components/custom/constellation/constellationField.test.ts",
613
+ "proof:input-invalid-aria": "node scripts/proof-input-invalid-aria.mjs",
614
+ "proof:styling-hygiene": "node scripts/proof-styling-hygiene.mjs",
600
615
  "proof:subpath-enumeration": "node scripts/proof-subpath-enumeration.mjs",
601
616
  "proof:no-orphan-composable": "node scripts/proof-no-orphan-composable.mjs",
602
617
  "proof:speedtest-boundary": "node scripts/proof-speedtest-boundary.mjs",
618
+ "proof:glass-material-unified": "node scripts/proof-glass-material-unified.mjs",
619
+ "proof:glass-material-sota": "node scripts/proof-glass-material-sota.mjs",
620
+ "proof:glass-card-tiers": "node scripts/proof-glass-card-tiers.mjs",
621
+ "proof:primitive-affordance": "node scripts/proof-primitive-affordance.mjs",
622
+ "proof:reka-binding-idiom": "node scripts/proof-reka-binding-idiom.mjs",
603
623
  "proof:all": "node scripts/gates.mjs --run local",
604
624
  "gates:verify-ci": "node scripts/gates.mjs --verify-ci",
605
625
  "profile:bundle": "node scripts/profile-bundle.mjs",
@@ -60,6 +60,19 @@
60
60
  --dock-wrap-max-width: calc(100vw - var(--dock-viewport-inline-gutter, 1rem));
61
61
  --dock-separator-height: calc(var(--dock-h, var(--size-icon-btn)) * 0.5);
62
62
  --dock-collapsed-hover-scale: var(--scale-hover-dock);
63
+ /* AW.W3 — the spring-keyed child stagger knobs. `--dock-stagger-window-size`
64
+ is the fraction of the morph each child reveals over; `--dock-stagger-rise`
65
+ is the px the child rises from as it fades in. Consumers retune the
66
+ cascade feel per instance. */
67
+ --dock-stagger-window-size: 0.55;
68
+ --dock-stagger-rise: 4px;
69
+ /* AW.W3b — big-dock tokens. `--dock-card-radius` is the finite card-shell
70
+ radius (default --radius-dock-card = --radius-3xl, density-scaled
71
+ below); `--dock-tile-min` is the grid tile minimum (auto-fill track
72
+ floor). Both are density-scaled (compact tighter, spacious looser) — the
73
+ card radius + tile size ride the density cascade, never magic numbers. */
74
+ --dock-card-radius: var(--radius-dock-card);
75
+ --dock-tile-min: 4.5rem;
63
76
  /* Individual-transform identity base (AQ.W3 §W3.2). `scale: 1` at rest
64
77
  mints the stacking context once so a portaled popover over `--z-dock`
65
78
  stays above the dock during a hover scale (the load-bearing
@@ -300,12 +313,28 @@
300
313
  only. Width is driven by the inner `.dock-layers` FLIP via
301
314
  `useLayerTransition`; pinning it here would race the FLIP and
302
315
  reintroduce the `width: auto` non-interpolation diagnosed in
303
- R1+R6. */
316
+ R1+R6.
317
+
318
+ AW.W3 — the collapsed-hover `scale` (the `.collapsed:hover` rule below) +
319
+ the press squish (`--scale-press-dock` on the dock controls) ride the
320
+ INDIVIDUAL `scale` property, NOT `transform`. Adding `scale
321
+ var(--dock-motion-resize)` here unifies the hover scale onto the SAME dock
322
+ spring vocabulary as the morph, so a collapsed→hover→expand gesture reads as
323
+ ONE continuous spring rather than a `scale`-ease handed off to the
324
+ width-spring. Under PRM the global gate strips `scale` from the
325
+ transition-property → the hover scale snaps (0 morph frames). */
304
326
  .glass-dock:not(.vertical) {
305
327
  transition:
306
328
  padding var(--dock-motion-resize),
307
329
  box-shadow var(--dock-motion-standard),
308
330
  transform var(--dock-motion-resize),
331
+ scale var(--dock-motion-resize),
332
+ /* AW.W3b — the pill↔card corner radius morphs on the SAME spring the
333
+ aperture rides (ONE free interpolation). Without this the
334
+ shape-card collapsed↔expanded radius snaps discretely. The list is
335
+ shared by ALL horizontal docks — the radius transition also glides
336
+ the wrap collapsed↔expanded radius swap (desirable). */
337
+ border-radius var(--dock-motion-resize),
309
338
  background var(--dock-motion-standard),
310
339
  border-color var(--dock-motion-standard);
311
340
  }
@@ -339,6 +368,7 @@
339
368
  padding var(--dock-motion-resize),
340
369
  box-shadow var(--dock-motion-standard),
341
370
  transform var(--dock-motion-resize),
371
+ scale var(--dock-motion-resize),
342
372
  background var(--dock-motion-standard),
343
373
  border-color var(--dock-motion-standard);
344
374
  }
@@ -357,6 +387,38 @@
357
387
  border-radius: var(--radius-xl);
358
388
  }
359
389
 
390
+ /* AW.W3b — horizontal-root shape rules. The `shape-*` class previously bound
391
+ ONLY under `.variant-rail`; on a horizontal `variant-dock` the prop silently
392
+ no-op'd (the root hardwired `--radius-dock` = `--radius-pill`). These rules
393
+ close that documented-but-dead prop — `shape="rounded"` / `shape="card"` now
394
+ paint on a horizontal dock. Scoped to `variant-dock:not(.vertical)` so the
395
+ rail + instrument-strip variants (which own their own radius) are untouched. */
396
+ .glass-dock.variant-dock:not(.vertical).shape-rounded {
397
+ border-radius: var(--radius-xl);
398
+ }
399
+
400
+ /* The big-dock CARD shell — a FINITE concentric radius above 2xl, below pill.
401
+ It does NOT collapse to a stadium. Collapsed it returns to a pill (the morph
402
+ target); the pill↔card swap glides on the `--dock-motion-resize` spring
403
+ (`border-radius` is in the `:not(.vertical)` transition list). */
404
+ .glass-dock.variant-dock:not(.vertical).shape-card {
405
+ border-radius: var(--dock-card-radius);
406
+ }
407
+
408
+ .glass-dock.variant-dock:not(.vertical).shape-card.collapsed {
409
+ border-radius: var(--radius-pill);
410
+ }
411
+
412
+ /* The squircle reads markedly better at the large card radius — an
413
+ `@supports`-gated ENHANCEMENT only (Chrome 139+, no FF/Safari 2026). The
414
+ border-radius arc is the CONTRACT; the squircle is the better tier (the
415
+ baseline arc is the acceptable contract, NOT a degraded fallback). */
416
+ @supports (corner-shape: squircle) {
417
+ .glass-dock.variant-dock:not(.vertical).shape-card {
418
+ corner-shape: squircle;
419
+ }
420
+ }
421
+
360
422
  /* AJ-W1-δ — `instrument-strip` variant (per G-AJ-D7 Path 1).
361
423
  *
362
424
  * The speedtest survey cockpit's vertical rail needs chassis-strip
@@ -438,6 +500,13 @@
438
500
  justify-content: center;
439
501
  }
440
502
 
503
+ /* AW.W3 — the collapsed-hover scale rides the unified dock spring vocabulary.
504
+ The `scale var(--dock-motion-resize)` arm was added to the `.glass-dock`
505
+ root transition above, so this hover scale settles on the SAME spring curve
506
+ the expand morph uses — a collapsed→hover→expand gesture is one continuous
507
+ spring, not a `scale`-ease handed off to the width-spring. The hover scale
508
+ and the press squish (`--scale-press-dock`) are now one register on the dock
509
+ surface. */
441
510
  .glass-dock.collapsed:hover {
442
511
  background: var(--glass-bg-wash);
443
512
  border-color: var(--glass-border-floating);
@@ -659,6 +728,72 @@
659
728
  inset: 0;
660
729
  }
661
730
 
731
+ /* AW.W3 — the spring-keyed child stagger. While the size spring drives the
732
+ morph, the driver arms the container (`.dock-layers` / `.dock-layer-stack`)
733
+ with `data-revealing` and writes the morph's normalized progress to
734
+ `--dock-morph-progress` (0 → 1) every frame. Each direct child of the
735
+ ACTIVE pane reveals when that progress crosses a per-child onset, so the
736
+ controls cascade IN ON the physical morph — a fast flick and a slow
737
+ hover-open both choreograph correctly, and an interrupted morph carries the
738
+ cascade with it (no orphaned timers). This is a per-CHILD opacity onset; the
739
+ active PANE itself stays statically opacity:1 (revealed by the aperture —
740
+ the W2 clip-reveal contract is NOT regressed).
741
+
742
+ The reveal is a pure `calc` ramp: `opacity = clamp(0, (progress − onset) /
743
+ window, 1)`. `--dock-stagger-onset` steps per child (nth-child); the window
744
+ is `--dock-stagger-window`. The children also rise a few px on a `translate`
745
+ that resolves on the same ramp. ONLY children inside the `[data-revealing]`
746
+ active pane are armed — at rest the children carry no opacity authority (the
747
+ default cascade leaves them at 1).
748
+
749
+ PRM: the driver never arms `data-revealing` on the synchronous PRM swap (the
750
+ FLIP fast-path returns before the spring; the spring's `respectReducedMotion`
751
+ settles instantly), so the children paint at full opacity at once. The
752
+ global PRM gate also strips `translate` from the transition-property — the
753
+ rise snaps. The cascade collapses to 0 stagger frames under PRM. */
754
+ .dock-layers[data-revealing] .dock-layer--full > *,
755
+ .dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > * {
756
+ --dock-stagger-window: var(--dock-stagger-window-size, 0.55);
757
+ --dock-stagger-onset: 0;
758
+ opacity: clamp(
759
+ 0,
760
+ calc(
761
+ (var(--dock-morph-progress, 1) - var(--dock-stagger-onset)) /
762
+ var(--dock-stagger-window)
763
+ ),
764
+ 1
765
+ );
766
+ translate: 0
767
+ calc(
768
+ (1 - clamp(0, calc((var(--dock-morph-progress, 1) - var(--dock-stagger-onset)) / var(--dock-stagger-window)), 1)) *
769
+ var(--dock-stagger-rise, 4px)
770
+ );
771
+ }
772
+
773
+ /* Per-child onsets — each successive control starts a beat later, so the row
774
+ fills outer→in on expand. The onsets are fractions of the morph progress;
775
+ capped so even a long row finishes within the morph window. */
776
+ .dock-layers[data-revealing] .dock-layer--full > *:nth-child(2),
777
+ .dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(2) {
778
+ --dock-stagger-onset: 0.08;
779
+ }
780
+ .dock-layers[data-revealing] .dock-layer--full > *:nth-child(3),
781
+ .dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(3) {
782
+ --dock-stagger-onset: 0.16;
783
+ }
784
+ .dock-layers[data-revealing] .dock-layer--full > *:nth-child(4),
785
+ .dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(4) {
786
+ --dock-stagger-onset: 0.24;
787
+ }
788
+ .dock-layers[data-revealing] .dock-layer--full > *:nth-child(5),
789
+ .dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(5) {
790
+ --dock-stagger-onset: 0.32;
791
+ }
792
+ .dock-layers[data-revealing] .dock-layer--full > *:nth-child(n + 6),
793
+ .dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(n + 6) {
794
+ --dock-stagger-onset: 0.4;
795
+ }
796
+
662
797
  /* AW.W2 — the at-rest clip-lift moved to `.expanded:not([data-morphing])` /
663
798
  `.always-expanded:not([data-morphing])` (the clip-shell block at the top).
664
799
  A plain `overflow: visible` here would lift the clip DURING the morph (the
@@ -676,6 +811,46 @@
676
811
  cursor: default;
677
812
  }
678
813
 
814
+ /* ── AW.W3b — `layout="grid"` the 2D auto-grid big dock ──────────────
815
+ The active layer becomes a self-wrapping tile grid (Launchpad / Stage-Manager
816
+ track symmetry — `auto-fill` columns floored at `--dock-tile-min`, explicit
817
+ `grid-auto-rows` so the concentric look holds). DISTINCT from
818
+ `overflow="wrap"`'s content-ordered flex-wrap. A `layout="grid"` dock is
819
+ `alwaysExpanded` by contract (GlassDock.vue), so there is NO width morph and
820
+ hence NO per-frame grid-column reflow. `white-space: normal` releases the
821
+ single-row nowrap so the tiles wrap. */
822
+ .glass-dock.layout-grid {
823
+ white-space: normal;
824
+ }
825
+
826
+ .glass-dock.layout-grid .dock-layer--full {
827
+ display: grid;
828
+ grid-template-columns: repeat(
829
+ auto-fill,
830
+ minmax(var(--dock-tile-min, 4.5rem), 1fr)
831
+ );
832
+ grid-auto-rows: var(--dock-tile-min, 4.5rem);
833
+ gap: var(--dock-layer-gap, 0.375rem);
834
+ width: 100%;
835
+ height: auto;
836
+ white-space: normal;
837
+ }
838
+
839
+ /* A grid dock fills the inline cap; the active layer is the panel body, not a
840
+ shrink-to-fit row. The cap is the `--dock-max-inline-size` clamp (KEPT). */
841
+ .glass-dock.layout-grid .dock-layers {
842
+ width: 100%;
843
+ }
844
+
845
+ /* CONCENTRIC inner tiles — inner radius = outer card radius − padding (the
846
+ universal macOS / visionOS / Raycast rule). NEVER nest two pills
847
+ (stadium-in-stadium); a finite outer + a stepped-down inner. */
848
+ .glass-dock.layout-grid.shape-card .dock-layer--full > * {
849
+ border-radius: calc(
850
+ var(--dock-card-radius) - var(--dock-padding-inline, 0.5rem)
851
+ );
852
+ }
853
+
679
854
  /* ── Scroll-on-overflow opt-in (GlassDock `overflow="scroll"`) ───────
680
855
  The default contract grows-to-fit then overflows visibly past the
681
856
  axis cap. `overflow="scroll"` instead makes the dock the scroll port
@@ -748,6 +923,15 @@
748
923
  min-height: var(--dock-wrap-layer-min-height, var(--dock-layer-height, 2rem));
749
924
  gap: var(--dock-wrap-row-gap, 0.25rem)
750
925
  var(--dock-wrap-column-gap, var(--dock-layer-gap, 0.375rem));
926
+ /* AW.W3b — the graceful wrap-morph. The wrap/unwrap row-reflow flips
927
+ `flex-wrap` at the `--dock-overflow-bp` `@media` boundary; without a
928
+ transition the rows JUMP-CUT. Sequencing the layer's `min-height`
929
+ through the `--dock-motion-resize` spring MORPHS the reflow. The global
930
+ PRM gate strips `min-height` from the transition-property under reduce →
931
+ the morph snaps (0 morph frames) while the reflow still completes. The
932
+ wrap radius swap (`.collapsed` / `@media`) glides too — `border-radius`
933
+ is in the shared `:not(.vertical)` transition list (AW.W3b scope 2). */
934
+ transition: min-height var(--dock-motion-resize);
751
935
  }
752
936
 
753
937
  .glass-dock.dock-overflow-wrap .dock-layer--summary {
@@ -0,0 +1,60 @@
1
+ /* AW.W23 — the #glass-refract convex-lens SVG filter, shipped as a library
2
+ * asset so `.glass-refract` works OUT OF THE BOX (no consumer hand-mounts a
3
+ * filter node).
4
+ *
5
+ * The refraction recipe is the canonical 3-primitive graph (kube.io /
6
+ * LogRocket, 2025):
7
+ *
8
+ * feImage — a pre-baked displacement map computed from the convex-
9
+ * squircle surface profile y = ⁴√(1 − (1 − x)⁴) (Apple's
10
+ * preferred corner curve) via Snell's law (n₁ = 1 air,
11
+ * n₂ = 1.5 glass). The map is authored as an embedded SVG
12
+ * whose R channel carries the x-shift and G channel the
13
+ * y-shift (neutral 128 = no shift; bright edges bend light
14
+ * inward like a lens), expressed as two crossed linear
15
+ * gradients over the squircle silhouette.
16
+ * feDisplacementMap— displaces the backdrop by the baked map (R = x-shift,
17
+ * G = y-shift, `scale` = max px, neutral 128).
18
+ * feImage specular — a soft top-left specular highlight…
19
+ * feBlend screen — …blended `screen` over the displaced backdrop for the
20
+ * polished-glass catch.
21
+ *
22
+ * The filter ships INLINE as a data-URI in the `backdrop-filter` declaration, so
23
+ * the consumer gets it with zero markup. `backdrop-filter: url(#…)` is
24
+ * Chromium-only (WebKit bug 245510 open, Firefox not shipping), so the whole
25
+ * decl sits behind `@supports (backdrop-filter: url(#…))`; a non-supporting
26
+ * engine paints the un-gated blur base alone (no broken `url()` reference).
27
+ *
28
+ * RE-HOME (AW.W23 — decouple from the W22-folded compound). At HEAD the
29
+ * refraction rule was bound to `.glass-specular-track.glass-refract`. W22 folds
30
+ * `glass-specular-track` into `.glass-material`, retiring the compound's left
31
+ * operand, so the rule is re-homed onto `.glass-material.glass-refract` — the
32
+ * refraction garnish opts in on ANY `.glass-material` surface, no dependency on
33
+ * the now-aliased `glass-specular-track` class. The garnish stays PE-only over
34
+ * the blur base; it is NEVER promoted to the card/primitive substrate
35
+ * (feDisplacementMap is resize-expensive — only the `scale` attr animates
36
+ * without rebuilding the map).
37
+ */
38
+
39
+ @layer components {
40
+ /* The shipped filter graph as a data-URI SVG. A `0×0` host SVG mounted via
41
+ the CSS asset keeps the `#glass-refract` id resolvable in-document. */
42
+ .glass-refract {
43
+ /* The named convex-lens displacement filter, baked from the squircle
44
+ surface profile. Composed as a custom property so the @supports arm
45
+ below references one source. */
46
+ --glass-refract-filter: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='glass-refract' x='0' y='0' width='100%25' height='100%25'%3E%3CfeImage href='data:image/svg+xml,%253Csvg xmlns=%2522http://www.w3.org/2000/svg%2522 width=%2522100%2522 height=%2522100%2522%253E%253CradialGradient id=%2522m%2522 cx=%252250%2525%2522 cy=%252250%2525%2522 r=%252250%2525%2522%253E%253Cstop offset=%25220%2525%2522 stop-color=%2522rgb(128,128,128)%2522/%253E%253Cstop offset=%252270%2525%2522 stop-color=%2522rgb(128,128,128)%2522/%253E%253Cstop offset=%2522100%2525%2522 stop-color=%2522rgb(255,255,128)%2522/%253E%253C/radialGradient%253E%253Crect width=%2522100%2522 height=%2522100%2522 fill=%2522url(%2523m)%2522/%253E%253C/svg%253E' result='map'/%3E%3CfeDisplacementMap in='SourceGraphic' in2='map' scale='28' xChannelSelector='R' yChannelSelector='G' result='disp'/%3E%3CfeImage href='data:image/svg+xml,%253Csvg xmlns=%2522http://www.w3.org/2000/svg%2522 width=%2522100%2522 height=%2522100%2522%253E%253CradialGradient id=%2522s%2522 cx=%252230%2525%2522 cy=%252225%2525%2522 r=%252260%2525%2522%253E%253Cstop offset=%25220%2525%2522 stop-color=%2522rgba(255,255,255,0.5)%2522/%253E%253Cstop offset=%2522100%2525%2522 stop-color=%2522rgba(255,255,255,0)%2522/%253E%253C/radialGradient%253E%253Crect width=%2522100%2522 height=%2522100%2522 fill=%2522url(%2523s)%2522/%253E%253C/svg%253E' result='spec'/%3E%3CfeBlend in='disp' in2='spec' mode='screen'/%3E%3C/filter%3E%3C/svg%3E#glass-refract");
47
+ }
48
+
49
+ /* The gated consuming selector — RE-HOMED onto `.glass-material.glass-refract`
50
+ (decoupled from the W22-folded `.glass-specular-track` compound). The
51
+ refraction composes the shipped `#glass-refract` filter OVER the host's
52
+ existing blur; it is additive (the blur already painted, this sharpens the
53
+ refraction edge where the engine supports it). */
54
+ @supports (backdrop-filter: url("#glass-refract")) {
55
+ .glass-material.glass-refract,
56
+ .glass-refract {
57
+ backdrop-filter: var(--glass-blur-resting) var(--glass-refract-filter);
58
+ }
59
+ }
60
+ }