@mkbabb/glass-ui 3.3.0 → 3.5.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 (60) hide show
  1. package/dist/{DataTable-FfkaAg2z.js → DataTable-DyKc_26a.js} +144 -106
  2. package/dist/{NumberFieldContent-iOTQ5rGO.js → NumberFieldContent-jO-zGOHf.js} +1 -1
  3. package/dist/api/index.d.ts +1 -0
  4. package/dist/{button-Ckn3eDfB.js → button-i8Ek5vvg.js} +1 -1
  5. package/dist/button.js +1 -1
  6. package/dist/carousel.js +1 -1
  7. package/dist/components/custom/deck-progress/DeckProgress.vue.d.ts +27 -0
  8. package/dist/components/custom/deck-progress/index.d.ts +2 -0
  9. package/dist/components/custom/dock/composables/useLayerTransition.d.ts +11 -0
  10. package/dist/components/ui/data-table/composables/useDataTableResponsive.d.ts +28 -0
  11. package/dist/components/ui/data-table/composables/useDataTableRowIdentity.d.ts +33 -0
  12. package/dist/composables/dark/useGlobalDark.d.ts +17 -6
  13. package/dist/composables/dom/index.d.ts +3 -0
  14. package/dist/composables/dom/useBreakpoint.d.ts +38 -0
  15. package/dist/composables/dom/useIdleReady.d.ts +63 -0
  16. package/dist/composables/dom/useTokenColor.d.ts +13 -0
  17. package/dist/composables/dom/useViewportReady.d.ts +87 -0
  18. package/dist/composables/glass/index.d.ts +1 -1
  19. package/dist/composables/glass/useGlassRenderer.d.ts +10 -3
  20. package/dist/composables/motion/useAnimatedNumber.d.ts +8 -2
  21. package/dist/composables/motion/useCountup.d.ts +8 -2
  22. package/dist/composables/motion/useNumericTransition.d.ts +31 -9
  23. package/dist/composables/motion/useViewTransition.d.ts +16 -1
  24. package/dist/confirm-dialog.js +1 -1
  25. package/dist/data-table.js +1 -1
  26. package/dist/dock.js +169 -158
  27. package/dist/dom.js +4 -4
  28. package/dist/glass-panel.js +14 -14
  29. package/dist/glass-ui.css +1 -1
  30. package/dist/glass-ui.js +134 -119
  31. package/dist/goo-blob.js +108 -89
  32. package/dist/index.d.ts +1 -0
  33. package/dist/labeled-field.js +1 -1
  34. package/dist/motion-core.js +1 -1
  35. package/dist/number-field.js +1 -1
  36. package/dist/search.js +1 -1
  37. package/dist/{slider-BOh8ycfZ.js → slider-CgNzRSh6.js} +1 -1
  38. package/dist/slider.js +1 -1
  39. package/dist/styles/components.css +1 -1
  40. package/dist/styles/dock.css +112 -48
  41. package/dist/styles/glass.css +44 -1
  42. package/dist/styles/instrument-chassis.css +10 -13
  43. package/dist/styles/instrument-rail.css +11 -12
  44. package/dist/styles/tokens.css +9 -7
  45. package/dist/styles/utilities.css +20 -0
  46. package/dist/styles/view-transition.css +24 -0
  47. package/dist/tabs.js +1 -1
  48. package/dist/timeline.js +1 -1
  49. package/dist/useViewTransition-CJnqg6wl.js +21 -0
  50. package/dist/useViewportReady-Dcvf0c4p.js +191 -0
  51. package/package.json +5 -3
  52. package/src/styles/dock.css +112 -48
  53. package/src/styles/glass.css +44 -1
  54. package/src/styles/instrument-chassis.css +10 -13
  55. package/src/styles/instrument-rail.css +11 -12
  56. package/src/styles/tokens.css +9 -7
  57. package/src/styles/utilities.css +20 -0
  58. package/src/styles/view-transition.css +24 -0
  59. package/dist/useClipboard-D8vFyRCa.js +0 -97
  60. package/dist/useViewTransition-D4ssvnXZ.js +0 -18
@@ -68,15 +68,20 @@
68
68
  position: relative;
69
69
  display: inline-flex;
70
70
  align-items: center;
71
- /* AV.W16 TW3 — the dock establishes an inline-size container named
72
- `dock` so its descendants (the label glyph, the density rules)
73
- read the dock's OWN box, not the viewport. This makes the dock
74
- portable into a 320px sidebar: the label compresses off the
75
- dock's box width, not `@media`. A consumer that sets the
76
- `containerName` prop overrides the name via inline style (the
77
- explicit container-query opt-in); the default `dock` name backs
78
- the built-in label/density container reads below. */
79
- container: dock / inline-size;
71
+ /* AW.W1 — the dock does NOT establish a default inline-size container.
72
+ `container-type: inline-size` applies `contain: inline-size`, which
73
+ makes an element's inline size INDEPENDENT of its contents — fatally
74
+ incompatible with a shrink-to-fit pill: under it `.glass-dock` cannot
75
+ size to its content and collapses to its padding floor (~19px) with the
76
+ active layer overflowing (the 3.3.0 regression — every horizontal dock,
77
+ collapsed AND always-expanded, rendered as a sliver). The `dock`-named
78
+ container is now OPT-IN via the `containerName` prop: a consumer
79
+ embedding the dock in a width-CONSTRAINED host (a 320px sidebar) sets
80
+ `containerName="dock"` to get the descendant container-query reads (the
81
+ audacious-label compression below), trading shrink-to-fit for the
82
+ host-constrained width it already has there. The default free-floating
83
+ dock shrink-wraps to its content (AV.W16 TW3 made this a default and
84
+ silently broke dock sizing for the common case). */
80
85
  border-radius: var(--radius-dock);
81
86
  white-space: nowrap;
82
87
  max-inline-size: var(--dock-max-inline-size);
@@ -90,14 +95,56 @@
90
95
  box-shadow: var(--glass-edge-light), var(--shadow-dock-override, var(--shadow-dock));
91
96
  }
92
97
 
93
- /* `overflow: hidden` is the clip shell for every dock. AT.W7-dock-a
94
- folded out the prior `containerName` clip-lift (the container-query
95
- opt-in is orthogonal — it no longer silently changes the clip), so the
96
- clip applies universally and the `.expanded` / `.always-expanded` /
97
- `.dock-overflow-wrap` states lift it back to `visible` where they need to
98
- grow. A consumer wanting multi-line content opts into `overflow="wrap"`. */
99
- .glass-dock {
100
- overflow: hidden;
98
+ /* AW.W2 — the box IS the reveal aperture (clip-reveal one-clock morph).
99
+ Migrated from `overflow: hidden` to a SINGLE-AXIS `overflow: clip` on the
100
+ MORPH axis (horizontal clips X / visible Y; vertical clips Y / visible X),
101
+ gated on a `data-morphing` attribute the driver owns (set at gesture START,
102
+ cleared on SETTLE). The clip PERSISTS through the morph — content is laid
103
+ out ONCE at its natural size behind the aperture and the growing/shrinking
104
+ box uncovers it, never spills it — and lifts to `visible` only at REST
105
+ expanded (so grown content + the focus ring paint past the box). The
106
+ prior `.expanded { overflow: visible }` (bound synchronously to
107
+ `visualExpanded`) lifted the clip at frame 0, leaving no aperture to reveal
108
+ through; the `:not([data-morphing])` guard holds the clip for the whole
109
+ gesture and lifts it on the final paint.
110
+
111
+ `overflow: clip` is PAINT-ONLY (not a scroll container): it never
112
+ participates in `scrollIntoView`/scroll-anchoring, cannot be a
113
+ scroll-driven-animation `scroller`, and drops the `min-width:0` scroll-box
114
+ dance for the non-scroll shell. The cross axis is ALWAYS explicitly
115
+ `visible` (the MDN single-axis-clip-degrades-to-hidden caveat). The
116
+ legitimate scroll ports (`.dock-scroll-{x,y}`, `overflow: auto`) STAY
117
+ `auto` — they are NOT migrated. AT.W7-dock-a's orthogonality holds: the
118
+ `containerName` opt-in does not touch the clip. */
119
+ .glass-dock:not(.vertical) {
120
+ overflow-x: clip;
121
+ overflow-y: visible;
122
+ }
123
+
124
+ .glass-dock.vertical {
125
+ overflow-x: visible;
126
+ overflow-y: clip;
127
+ }
128
+
129
+ /* DURING a morph the clip is the aperture — keyed off `data-morphing`, NOT
130
+ off `.expanded`, so it survives the whole gesture and the box reveals its
131
+ content rather than letting it spill. */
132
+ .glass-dock[data-morphing]:not(.vertical) {
133
+ overflow-x: clip;
134
+ overflow-y: visible;
135
+ }
136
+
137
+ .glass-dock[data-morphing].vertical {
138
+ overflow-x: visible;
139
+ overflow-y: clip;
140
+ }
141
+
142
+ /* AT REST EXPANDED — lift the morph-axis clip (grown content + focus ring
143
+ paint past the box). Fires ONLY when NOT morphing; the [data-morphing]
144
+ rules above win during the gesture by being more specific + later. */
145
+ .glass-dock.expanded:not([data-morphing]),
146
+ .glass-dock.always-expanded:not([data-morphing]) {
147
+ overflow: visible;
101
148
  }
102
149
 
103
150
  .glass-dock[data-density="compact"] {
@@ -210,9 +257,13 @@
210
257
  /* Density-audacious narrow-box carve (AV.W16 TW3) — when the dock's OWN
211
258
  box is narrow the audacious-dock label glyphs compress so the chrome
212
259
  doesn't overflow its host (≤14px below 30rem, 15px 30–45rem). Keyed off
213
- the dock's `dock` inline-size container (established on `.glass-dock`
214
- above) rather than the viewport — so a dock dropped into a 320px
215
- sidebar compresses its label off ITS box, not the page width. The var
260
+ the dock's `dock` inline-size container — now OPT-IN via the
261
+ `containerName="dock"` prop (AW.W1 removed the always-on default that
262
+ broke shrink-to-fit), so it fires only for a dock the consumer has
263
+ width-CONSTRAINED into a host (the 320px sidebar case) where the
264
+ compression is meaningful and the containment does not collapse the box.
265
+ A free-floating dock shrink-wraps (box == content) so there is nothing to
266
+ compress. The var
216
267
  is set on the `.dock-label` descendant (which reads it via
217
268
  `font-size: var(--dock-label-size)`, typography.css). Thresholds keep
218
269
  the prior 479/719px viewport cadence as box widths (30rem ≈ 480px,
@@ -274,13 +325,17 @@
274
325
  width: auto;
275
326
  height: auto;
276
327
  max-block-size: var(--dock-max-block-size);
277
- overflow-x: visible;
278
- overflow-y: visible;
328
+ /* overflow on the block (morph) axis is the data-morphing-gated clip
329
+ shell above; the cross axis stays visible there. */
279
330
  padding: var(--dock-vertical-padding, var(--dock-padding-block, 0.375rem));
280
331
  border-radius: var(--radius-dock);
332
+ /* AW.W2 — axis-parity carve: NON-morph properties only. The block-axis
333
+ (height) morph is owned by the inner `.dock-layer-stack` / DockLayerGroup
334
+ spring (or the VT path); a `height var(--dock-motion-resize)` here would
335
+ be a second driver on the same axis (the AV.W9.0 dual-driver race),
336
+ and `width` is the cross axis the spring never writes. Matches how
337
+ `:not(.vertical)` already excludes `width`. */
281
338
  transition:
282
- height var(--dock-motion-resize),
283
- width var(--dock-motion-resize),
284
339
  padding var(--dock-motion-resize),
285
340
  box-shadow var(--dock-motion-standard),
286
341
  transform var(--dock-motion-resize),
@@ -540,13 +595,16 @@
540
595
  opacity: 1;
541
596
  visibility: visible;
542
597
  pointer-events: auto;
543
- /* The active layer shows immediately — no deferred `visibility`. AU.W2:
544
- the opacity rides `--dock-motion-resize` (lockstep with the container
545
- morph), but the `visibility 0s` stays immediate (the `:434` rule
546
- governs — the active layer must paint at once, no fade-in delay). */
547
- transition:
548
- opacity var(--dock-motion-resize),
549
- visibility 0s;
598
+ /* AW.W2 — the active pane is STATICALLY opacity:1, REVEALED by the clip
599
+ aperture (the box is the reveal mechanism), never faded. The prior
600
+ `opacity var(--dock-motion-resize)` arm is DELETED: there is ZERO
601
+ opacity authority on the active pane now — the aperture uncovers a
602
+ layout that is laid out ONCE at natural size behind it. Only `visibility
603
+ 0s` survives (the active layer must paint at once, no fade-in delay —
604
+ the AU.W2 governing rule). The leaving pane keeps the shared-token
605
+ opacity fade below (the ONLY surviving opacity animation, the thin
606
+ polish proof:dock-opacity-lockstep names). */
607
+ transition: visibility 0s;
550
608
  }
551
609
 
552
610
  /* A leaving layer is inactive for hit-testing but still painted
@@ -576,9 +634,19 @@
576
634
  .dock-layer {
577
635
  display: flex;
578
636
  align-items: center;
579
- white-space: nowrap;
580
637
  grid-area: 1 / 1;
581
638
  gap: var(--dock-layer-gap, 0.375rem);
639
+ }
640
+
641
+ /* AW.W2 — content-intrinsic axis follows the MORPH axis. A horizontal dock
642
+ lays its inline content out ONCE at `width: max-content` (nowrap) so the
643
+ width aperture uncovers a static row; the fixed `height` keeps the row a
644
+ single line. Scoped to `:not(.vertical)` so a vertical rail (whose
645
+ block-axis morph reveals a column) is NOT forced onto one nowrap row — the
646
+ vertical group's active host block-sizes to `height: max-content` below
647
+ (the AP.W3 column treatment, KEPT). */
648
+ .glass-dock:not(.vertical) .dock-layer {
649
+ white-space: nowrap;
582
650
  height: var(--dock-layer-height, 2.5rem);
583
651
  }
584
652
 
@@ -591,10 +659,11 @@
591
659
  inset: 0;
592
660
  }
593
661
 
594
- .glass-dock.expanded {
595
- overflow: visible;
596
- }
597
-
662
+ /* AW.W2 — the at-rest clip-lift moved to `.expanded:not([data-morphing])` /
663
+ `.always-expanded:not([data-morphing])` (the clip-shell block at the top).
664
+ A plain `overflow: visible` here would lift the clip DURING the morph (the
665
+ load-bearing sequencing gap), so the `overflow` declarations are dropped
666
+ from these state rules; they keep only their non-overflow concerns. */
598
667
  .glass-dock.expanded:not(.fit-content) .dock-layers {
599
668
  width: 100%;
600
669
  }
@@ -605,15 +674,6 @@
605
674
 
606
675
  .glass-dock.always-expanded {
607
676
  cursor: default;
608
- overflow: visible;
609
- }
610
-
611
- /* Z.W2.T2 — always-expanded vertical rails inherit the same
612
- grow-to-fit + visible contract. The prior auto pair re-introduced
613
- the scroll affordance that A2 §B6 rejected. */
614
- .glass-dock.vertical.always-expanded {
615
- overflow-x: visible;
616
- overflow-y: visible;
617
677
  }
618
678
 
619
679
  /* ── Scroll-on-overflow opt-in (GlassDock `overflow="scroll"`) ───────
@@ -845,9 +905,13 @@
845
905
  .dock-layer-stack {
846
906
  position: relative;
847
907
  display: grid;
848
- transition:
849
- width var(--dock-motion-resize),
850
- height var(--dock-motion-resize);
908
+ /* AW.W2 — the morph axis (width for a horizontal group, height for a
909
+ vertical one) is driven by the `useLayerTransition` SpringProgress in
910
+ pixel space (FLIP path) or the View-Transition (native path); a CSS
911
+ `width`/`height` transition here would be a SECOND driver on the same
912
+ axis (the AV.W9.0 dual-driver race that froze the dock). The cross axis
913
+ is intrinsic-sized and never morphs. So NO size transition on the stack
914
+ — one owner per axis. */
851
915
  }
852
916
 
853
917
  /* Base layout — the crossfade + hit-test contract is shared with
@@ -285,7 +285,13 @@
285
285
 
286
286
  height: 2.5rem;
287
287
  border-radius: var(--radius-pill);
288
- border: 1.5px solid var(--glass-border-wash);
288
+ /* AW.W13 — the resting field edge reads as an enclosed control at rest.
289
+ Was `--glass-border-wash` (~8%α), which nearly vanished on cream; the
290
+ lift to `--surface-tint-15` (15%α — ~2× the prior floor) makes the
291
+ edge legible without going loud. The `:focus-visible` accent ring +
292
+ the `:user-invalid`/`[aria-invalid]` destructive ring below still win
293
+ by order + specificity, so this only raises the resting alpha. */
294
+ border: 1.5px solid var(--surface-tint-15);
289
295
  background: var(--glass-bg-quiet);
290
296
  backdrop-filter: var(--glass-blur-wash);
291
297
  padding: 0 1rem;
@@ -361,6 +367,43 @@
361
367
  opacity: 0.5;
362
368
  cursor: not-allowed;
363
369
  }
370
+
371
+ /* AW.W13 — the Select trigger mirrors the input pill's resting border lift.
372
+ The reka-ui SelectTrigger renders `role="combobox"` over the `.glass-wash`
373
+ surface; its 8%α `--glass-border-wash` field edge vanished on cream just
374
+ like the input pill, so the same `--surface-tint-15` lift makes the
375
+ trigger read as an enclosed control at rest. Scoped to the combobox role
376
+ so the lift reaches ONLY the Select trigger, not every `.glass-wash`
377
+ surface. */
378
+ .glass-wash[role="combobox"] {
379
+ border-color: var(--surface-tint-15);
380
+ }
381
+
382
+ /* ── Deck-position rail (AW.W16) ──────────────────────────────────────
383
+ A CSS recipe that RESTYLES the shipped `<Progress variant="default">`
384
+ (the reka-ui ProgressRoot track + its indicator child) into the thin
385
+ bottom-of-deck position rail. It composes the EXISTING fill — it does NOT
386
+ re-author a second track/range mechanic. Every visual axis is a
387
+ `--progress-rail-*` token, so a consumer/dark-flip re-tints with zero
388
+ library edit (`:root { --progress-rail-fill: … }`). The viewport-pinned
389
+ chrome (`position: fixed`, `env(safe-area-inset-bottom)`, the z-index) is
390
+ CONSUMER-supplied — this recipe owns only the LOOK. */
391
+ .glass-progress-rail {
392
+ /* The track hairline — the rail reads as a thin sliver, not a 16px bar. */
393
+ height: var(--progress-rail-h, 3px);
394
+ border-radius: 0;
395
+ background: var(--progress-rail-track, color-mix(in srgb, var(--foreground) 8%, transparent));
396
+ }
397
+
398
+ /* The fill — the shipped ProgressIndicator child. A leading-edge glow on the
399
+ fill's trailing edge reads as a moving position head. */
400
+ .glass-progress-rail > * {
401
+ border-radius: 0;
402
+ background: var(--progress-rail-fill, var(--primary));
403
+ box-shadow:
404
+ var(--progress-rail-glow-x, 4px) 0 var(--progress-rail-glow-blur, 8px)
405
+ color-mix(in srgb, var(--progress-rail-fill, var(--primary)) var(--progress-rail-glow-strength, 55%), transparent);
406
+ }
364
407
  }
365
408
 
366
409
  /* ── Accessibility: reduced transparency ────────── */
@@ -231,6 +231,9 @@
231
231
  /* Chassis dividers — twin-line hairline grooves ─────────────── */
232
232
 
233
233
  .chassis-divider {
234
+ /* AW.W15 — consume the shared twin-line α pair (the catch-light +
235
+ under-shadow tokens publish here; the .bezel-line rules read them). */
236
+ @apply twin-line-divider;
234
237
  flex-shrink: 0;
235
238
  position: relative;
236
239
  background: transparent;
@@ -269,19 +272,13 @@
269
272
  height: 100%;
270
273
  }
271
274
 
272
- /* Q.W4.A — bezel-line α lift in light mode per audit-A4 §1 row 2-3 +
273
- §11 #2-#3 + §12 Action 2. The 0.04 / 0.06 light-mode pair was
274
- calibrated against a flat bg; under the saturated-aurora chassis
275
- composition the catch-light + under-shadow read as one slightly
276
- darker pixel, and the twin-line groove fails its job. Bumping to
277
- 0.10 / 0.12 holds the divider against the bleed without committing
278
- to the dark-mode pair's contrast (the dark mode already runs at
279
- 0.06 / 0.18 against a low-light bg). */
280
- .bezel-line.top { background: rgb(255 255 255 / 0.10); }
281
- .bezel-line.bottom { background: rgb(0 0 0 / 0.12); }
282
-
283
- .dark .bezel-line.top { background: rgb(255 255 255 / 0.06); }
284
- .dark .bezel-line.bottom { background: rgb(0 0 0 / 0.18); }
275
+ /* AW.W15 — the twin-line α pair now lives in the `twin-line-divider`
276
+ @utility (single source, dark-adaptive). The `.chassis-divider` host
277
+ `@apply`s it, publishing `--twin-line-catch` / `--twin-line-shadow`; these
278
+ rules read the tokens. The prior Q.W4.A light pair (0.10 / 0.12) and the
279
+ dark pair (0.06 / 0.18) are the values the utility resolves. */
280
+ .bezel-line.top { background: var(--twin-line-catch); }
281
+ .bezel-line.bottom { background: var(--twin-line-shadow); }
285
282
 
286
283
  /* Inner dial divider — same primitive, vertical on desktop. Speedtest
287
284
  slots its <ChassisDivider orientation="vertical" class="dial-divider" />
@@ -17,6 +17,9 @@
17
17
 
18
18
  @layer components {
19
19
  .instrument-rail {
20
+ /* AW.W15 — consume the shared twin-line α pair (the sibling-divider
21
+ ::before/::after rules below read --twin-line-catch/--twin-line-shadow). */
22
+ @apply twin-line-divider;
20
23
  position: relative;
21
24
  display: flex;
22
25
  flex-direction: column;
@@ -71,13 +74,17 @@
71
74
  a sibling ::before/::after stack keeps consumers free of manual
72
75
  <ChassisDivider> slotting. Status header is excluded — the
73
76
  opening-edge status light reads as a region, not a sibling. */
77
+ /* AW.W15 — the twin-line α pair reads from the shared `twin-line-divider`
78
+ tokens (`@apply`ed on `.instrument-rail` above; dark-adaptive there), so
79
+ the prior per-variant light pair + the `.dark` re-declaration collapse
80
+ onto the single source. */
74
81
  .instrument-rail[data-divider-rule][data-variant="vertical"]
75
82
  > :not(:first-child):not(.instrument-rail-status)::before {
76
83
  content: "";
77
84
  display: block;
78
85
  height: 1px;
79
86
  margin-block-end: 1px;
80
- background: rgb(255 255 255 / 0.10);
87
+ background: var(--twin-line-catch);
81
88
  }
82
89
 
83
90
  .instrument-rail[data-divider-rule][data-variant="vertical"]
@@ -86,7 +93,7 @@
86
93
  display: block;
87
94
  height: 1px;
88
95
  margin-block-start: -1px;
89
- background: rgb(0 0 0 / 0.12);
96
+ background: var(--twin-line-shadow);
90
97
  }
91
98
 
92
99
  .instrument-rail[data-divider-rule][data-variant="horizontal"]
@@ -95,7 +102,7 @@
95
102
  display: block;
96
103
  width: 1px;
97
104
  margin-inline-end: 1px;
98
- background: rgb(255 255 255 / 0.10);
105
+ background: var(--twin-line-catch);
99
106
  }
100
107
 
101
108
  .instrument-rail[data-divider-rule][data-variant="horizontal"]
@@ -104,15 +111,7 @@
104
111
  display: block;
105
112
  width: 1px;
106
113
  margin-inline-start: -1px;
107
- background: rgb(0 0 0 / 0.12);
108
- }
109
-
110
- .dark .instrument-rail[data-divider-rule] > :not(:first-child):not(.instrument-rail-status)::before {
111
- background: rgb(255 255 255 / 0.06);
112
- }
113
-
114
- .dark .instrument-rail[data-divider-rule] > :not(:first-child):not(.instrument-rail-status)::after {
115
- background: rgb(0 0 0 / 0.18);
114
+ background: var(--twin-line-shadow);
116
115
  }
117
116
 
118
117
  /* Status header — pinned to the opening edge, flush against the
@@ -146,7 +146,7 @@
146
146
  snappy: (0.35s, ζ=0.65) — quick crisp, overshoot ~+6.8%
147
147
  bouncy: (0.50s, ζ=0.45) — emphatic overshoot ~+20.5%
148
148
  gentle: (0.70s, ζ=1.00) — patient critically-damped
149
- dock: (0.50s, ζ=0.50) — dock expand/collapse morph, overshoot ~+18.5%
149
+ dock: (0.32s, ζ=0.70) — dock expand/collapse morph, overshoot ~+4.6%
150
150
 
151
151
  Note on `smooth`: at ζ=0.86 the analytic solver produces a peak
152
152
  of 1.00498 (+0.5%) at the 24% slot — mathematically present but
@@ -160,7 +160,7 @@
160
160
  --spring-snappy: linear(0, 0.10438 2.041%, 0.32622 4.082%, 0.56668 6.122%, 0.77153 8.163%, 0.91961 10.204%, 1.01073 12.245%, 1.05549 14.286%, 1.06804 16.327%, 1.06171 18.367%, 1.04686 20.408%, 1.03044 22.449%, 1.01629 24.490%, 1.00596 26.531%, 0.99954 28.571%, 0.99633 30.612%, 0.99537 32.653%, 0.99575 34.694%, 0.99674 36.735%, 0.99786 38.776%, 0.99884 40.816%, 0.99956 42.857%, 1.00001 44.898%, 1.00024 46.939%, 1.00000 48.980%, 1.00000 51.020%, 1.00000 53.061%, 1.00000 55.102%, 1.00000 57.143%, 1.00000 59.184%, 1.00000 61.224%, 1.00000 63.265%, 1.00000 65.306%, 1.00000 67.347%, 1.00000 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
161
161
  --spring-bouncy: linear(0, 0.11105 2.041%, 0.36430 4.082%, 0.65496 6.122%, 0.90929 8.163%, 1.08828 10.204%, 1.18289 12.245%, 1.20482 14.286%, 1.17641 16.327%, 1.12214 18.367%, 1.06276 20.408%, 1.01239 22.449%, 0.97809 24.490%, 0.96099 26.531%, 0.95826 28.571%, 0.96514 30.612%, 0.97666 32.653%, 0.98873 34.694%, 0.99867 36.735%, 1.00520 38.776%, 1.00825 40.816%, 1.00847 42.857%, 1.00686 44.898%, 1.00443 46.939%, 1.00199 48.980%, 1.00004 51.020%, 0.99880 53.061%, 0.99827 55.102%, 0.99829 57.143%, 0.99866 59.184%, 0.99916 61.224%, 0.99966 63.265%, 1.00004 65.306%, 1.00027 67.347%, 1.00000 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
162
162
  --spring-gentle: linear(0, 0.09415 2.041%, 0.27374 4.082%, 0.45506 6.122%, 0.60779 8.163%, 0.72570 10.204%, 0.81213 12.245%, 0.87336 14.286%, 0.91570 16.327%, 0.94445 18.367%, 0.96371 20.408%, 0.97645 22.449%, 0.98481 24.490%, 0.99025 26.531%, 0.99377 28.571%, 0.99604 30.612%, 0.99749 32.653%, 0.99841 34.694%, 0.99900 36.735%, 0.99937 38.776%, 0.99961 40.816%, 0.99975 42.857%, 0.99985 44.898%, 1.00000 46.939%, 1.00000 48.980%, 1.00000 51.020%, 1.00000 53.061%, 1.00000 55.102%, 1.00000 57.143%, 1.00000 59.184%, 1.00000 61.224%, 1.00000 63.265%, 1.00000 65.306%, 1.00000 67.347%, 1.00000 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
163
- --spring-dock: linear(0, 0.10932 2.041%, 0.35412 4.082%, 0.63074 6.122%, 0.87072 8.163%, 1.04044 10.204%, 1.13391 12.245%, 1.16292 14.286%, 1.14748 16.327%, 1.10854 18.367%, 1.06337 20.408%, 1.02357 22.449%, 0.99499 24.490%, 0.97889 26.531%, 0.97350 28.571%, 0.97561 30.612%, 0.98178 32.653%, 0.98915 34.694%, 0.99574 36.735%, 1.00055 38.776%, 1.00332 40.816%, 1.00431 42.857%, 1.00403 44.898%, 1.00305 46.939%, 1.00186 48.980%, 1.00076 51.020%, 0.99996 53.061%, 0.99948 55.102%, 0.99930 57.143%, 0.99933 59.184%, 0.99949 61.224%, 0.99968 63.265%, 0.99986 65.306%, 1.00000 67.347%, 1.00008 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
163
+ --spring-dock: linear(0, 0.10282 2.041%, 0.31773 4.082%, 0.54783 6.122%, 0.74309 8.163%, 0.88550 10.204%, 0.97598 12.245%, 1.02452 14.286%, 1.04368 16.327%, 1.04501 18.367%, 1.03739 20.408%, 1.02681 22.449%, 1.01672 24.490%, 1.00873 26.531%, 1.00323 28.571%, 0.99996 30.612%, 0.99837 32.653%, 0.99789 34.694%, 0.99805 36.735%, 0.99848 38.776%, 0.99897 40.816%, 0.99940 42.857%, 0.99972 44.898%, 0.99993 46.939%, 1.00004 48.980%, 1.00000 51.020%, 1.00000 53.061%, 1.00000 55.102%, 1.00000 57.143%, 1.00000 59.184%, 1.00000 61.224%, 1.00000 63.265%, 1.00000 65.306%, 1.00000 67.347%, 1.00000 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
164
164
 
165
165
  /* Core cubic-bezier (exits, fallbacks, non-spring uses) */
166
166
  --motion-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
@@ -1287,17 +1287,19 @@
1287
1287
  both engine paths are timing-IDENTICAL: the FLIP fallback (via
1288
1288
  `--dock-motion-resize`), the native View-Transition group (its
1289
1289
  `animation-timing-function`), AND the runtime SpringProgress driver
1290
- (AU.W8.3) consume this same `(0.5, 0.5)` source curve. Retires the AQ.W6
1290
+ (AU.W8.3) consume this same `(0.32, 0.7)` source curve. Retires the AQ.W6
1291
1291
  fork where the VT path ran `--vt-ease` (apple-spring, +27.5% overshoot)
1292
1292
  while the FLIP ran the snappy spring. `--vt-ease` still defaults the
1293
1293
  other VT recipes (`.gl-list-item`).
1294
1294
 
1295
1295
  AU.W8 — RETARGET snappy → dock. `--spring-snappy` (ζ=0.65, +6.8%) read as
1296
1296
  mechanical: a stiff plateau at ~48% with too little overshoot to feel
1297
- alive ("not iOS-smooth"). `--spring-dock` ((0.5, 0.5), ~+18.5%) sits in
1298
- the springy 15-30% band — the user-felt iOS bounce. The runtime driver's
1299
- DOCK_SPRING const MIRRORS the (0.5, 0.5) PRESETS row, so the CSS token and
1300
- the JS-driven morph are bit-identical. */
1297
+ alive ("not iOS-smooth"). AW.W2 then retuned the dock spring to the
1298
+ iOS-control register: `--spring-dock` ((0.32, 0.7), ~+4.6%) settles snappy
1299
+ with a restrained overshoot — the felt iOS dock morph, off the prior
1300
+ playful +18.5% register. The runtime driver's DOCK_SPRING const MIRRORS the
1301
+ (0.32, 0.7) PRESETS row, so the CSS token and the JS-driven morph are
1302
+ bit-identical. */
1301
1303
  --dock-resize-spring: var(--spring-dock);
1302
1304
 
1303
1305
  /* AT.W7-dock-b — dock-control press micro-feedback spring. A TRANSFORM-ONLY
@@ -688,6 +688,26 @@
688
688
  }
689
689
  }
690
690
 
691
+ /* ── Twin-line divider — the chassis catch-light / under-shadow groove ───────
692
+ AW.W15 DRY extraction. The twin-line idiom (a 1px white catch-light over a
693
+ 1px black under-shadow reading as one engraved hairline groove) is the
694
+ chassis-divider signature. The exact light/dark α pair was DUPLICATED in
695
+ `instrument-chassis.css` (`.bezel-line.top`/`.bottom`) and `instrument-rail.css`
696
+ (the sibling `::before`/`::after`). This utility is the SINGLE source of that
697
+ α pair: it publishes `--twin-line-catch` (the top catch-light) and
698
+ `--twin-line-shadow` (the bottom under-shadow) as cascade tokens, dark-adaptive
699
+ by construction (the `.dark &` arm re-resolves them). Both consumers read the
700
+ tokens; a retint touches one rule. Real DRY, ≥2 consumers. */
701
+ @utility twin-line-divider {
702
+ --twin-line-catch: rgb(255 255 255 / 0.10);
703
+ --twin-line-shadow: rgb(0 0 0 / 0.12);
704
+
705
+ :where(.dark) & {
706
+ --twin-line-catch: rgb(255 255 255 / 0.06);
707
+ --twin-line-shadow: rgb(0 0 0 / 0.18);
708
+ }
709
+ }
710
+
691
711
  /* ── Control transition recipe (AU.W8b.4) ───────────────────────────────
692
712
  Shared compound transition for interactive controls that animate their
693
713
  surface (background-color/color/box-shadow/border-color) on the four-state
@@ -61,6 +61,30 @@
61
61
  animation-timing-function: var(--dock-resize-spring, var(--spring-dock));
62
62
  }
63
63
 
64
+ /* AW.W3 — typed directional dock curves. The swap tags the active transition
65
+ * `dock-expand` / `dock-collapse` (the outer collapsed↔expanded pair) and
66
+ * `layer-forward` / `layer-back` (the inner DockLayerGroup pane swap) via the
67
+ * `types` argument to `startViewTransition` (Chrome 140+; feature-detected, the
68
+ * untyped fallback is the symmetric `.gl-dock-layer` curve above). iOS-feel
69
+ * motion is ASYMMETRIC — a snappier exit, a softer entry-overshoot. EXPAND
70
+ * (and forward) lead with the overshooting `--spring-dock`; COLLAPSE (and back)
71
+ * exit on the non-overshooting `--spring-snappy` (a quicker settle, no bounce on
72
+ * the way out). The shorter `--duration-fast` on the exit reads as the crisp
73
+ * dismiss. A non-typed engine never matches these — it runs the symmetric curve,
74
+ * which is the acceptable contract. PRM zeroes the animation (the `@media` at
75
+ * the top of this file). */
76
+ :active-view-transition-type(dock-expand) ::view-transition-group(.gl-dock-layer),
77
+ :active-view-transition-type(layer-forward) ::view-transition-group(.gl-dock-layer) {
78
+ animation-duration: var(--vt-duration, var(--duration-normal));
79
+ animation-timing-function: var(--dock-resize-spring, var(--spring-dock));
80
+ }
81
+
82
+ :active-view-transition-type(dock-collapse) ::view-transition-group(.gl-dock-layer),
83
+ :active-view-transition-type(layer-back) ::view-transition-group(.gl-dock-layer) {
84
+ animation-duration: var(--vt-duration-exit, var(--duration-fast));
85
+ animation-timing-function: var(--spring-snappy);
86
+ }
87
+
64
88
  @keyframes gl-vt-slide-in { from { opacity: 0; translate: 0 var(--vt-rise, 8px); } }
65
89
  @keyframes gl-vt-slide-out { to { opacity: 0; translate: 0 var(--vt-rise, 8px); } }
66
90
 
@@ -1,97 +0,0 @@
1
- import { getCurrentScope as e, onMounted as t, onScopeDispose as n, ref as r, toValue as i, watch as a } from "vue";
2
- //#region src/composables/dom/useTokenColor.ts
3
- function o(o, s = {}) {
4
- let c = s.fallback ?? "", l = r(c);
5
- function u() {
6
- if (typeof document > "u") return c;
7
- let e = i(o);
8
- if (!e) return c;
9
- let t = (s.element ? i(s.element) : null) ?? document.documentElement;
10
- return t && getComputedStyle(t).getPropertyValue(e).trim() || c;
11
- }
12
- function d() {
13
- l.value = u();
14
- }
15
- if (d(), t(d), typeof document < "u") {
16
- let t = new MutationObserver(d);
17
- t.observe(document.documentElement, {
18
- attributes: !0,
19
- attributeFilter: ["class"]
20
- });
21
- let r = typeof window < "u" && window.matchMedia ? window.matchMedia("(prefers-color-scheme: dark)") : null;
22
- r?.addEventListener?.("change", d), e() && n(() => {
23
- t.disconnect(), r?.removeEventListener?.("change", d);
24
- });
25
- }
26
- return a(() => i(o), d), s.element !== void 0 && a(() => i(s.element), d), {
27
- value: l,
28
- refresh: d
29
- };
30
- }
31
- //#endregion
32
- //#region src/composables/dom/useClipboard.ts
33
- async function s(e) {
34
- if (typeof navigator > "u" || !navigator.clipboard?.writeText) return {
35
- ok: !1,
36
- reason: "no-api"
37
- };
38
- try {
39
- return await navigator.clipboard.writeText(e), { ok: !0 };
40
- } catch {
41
- return {
42
- ok: !1,
43
- reason: "clipboard-api"
44
- };
45
- }
46
- }
47
- function c(e) {
48
- if (typeof document > "u") return {
49
- ok: !1,
50
- reason: "no-api"
51
- };
52
- let t = document.createElement("textarea");
53
- t.value = e, t.setAttribute("readonly", ""), t.style.position = "fixed", t.style.top = "-9999px", document.body.appendChild(t), t.select();
54
- let n = !1;
55
- try {
56
- n = document.execCommand("copy");
57
- } catch {
58
- n = !1;
59
- }
60
- return document.body.removeChild(t), n ? { ok: !0 } : {
61
- ok: !1,
62
- reason: "exec-command"
63
- };
64
- }
65
- async function l(e, t) {
66
- let n = await s(e);
67
- if (n.ok) return n;
68
- let r = c(e);
69
- if (r.ok) return r;
70
- let i = r.reason ?? n.reason ?? "no-api";
71
- return t?.(i), {
72
- ok: !1,
73
- reason: i
74
- };
75
- }
76
- async function u(e, t = {}) {
77
- let { ok: n } = await l(e, t.onCopyError);
78
- return n;
79
- }
80
- function d(e = {}) {
81
- let t = e.resetMs ?? 1500, i = r(!1), a = null;
82
- function o() {
83
- a != null && (clearTimeout(a), a = null);
84
- }
85
- async function s(n) {
86
- let r = await l(n, e.onCopyError);
87
- return r.ok && (i.value = !0, o(), a = setTimeout(() => {
88
- i.value = !1, a = null;
89
- }, t)), r;
90
- }
91
- return n(o), {
92
- copied: i,
93
- copy: s
94
- };
95
- }
96
- //#endregion
97
- export { d as n, o as r, u as t };
@@ -1,18 +0,0 @@
1
- //#region src/composables/motion/useViewTransition.ts
2
- function e() {
3
- return typeof document < "u" && typeof document.startViewTransition == "function";
4
- }
5
- function t(e) {
6
- let t = typeof document > "u" ? void 0 : document;
7
- if (!t || typeof t.startViewTransition != "function") return e(), {
8
- finished: Promise.resolve(),
9
- transitioned: !1
10
- };
11
- let n = t.startViewTransition(() => e());
12
- return n.ready?.catch(() => {}), {
13
- finished: n.finished.then(() => void 0, () => void 0),
14
- transitioned: !0
15
- };
16
- }
17
- //#endregion
18
- export { e as n, t };