@mkbabb/glass-ui 3.8.0 → 3.10.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 (228) hide show
  1. package/README.md +1 -6
  2. package/dist/{CardAction-q2qHCJFG.js → CardAction-DuArVky-.js} +4 -4
  3. package/dist/{CommandShortcut-SzWRhROC.js → CommandShortcut-CefCj5mt.js} +7 -7
  4. package/dist/{ContextMenuSubContent-Do977Cbf.js → ContextMenuSubContent-Bhg4f5jX.js} +3 -3
  5. package/dist/{DataTable-CvlWXz65.js → DataTable-CUHMukiQ.js} +1 -1
  6. package/dist/{DialogContent-DjZWT3lK.js → DialogContent-BRkT1cjP.js} +1 -1
  7. package/dist/{MetricBadge-lTc002u2.js → MetricBadge-BZVWIxNy.js} +6 -4
  8. package/dist/{Notification-BxK8LZMw.js → Notification-DLMhblXR.js} +2 -2
  9. package/dist/{NumberFieldContent-CLNVR1iY.js → NumberFieldContent-BbLOh8RU.js} +2 -2
  10. package/dist/{SelectScrollDownButton-C7TRe-ep.js → SelectScrollDownButton-T32cVFeM.js} +3 -3
  11. package/dist/{SelectSeparator-L8pPEn4L.js → SelectSeparator-C4Ql0gzV.js} +1 -1
  12. package/dist/{Switch-LDKLmmgx.js → Switch-Ch-k3Rv-.js} +1 -1
  13. package/dist/{TabsList-qyHZ2lKY.js → TabsList-B8CVOQPC.js} +2 -2
  14. package/dist/{Toaster-CabYEEF_.js → Toaster-1rYWrmAm.js} +1 -1
  15. package/dist/{ToggleGroupItem-CkXUcBlt.js → ToggleGroupItem-Cev4pOVK.js} +4 -4
  16. package/dist/animated-digit.js +1 -1
  17. package/dist/api/index.d.ts +2 -1
  18. package/dist/api.js +1 -1
  19. package/dist/aurora.js +236 -545
  20. package/dist/{badge-DHQrc_GQ.js → badge-CaoFJzRb.js} +5 -5
  21. package/dist/badge.js +1 -1
  22. package/dist/{button-CAywcrhO.js → button-DN39cGqL.js} +13 -12
  23. package/dist/button.js +1 -1
  24. package/dist/card.js +1 -1
  25. package/dist/carousel.js +1 -1
  26. package/dist/color.js +1 -1
  27. package/dist/command.js +1 -1
  28. package/dist/{compile-BBTRjvQS.js → compile-BOXjLUxu.js} +24 -24
  29. package/dist/components/custom/aurora/composables/atoms.d.ts +21 -4
  30. package/dist/components/custom/aurora/composables/runtime.d.ts +0 -9
  31. package/dist/components/custom/aurora/composables/uniformBridge.d.ts +0 -11
  32. package/dist/components/custom/aurora/constants/renderMode.d.ts +0 -32
  33. package/dist/components/custom/aurora/constants/shaders/brush.glsl.d.ts +1 -1
  34. package/dist/components/custom/aurora/constants/shaders/mediums.glsl.d.ts +2 -2
  35. package/dist/components/custom/aurora/constants/shaders/tonemap.glsl.d.ts +1 -1
  36. package/dist/components/custom/aurora/index.d.ts +1 -1
  37. package/dist/components/custom/configurator/ConfiguratorLayer.vue.d.ts +1 -1
  38. package/dist/components/custom/constellation/Constellation.vue.d.ts +73 -1
  39. package/dist/components/custom/constellation/constellationField.d.ts +255 -3
  40. package/dist/components/custom/constellation/index.d.ts +2 -2
  41. package/dist/components/custom/dock/DockSeparator.vue.d.ts +3 -0
  42. package/dist/components/custom/dock/DockTabButton.vue.d.ts +4 -3
  43. package/dist/components/custom/dock/GlassDock.vue.d.ts +45 -63
  44. package/dist/components/custom/dock/composables/dockContext.d.ts +7 -0
  45. package/dist/components/custom/dock/index.d.ts +2 -1
  46. package/dist/components/custom/expandable-container/ExpandableContainer.vue.d.ts +29 -0
  47. package/dist/components/custom/fourier-field/FourierField.vue.d.ts +6 -0
  48. package/dist/components/custom/fourier-field/index.d.ts +25 -1
  49. package/dist/components/custom/fourier-field/math.d.ts +3 -8
  50. package/dist/components/custom/goo-blob/GooBlob.vue.d.ts +8 -10
  51. package/dist/components/custom/goo-blob/composables/useBlobMood.d.ts +19 -5
  52. package/dist/components/custom/goo-blob/composables/useMetaballRenderer.d.ts +6 -14
  53. package/dist/components/custom/goo-blob/index.d.ts +1 -1
  54. package/dist/components/custom/goo-blob/types.d.ts +59 -25
  55. package/dist/components/custom/handmark/HandMark.vue.d.ts +29 -0
  56. package/dist/components/custom/handmark/brush.d.ts +219 -0
  57. package/dist/components/custom/handmark/freehand.d.ts +69 -0
  58. package/dist/components/custom/handmark/geometry.d.ts +32 -0
  59. package/dist/components/custom/handmark/index.d.ts +21 -0
  60. package/dist/components/custom/handmark/ink.d.ts +11 -0
  61. package/dist/components/custom/handmark/texture.d.ts +39 -0
  62. package/dist/components/custom/handmark/types.d.ts +53 -0
  63. package/dist/components/custom/handmark/useHandMark.d.ts +66 -0
  64. package/dist/components/custom/metric-badge/MetricBadge.vue.d.ts +8 -0
  65. package/dist/components/custom/tabs/SegmentedTabs.vue.d.ts +92 -0
  66. package/dist/components/custom/tabs/composables/useTabIndicator.d.ts +49 -0
  67. package/dist/components/custom/tabs/index.d.ts +2 -5
  68. package/dist/components/ui/button/index.d.ts +1 -1
  69. package/dist/components/ui/card/Card.vue.d.ts +5 -3
  70. package/dist/components/ui/carousel/CarouselNext.vue.d.ts +1 -1
  71. package/dist/components/ui/carousel/CarouselPrevious.vue.d.ts +1 -1
  72. package/dist/components/ui/carousel/GlassCarouselPager.vue.d.ts +3 -4
  73. package/dist/components/ui/slider/index.d.ts +22 -11
  74. package/dist/components/ui/tabs/TabsList.vue.d.ts +1 -1
  75. package/dist/composables/color/index.d.ts +12 -7
  76. package/dist/composables/context/createContext.d.ts +1 -1
  77. package/dist/composables/glass/webgl/shaders/procedural-color.glsl.d.ts +1 -6
  78. package/dist/composables/motion/core/index.d.ts +1 -0
  79. package/dist/composables/motion/useRouteTransition.d.ts +42 -0
  80. package/dist/configurator.js +1 -1
  81. package/dist/confirm-dialog.js +1 -1
  82. package/dist/constellation.js +212 -72
  83. package/dist/context-menu.js +1 -1
  84. package/dist/controls.js +1 -1
  85. package/dist/dark.js +1 -1
  86. package/dist/data-table.js +1 -1
  87. package/dist/deck-progress.js +1 -1
  88. package/dist/dialog.js +1 -1
  89. package/dist/dock.js +257 -243
  90. package/dist/{dropdown-menu-B_4bWyyG.js → dropdown-menu-D77jX6Df.js} +3 -3
  91. package/dist/dropdown-menu.js +1 -1
  92. package/dist/expandable-container.js +61 -38
  93. package/dist/forms.js +5 -5
  94. package/dist/fourier-field.js +89 -97
  95. package/dist/fourier-math.d.ts +1 -0
  96. package/dist/fourier-math.js +2 -0
  97. package/dist/glass-panel.js +1 -1
  98. package/dist/glass-ui.css +1 -1
  99. package/dist/glass-ui.js +107 -109
  100. package/dist/goo-blob.js +316 -297
  101. package/dist/handmark.d.ts +1 -0
  102. package/dist/handmark.js +609 -0
  103. package/dist/hover-popover.js +1 -1
  104. package/dist/icon-tooltip.js +1 -1
  105. package/dist/index.d.ts +1 -2
  106. package/dist/keyboard.js +1 -1
  107. package/dist/labeled-field.js +5 -5
  108. package/dist/math-DTpRVC0e.js +27 -0
  109. package/dist/{menuItemVariants-DKM85SNE.js → menuItemVariants-CCoBNkU5.js} +1 -1
  110. package/dist/metric-badge.js +1 -1
  111. package/dist/metric-stack.js +1 -1
  112. package/dist/motion-core.js +70 -69
  113. package/dist/motion.js +2 -2
  114. package/dist/notification.js +1 -1
  115. package/dist/number-field.js +1 -1
  116. package/dist/pulse.js +1 -1
  117. package/dist/reactive.js +1 -1
  118. package/dist/scrolling-text.js +1 -1
  119. package/dist/search.js +4 -4
  120. package/dist/select.js +2 -2
  121. package/dist/{slider-D9puf-Ko.js → slider-CyzcxBoh.js} +6 -6
  122. package/dist/slider.js +1 -1
  123. package/dist/sortable-list.js +1 -1
  124. package/dist/status-dot.js +17 -16
  125. package/dist/styles/animations.css +80 -20
  126. package/dist/styles/cards.css +2 -2
  127. package/dist/styles/components.css +1 -1
  128. package/dist/styles/dock/density.css +230 -0
  129. package/dist/styles/dock/layer-group.css +275 -0
  130. package/dist/styles/dock/layers.css +355 -0
  131. package/dist/styles/dock/morph.css +244 -0
  132. package/dist/styles/dock/overflow.css +201 -0
  133. package/dist/styles/dock/shell.css +457 -0
  134. package/dist/styles/dock-controls.css +205 -55
  135. package/dist/styles/dock.css +42 -1349
  136. package/dist/styles/drawer.css +12 -4
  137. package/dist/styles/glass.css +535 -78
  138. package/dist/styles/index.css +12 -11
  139. package/dist/styles/paper.css +68 -2
  140. package/dist/styles/theme.css +58 -0
  141. package/dist/styles/tokens.css +601 -69
  142. package/dist/styles/transitions.css +20 -2
  143. package/dist/styles/utilities.css +218 -78
  144. package/dist/styles/view-transition.css +51 -0
  145. package/dist/switch.js +1 -1
  146. package/dist/tabs.js +182 -151
  147. package/dist/timeline.js +1 -1
  148. package/dist/toast.js +1 -1
  149. package/dist/toggle-group.js +1 -1
  150. package/dist/{useAnimatedNumber-C_3wZLx4.js → useAnimatedNumber-Bcpy5Etf.js} +1 -1
  151. package/dist/{useConfiguratorState-CMff4GzH.js → useConfiguratorState-DxAqksK6.js} +21 -10
  152. package/dist/{useGlassRenderer-5I0YkI4t.js → useGlassRenderer-D7YGOYiM.js} +1 -1
  153. package/dist/useViewTransition-DaN5Zrox.js +21 -0
  154. package/dist/vReveal-zUIQk-rJ.js +53 -0
  155. package/dist/watercolor-dot.js +1 -1
  156. package/package.json +80 -39
  157. package/src/styles/animations.css +80 -20
  158. package/src/styles/cards.css +2 -2
  159. package/src/styles/dock/density.css +230 -0
  160. package/src/styles/dock/layer-group.css +275 -0
  161. package/src/styles/dock/layers.css +355 -0
  162. package/src/styles/dock/morph.css +244 -0
  163. package/src/styles/dock/overflow.css +201 -0
  164. package/src/styles/dock/shell.css +457 -0
  165. package/src/styles/dock-controls.css +205 -55
  166. package/src/styles/dock.css +42 -1349
  167. package/src/styles/drawer.css +12 -4
  168. package/src/styles/glass.css +535 -78
  169. package/src/styles/index.css +12 -11
  170. package/src/styles/paper.css +68 -2
  171. package/src/styles/theme.css +58 -0
  172. package/src/styles/tokens.css +601 -69
  173. package/src/styles/transitions.css +20 -2
  174. package/src/styles/utilities.css +218 -78
  175. package/src/styles/view-transition.css +51 -0
  176. package/dist/DiscoGlyph-CG0F410s.js +0 -132
  177. package/dist/GlyphFace-DRjDkB0s.js +0 -51
  178. package/dist/UnderlineTabs-Uxn-R3MT.js +0 -39
  179. package/dist/components/custom/aurora/composables/gpuRuntime.d.ts +0 -16
  180. package/dist/components/custom/aurora/constants/shaders/aurora.wgsl.d.ts +0 -7
  181. package/dist/components/custom/aurora/constants/shaders/painterly.wgsl.d.ts +0 -7
  182. package/dist/components/custom/aurora/constants/shaders/wake.wgsl.d.ts +0 -4
  183. package/dist/components/custom/disco-glyph/DiscoGlyph.vue.d.ts +0 -58
  184. package/dist/components/custom/disco-glyph/index.d.ts +0 -1
  185. package/dist/components/custom/glass-carousel/GlassCarousel.vue.d.ts +0 -36
  186. package/dist/components/custom/glass-carousel/GlassCarouselItem.vue.d.ts +0 -28
  187. package/dist/components/custom/glass-carousel/index.d.ts +0 -4
  188. package/dist/components/custom/glass-carousel/useGlassCarousel.d.ts +0 -30
  189. package/dist/components/custom/glyph-face/GlyphFace.vue.d.ts +0 -64
  190. package/dist/components/custom/glyph-face/index.d.ts +0 -2
  191. package/dist/components/custom/glyph-face/keys.d.ts +0 -9
  192. package/dist/components/custom/responsive-tabs/ResponsiveTabs.vue.d.ts +0 -73
  193. package/dist/components/custom/responsive-tabs/index.d.ts +0 -2
  194. package/dist/components/custom/tabs/BouncyTabs.vue.d.ts +0 -27
  195. package/dist/components/custom/tabs/BouncyToggle.vue.d.ts +0 -60
  196. package/dist/components/custom/tabs/UnderlineTabs.vue.d.ts +0 -20
  197. package/dist/components/custom/tabs/composables/useBouncySlider.d.ts +0 -39
  198. package/dist/composables/glass/createGPUCanvas.d.ts +0 -40
  199. package/dist/disco-glyph.d.ts +0 -1
  200. package/dist/disco-glyph.js +0 -2
  201. package/dist/glass-carousel.d.ts +0 -1
  202. package/dist/glass-carousel.js +0 -219
  203. package/dist/glyph-face.d.ts +0 -1
  204. package/dist/glyph-face.js +0 -3
  205. package/dist/keys-BsYjEFOW.js +0 -9
  206. package/dist/responsive-tabs.d.ts +0 -1
  207. package/dist/responsive-tabs.js +0 -95
  208. package/dist/styles/disco-glyph.css +0 -26
  209. package/dist/styles/glyph-face.css +0 -92
  210. package/dist/vReveal-CqaCz_mC.js +0 -44
  211. package/src/styles/disco-glyph.css +0 -26
  212. package/src/styles/glyph-face.css +0 -92
  213. /package/dist/{DeckProgress-AVgbIULY.js → DeckProgress-Dmk4_AKN.js} +0 -0
  214. /package/dist/{HoverPopover-D56O6Ylp.js → HoverPopover-2LOIW34a.js} +0 -0
  215. /package/dist/{IconTooltip-D2T5nYOM.js → IconTooltip-Cus0fvK0.js} +0 -0
  216. /package/dist/{Input-DVG_J0ne.js → Input-xuDwALQR.js} +0 -0
  217. /package/dist/{ScrollingText-CVyqxvfv.js → ScrollingText-BPP84wKo.js} +0 -0
  218. /package/dist/{color-DMLTunJK.js → color-C5ls_QF7.js} +0 -0
  219. /package/dist/{constants-BPOqYJAm.js → constants-yKeKnE84.js} +0 -0
  220. /package/dist/{presets-D1XPuTj-.js → presets-Dkdv55Gt.js} +0 -0
  221. /package/dist/{prng-COGhh02-.js → prng-D27-B3tK.js} +0 -0
  222. /package/dist/{useGlobalDark-D3EBWzXK.js → useGlobalDark-d6waty-X.js} +0 -0
  223. /package/dist/{useIntersectionPause-Bj4sPkMh.js → useIntersectionPause-Cck5Uxk8.js} +0 -0
  224. /package/dist/{useInterval-CcLfq2WU.js → useInterval-Dsmqwi03.js} +0 -0
  225. /package/dist/{useKeyboardShortcuts-BXWXOZ-9.js → useKeyboardShortcuts-CyIYrndH.js} +0 -0
  226. /package/dist/{useScrollProgress-C9ysDic5.js → useScrollProgress-CffFGLa5.js} +0 -0
  227. /package/dist/{useSortable-MGAbCGVJ.js → useSortable-C8ReQ50P.js} +0 -0
  228. /package/dist/{useTextHighlight-9nMW23Qb.js → useTextHighlight-CWEi25hf.js} +0 -0
@@ -1,9 +1,45 @@
1
1
  /* Dock primitives exported through @mkbabb/glass-ui/styles.
2
2
  *
3
- * This file is the style authority for the dock component family. The Vue
4
- * SFCs own structure and runtime behavior; selectors, motion, glass, and
5
- * layering tokens live here so consumers have one public CSS path.
6
- */
3
+ * This file is the CORE of the dock component family — the `@property
4
+ * --dock-morph-t` registration, the @layer-components OPEN, the SHARED
5
+ * cross-control four-state contract (the :focus-visible / :disabled comma-groups
6
+ * + the motion-token :where() group — they reference every control selector but
7
+ * are shared, so they live at the import root, not per-control), and the
8
+ * `@import`s of the cohesive `dock/*.css` partials.
9
+ *
10
+ * AX.W06 — the 1762-line dock.css god-module (slice 25) was CARVED into
11
+ * src/styles/dock/ cohesive single-axis partials, each under the 500-line bound,
12
+ * @import-ed below in cascade order into the SAME `@layer components` so the
13
+ * carve is isomorphic (same layer, same source order → ZERO visual delta):
14
+ * - dock/shell.css — the `.glass-dock` structural skeleton (root, W02
15
+ * morph-clip aperture, grain, vertical-root, shape /
16
+ * variant plates, position, W45 #persistent + vertical
17
+ * three-region body).
18
+ * - dock/morph.css — the W01 single-scalar morph chrome + state response
19
+ * (--dock-expand-t interp, collapsed/summary, held /
20
+ * open, the W55 adaptive-legibility bucket).
21
+ * - dock/density.css — the W45 density × --dock-scale cascade.
22
+ * - dock/layers.css — the `.dock-layer` crossfade + hit-test + reveal-
23
+ * stagger contract + the `layout="grid"` tile grid.
24
+ * - dock/layer-group.css — `.dock-layer-group` / layer-switcher rail / stack.
25
+ * - dock/overflow.css — the `overflow=` scroll/wrap strategies + the coarse
26
+ * multiplier.
27
+ *
28
+ * The FIVE interactive control families stay in dock-controls.css (the AU.W8b.3
29
+ * carve, @import-ed by index.css directly after this file). The Vue SFCs own
30
+ * structure + runtime behavior; selectors, motion, glass, and layering tokens
31
+ * live here + in the partials so consumers have one public CSS path. */
32
+
33
+ /* CSS `@import` rules must precede every other rule (a `@property`/`@layer`
34
+ block after an `@import` would render it invalid + dropped). The six partials
35
+ @import in cascade order; each opens its own `@layer components` so the layered
36
+ cascade is preserved bit-for-bit (the carve-isomorphism gate is the witness). */
37
+ @import "./dock/shell.css";
38
+ @import "./dock/morph.css";
39
+ @import "./dock/density.css";
40
+ @import "./dock/layers.css";
41
+ @import "./dock/layer-group.css";
42
+ @import "./dock/overflow.css";
7
43
 
8
44
  /* AX.W01 — the SINGLE morph scalar. `useLayerTransition` writes it once per frame
9
45
  to the `.glass-dock` root; EVERY morph axis (box size, padding, radius, scale,
@@ -55,7 +91,8 @@
55
91
  group keeps full class+pseudo specificity (NOT `:where()`, which would
56
92
  zero the class and let a per-control :hover box-shadow override the focus
57
93
  ring). Per-control transition extensions (the box-shadow fade on
58
- focus-visible) stay with their own selectors below where they differ. */
94
+ focus-visible) stay with their own selectors in dock-controls.css where
95
+ they differ. */
59
96
  .dock-icon-button:focus-visible,
60
97
  .dock-tab-button:focus-visible,
61
98
  .dock-select-trigger:focus-visible,
@@ -71,1348 +108,4 @@
71
108
  opacity: var(--opacity-disabled);
72
109
  cursor: not-allowed;
73
110
  }
74
-
75
- .glass-dock {
76
- --dock-surface-blur: var(--glass-blur-dock, var(--glass-blur-wash));
77
- /* J.W3.C — vertical-rail max-block + horizontal max-inline caps
78
- live as semantic tokens (defaults in `tokens.css`). When
79
- content exceeds the cap, the appropriate axis container
80
- scrolls with a mask-fade. Consumers override per-instance by
81
- setting `--dock-max-block-size` / `--dock-max-inline-size`
82
- on a `.glass-dock` directly. */
83
- --dock-separator-height: calc(var(--dock-h, var(--size-icon-btn)) * 0.5);
84
- --dock-collapsed-hover-scale: var(--scale-hover-dock);
85
- /* AW.W3 — the spring-keyed child stagger knobs. `--dock-stagger-window-size`
86
- is the fraction of the morph each child reveals over; `--dock-stagger-rise`
87
- is the px the child rises from as it fades in. Consumers retune the
88
- cascade feel per instance.
89
-
90
- AX.W02 — `--dock-stagger-step` is the per-child onset BEAT: each
91
- successive control's reveal starts this fraction-of-morph later than the
92
- one before it, so the row fills outer→in. ONE token × the child index
93
- replaces the hand-typed 0.08/0.16/0.24/0.32/0.4 onset ladder — the onset
94
- is `step × (childIndex − 1)`, capped (the cap below). 0.08 = ~5% of the
95
- ~0.32s morph between items — inside Emil Kowalski's 30–60ms stagger band,
96
- never the >100ms "slideshow" register. The onset rides the NORMALIZED
97
- morph progress (`--dock-expand-t`), NOT a wall-clock timer, so a fast
98
- flick and a slow hover-open both choreograph correctly and an interrupted
99
- morph orphans no timers. */
100
- --dock-stagger-window-size: 0.55;
101
- --dock-stagger-rise: 4px;
102
- --dock-stagger-step: 0.08;
103
- /* AW.W3b — big-dock tokens. `--dock-card-radius` is the finite card-shell
104
- radius (default --radius-dock-card = --radius-3xl, density-scaled
105
- below); `--dock-tile-min` is the grid tile minimum (auto-fill track
106
- floor). Both are density-scaled (compact tighter, spacious looser) — the
107
- card radius + tile size ride the density cascade, never magic numbers. */
108
- --dock-card-radius: var(--radius-dock-card);
109
- --dock-tile-min: 4.5rem;
110
- /* Individual-transform identity base (AQ.W3 §W3.2). `scale: 1` at rest
111
- mints the stacking context once so a portaled popover over `--z-dock`
112
- stays above the dock during a hover scale (the load-bearing
113
- stacking-context guarantee). Covers every dock scale site. */
114
- scale: 1;
115
- position: relative;
116
- display: inline-flex;
117
- align-items: center;
118
- /* AW.W1 — the dock does NOT establish a default inline-size container.
119
- `container-type: inline-size` applies `contain: inline-size`, which
120
- makes an element's inline size INDEPENDENT of its contents — fatally
121
- incompatible with a shrink-to-fit pill: under it `.glass-dock` cannot
122
- size to its content and collapses to its padding floor (~19px) with the
123
- active layer overflowing (the 3.3.0 regression — every horizontal dock,
124
- collapsed AND always-expanded, rendered as a sliver). The `dock`-named
125
- container is now OPT-IN via the `containerName` prop: a consumer
126
- embedding the dock in a width-CONSTRAINED host (a 320px sidebar) sets
127
- `containerName="dock"` to get the descendant container-query reads (the
128
- audacious-label compression below), trading shrink-to-fit for the
129
- host-constrained width it already has there. The default free-floating
130
- dock shrink-wraps to its content (AV.W16 TW3 made this a default and
131
- silently broke dock sizing for the common case). */
132
- border-radius: var(--radius-dock);
133
- white-space: nowrap;
134
- max-inline-size: var(--dock-max-inline-size);
135
- padding: var(--dock-padding-block, 0.375rem) var(--dock-padding-inline, 0.5rem);
136
- background: var(--glass-bg-dock, var(--glass-bg-resting));
137
- backdrop-filter: var(--dock-surface-blur);
138
- border: 1.5px solid var(--glass-border-dock, var(--glass-border-resting));
139
- /* AV.W15 M3 — the full-perimeter `--glass-edge-light` rim rides as the
140
- leading inset layer of the dock shadow stack (the navigation-band
141
- silhouette catch-light, distinct from the top-only highlight). */
142
- box-shadow: var(--glass-edge-light), var(--shadow-dock-override, var(--shadow-dock));
143
- }
144
-
145
- /* AW.W2 — the box IS the reveal aperture (clip-reveal one-clock morph).
146
- Migrated from `overflow: hidden` to a SINGLE-AXIS `overflow: clip` on the
147
- MORPH axis (horizontal clips X / visible Y; vertical clips Y / visible X),
148
- gated on a `data-morphing` attribute the driver owns (set at gesture START,
149
- cleared on SETTLE). The clip PERSISTS through the morph — content is laid
150
- out ONCE at its natural size behind the aperture and the growing/shrinking
151
- box uncovers it, never spills it — and lifts to `visible` only at REST
152
- expanded (so grown content + the focus ring paint past the box). The
153
- prior `.expanded { overflow: visible }` (bound synchronously to
154
- `visualExpanded`) lifted the clip at frame 0, leaving no aperture to reveal
155
- through; the `:not([data-morphing])` guard holds the clip for the whole
156
- gesture and lifts it on the final paint.
157
-
158
- `overflow: clip` is PAINT-ONLY (not a scroll container): it never
159
- participates in `scrollIntoView`/scroll-anchoring, cannot be a
160
- scroll-driven-animation `scroller`, and drops the `min-width:0` scroll-box
161
- dance for the non-scroll shell. The cross axis is ALWAYS explicitly
162
- `visible` (the MDN single-axis-clip-degrades-to-hidden caveat). The
163
- legitimate scroll ports (`.dock-scroll-{x,y}`, `overflow: auto`) STAY
164
- `auto` — they are NOT migrated. AT.W7-dock-a's orthogonality holds: the
165
- `containerName` opt-in does not touch the clip. */
166
- .glass-dock:not(.vertical) {
167
- overflow-x: clip;
168
- overflow-y: visible;
169
- }
170
-
171
- .glass-dock.vertical {
172
- overflow-x: visible;
173
- overflow-y: clip;
174
- }
175
-
176
- /* DURING a morph the clip is the aperture — keyed off `data-morphing`, NOT
177
- off `.expanded`, so it survives the whole gesture and the box reveals its
178
- content rather than letting it spill. */
179
- .glass-dock[data-morphing]:not(.vertical) {
180
- overflow-x: clip;
181
- overflow-y: visible;
182
- }
183
-
184
- .glass-dock[data-morphing].vertical {
185
- overflow-x: visible;
186
- overflow-y: clip;
187
- }
188
-
189
- /* AT REST EXPANDED — lift the morph-axis clip (grown content + focus ring
190
- paint past the box). Fires ONLY when NOT morphing; the [data-morphing]
191
- rules above win during the gesture by being more specific + later. */
192
- .glass-dock.expanded:not([data-morphing]),
193
- .glass-dock.always-expanded:not([data-morphing]) {
194
- overflow: visible;
195
- }
196
-
197
- /* AX.W04 F6 — the vertical-dock max-block-size + internal-scroll CONTRACT.
198
- A vertical rail caps at `--dock-max-block-size` (the `.vertical` rule), but
199
- at rest the `overflow: visible` above let over-cap content SPILL — forcing a
200
- consumer (bbnf-buddy ToolsLayer) to hand-anchor a max-height + scroll. Ship
201
- the contract here: at rest a vertical column scrolls its OWN block axis when
202
- content exceeds the cap (cross axis stays `visible` for focus rings + grown
203
- popovers). When content fits under the cap (`height:auto` < cap) nothing
204
- scrolls — the scroll port only engages on real over-cap content. The
205
- block-axis morph clip stays `data-morphing`-gated above; this is the
206
- AT-REST port only. Consumers set `--dock-max-block-size` per-instance to
207
- anchor the column to the viewport without re-deriving the scroll plumbing. */
208
- .glass-dock.vertical.always-expanded:not([data-morphing]),
209
- .glass-dock.vertical.expanded:not([data-morphing]) {
210
- overflow-x: visible;
211
- overflow-y: auto;
212
- scrollbar-width: none;
213
- overscroll-behavior-y: contain;
214
- }
215
-
216
- .glass-dock.vertical.always-expanded:not([data-morphing])::-webkit-scrollbar,
217
- .glass-dock.vertical.expanded:not([data-morphing])::-webkit-scrollbar {
218
- display: none;
219
- }
220
-
221
- .glass-dock[data-density="compact"] {
222
- --dock-padding-block: var(--dock-density-compact-padding-block, 0.25rem);
223
- --dock-padding-inline: var(--dock-density-compact-padding-inline, 0.375rem);
224
- --dock-control-size: var(--dock-density-compact-control-size, 2rem);
225
- --dock-layer-height: var(--dock-density-compact-layer-height, 2rem);
226
- --dock-layer-gap: var(--dock-density-compact-gap, 0.25rem);
227
- --dock-trigger-padding-block: var(
228
- --dock-density-compact-trigger-padding-block,
229
- 0.1875rem
230
- );
231
- --dock-trigger-padding-inline: var(
232
- --dock-density-compact-trigger-padding-inline,
233
- 0.4375rem
234
- );
235
- --dock-tab-padding-block: var(
236
- --dock-density-compact-tab-padding-block,
237
- 0.3125rem
238
- );
239
- --dock-tab-padding-inline: var(
240
- --dock-density-compact-tab-padding-inline,
241
- 0.625rem
242
- );
243
- --dock-tab-h: 32px;
244
- }
245
-
246
- /* Comfortable density — the GlassDock default. Sits between compact
247
- and spacious. */
248
- .glass-dock[data-density="comfortable"] {
249
- --dock-padding-block: var(--dock-density-comfortable-padding-block, 0.375rem);
250
- --dock-padding-inline: var(--dock-density-comfortable-padding-inline, 0.5rem);
251
- --dock-control-size: var(--dock-density-comfortable-control-size, 2.5rem);
252
- --dock-layer-height: var(--dock-density-comfortable-layer-height, 2.5rem);
253
- --dock-layer-gap: var(--dock-density-comfortable-gap, 0.375rem);
254
- --dock-trigger-padding-block: var(
255
- --dock-density-comfortable-trigger-padding-block,
256
- 0.25rem
257
- );
258
- --dock-trigger-padding-inline: var(
259
- --dock-density-comfortable-trigger-padding-inline,
260
- 0.5rem
261
- );
262
- --dock-tab-padding-block: var(
263
- --dock-density-comfortable-tab-padding-block,
264
- 0.4375rem
265
- );
266
- --dock-tab-padding-inline: var(
267
- --dock-density-comfortable-tab-padding-inline,
268
- 0.75rem
269
- );
270
- --dock-tab-h: 38px;
271
- }
272
-
273
- .glass-dock[data-density="spacious"] {
274
- --dock-padding-block: var(--dock-density-spacious-padding-block, 0.5rem);
275
- --dock-padding-inline: var(--dock-density-spacious-padding-inline, 0.75rem);
276
- --dock-control-size: var(--dock-density-spacious-control-size, 2.75rem);
277
- --dock-layer-height: var(--dock-density-spacious-layer-height, 2.75rem);
278
- --dock-layer-gap: var(--dock-density-spacious-gap, 0.5rem);
279
- --dock-trigger-padding-block: var(
280
- --dock-density-spacious-trigger-padding-block,
281
- 0.375rem
282
- );
283
- --dock-trigger-padding-inline: var(
284
- --dock-density-spacious-trigger-padding-inline,
285
- 0.625rem
286
- );
287
- --dock-tab-padding-block: var(
288
- --dock-density-spacious-tab-padding-block,
289
- 0.5rem
290
- );
291
- --dock-tab-padding-inline: var(
292
- --dock-density-spacious-tab-padding-inline,
293
- 0.875rem
294
- );
295
- --dock-tab-h: 44px;
296
- }
297
-
298
- /* Audacious density — typography-forward chrome where the dock reads as
299
- a primary surface, not a tray. Tab buttons set their own min-height so
300
- consumer-bumped fonts no longer drift the button height — the
301
- `--dock-tab-min-height` token reserves the row regardless of glyph rise. */
302
- .glass-dock[data-density="audacious"] {
303
- --dock-padding-block: var(--dock-density-audacious-padding-block, 0.5rem);
304
- --dock-padding-inline: var(--dock-density-audacious-padding-inline, 0.875rem);
305
- --dock-control-size: var(--dock-density-audacious-control-size, 4rem);
306
- --dock-layer-height: var(--dock-density-audacious-layer-height, 4rem);
307
- --dock-layer-gap: var(--dock-density-audacious-gap, 0.875rem);
308
- --dock-trigger-padding-block: var(
309
- --dock-density-audacious-trigger-padding-block,
310
- 0.625rem
311
- );
312
- --dock-trigger-padding-inline: var(
313
- --dock-density-audacious-trigger-padding-inline,
314
- 1rem
315
- );
316
- --dock-tab-padding-block: var(
317
- --dock-density-audacious-tab-padding-block,
318
- 0.5rem
319
- );
320
- --dock-tab-padding-inline: var(
321
- --dock-density-audacious-tab-padding-inline,
322
- 1.5rem
323
- );
324
- --dock-tab-min-height: var(--dock-density-audacious-tab-min-height, 3.5rem);
325
- --dock-tab-h: var(--dock-density-audacious-tab-min-height, 3.5rem);
326
- }
327
-
328
- /* Density-audacious narrow-box carve (AV.W16 TW3) — when the dock's OWN
329
- box is narrow the audacious-dock label glyphs compress so the chrome
330
- doesn't overflow its host (≤14px below 30rem, 15px 30–45rem). Keyed off
331
- the dock's `dock` inline-size container — now OPT-IN via the
332
- `containerName="dock"` prop (AW.W1 removed the always-on default that
333
- broke shrink-to-fit), so it fires only for a dock the consumer has
334
- width-CONSTRAINED into a host (the 320px sidebar case) where the
335
- compression is meaningful and the containment does not collapse the box.
336
- A free-floating dock shrink-wraps (box == content) so there is nothing to
337
- compress. The var
338
- is set on the `.dock-label` descendant (which reads it via
339
- `font-size: var(--dock-label-size)`, typography.css). Thresholds keep
340
- the prior 479/719px viewport cadence as box widths (30rem ≈ 480px,
341
- 45rem ≈ 720px at the 16px root). */
342
- @container dock (max-width: 29.9375rem) {
343
- .glass-dock[data-density="audacious"] .dock-label {
344
- --dock-label-size: 14px;
345
- }
346
- }
347
- @container dock (min-width: 30rem) and (max-width: 44.9375rem) {
348
- .glass-dock[data-density="audacious"] .dock-label {
349
- --dock-label-size: 15px;
350
- }
351
- }
352
-
353
- .glass-dock::after {
354
- content: "";
355
- position: absolute;
356
- inset: 0;
357
- border-radius: inherit;
358
- background: var(--paper-clean-texture);
359
- background-repeat: repeat;
360
- background-size: var(--paper-texture-size);
361
- opacity: var(--glass-grain-opacity);
362
- pointer-events: none;
363
- mix-blend-mode: overlay;
364
- }
365
-
366
- .dark .glass-dock::after {
367
- mix-blend-mode: soft-light;
368
- }
369
-
370
- /* AX.W01 — the root chrome morphs off the SINGLE `--dock-morph-t` scalar, NOT a
371
- CSS transition (the prior `padding/scale/radius var(--dock-motion-resize)` +
372
- `background/border-color var(--dock-motion-standard)` transition list — TWO
373
- curves on TWO durations — is DELETED). `--dock-expand-t` is the directional
374
- expanded-ness (0 = collapsed look, 1 = expanded look) derived from the
375
- per-swap morph scalar + the synchronously-set `.expanded`/`.collapsed` class:
376
- the box width AND the root padding/radius/color move on ONE clock. The
377
- per-frame writes drive the registered `<number>` scalar; the `calc()` reads
378
- below interpolate composited. Under PRM the spring's `respectReducedMotion`
379
- settles instantly (the scalar jumps 0→1 in one frame → the chrome snaps). */
380
-
381
- /* `--dock-expand-t` — the directional expanded-ness. `--dock-morph-t` is a
382
- REGISTERED `@property` (initial-value 0), so it is NEVER guaranteed-invalid:
383
- `var(--dock-morph-t, 1)` reads 0 at REST (the fallback is DEAD), which would
384
- resolve every expanded dock to expand-t 0 → the collapsed resting look (the
385
- pill-radius + collapsed bg/border/padding regression). So the resting value is
386
- stated STATICALLY per class, and ONLY while a morph is in flight
387
- (`[data-morphing]` armed on the root by useLayerTransition, cleared on settle)
388
- does expand-t track the live spring scalar. The class is ALREADY the END state
389
- during the morph, so the read resolves the correct in-flight blend (collapse
390
- 1→0 via `1 - t`; expand 0→1 via `t`); on settle `[data-morphing]` clears and
391
- expand-t snaps to the exact class endpoint. */
392
- .glass-dock:not(.vertical).expanded,
393
- .glass-dock:not(.vertical).always-expanded {
394
- --dock-expand-t: 1;
395
- }
396
-
397
- .glass-dock:not(.vertical).collapsed {
398
- --dock-expand-t: 0;
399
- }
400
-
401
- .glass-dock:not(.vertical).expanded[data-morphing],
402
- .glass-dock:not(.vertical).always-expanded[data-morphing] {
403
- --dock-expand-t: var(--dock-morph-t);
404
- }
405
-
406
- .glass-dock:not(.vertical).collapsed[data-morphing] {
407
- --dock-expand-t: calc(1 - var(--dock-morph-t));
408
- }
409
-
410
- /* The morphing root chrome — background / border-color interpolate between the
411
- collapsed and expanded endpoints on the ONE `--dock-expand-t` scalar. Scoped
412
- to the horizontal `variant-dock` (the rail / instrument-strip variants own
413
- their own surface vocabulary and do not collapse↔expand). The two color axes
414
- blend in srgb (the house mix the token ladder speaks). The `.collapsed`
415
- discrete bg/border declarations are RETIRED — this block is the single
416
- authority, and at rest `--dock-expand-t` resolves to the class endpoint (0
417
- collapsed / 1 expanded) so the resting look is exact. */
418
- .glass-dock.variant-dock:not(.vertical) {
419
- background: color-mix(
420
- in srgb,
421
- var(--glass-bg-dock, var(--glass-bg-resting))
422
- calc(var(--dock-expand-t, 1) * 100%),
423
- var(--glass-bg-wash)
424
- );
425
- border-color: color-mix(
426
- in srgb,
427
- var(--glass-border-dock, var(--glass-border-resting))
428
- calc(var(--dock-expand-t, 1) * 100%),
429
- var(--glass-border-floating)
430
- );
431
- }
432
-
433
- /* The padding morph is split out + scoped `:not(.dock-overflow-wrap)` so the
434
- wrap variant's own padding rules (W04 territory — `.dock-overflow-wrap` +
435
- its `@media` snap-back) are NOT clobbered by this higher-specificity longhand.
436
- Padding interpolates linearly between the collapsed floor and the expanded
437
- block/inline padding on the same `--dock-expand-t` scalar. */
438
- .glass-dock.variant-dock:not(.vertical):not(.dock-overflow-wrap) {
439
- --dock-pad-collapsed: var(
440
- --dock-collapsed-padding,
441
- var(--dock-padding-block, 0.375rem)
442
- );
443
- padding-block: calc(
444
- var(--dock-pad-collapsed) +
445
- (var(--dock-padding-block, 0.375rem) - var(--dock-pad-collapsed)) *
446
- var(--dock-expand-t, 1)
447
- );
448
- padding-inline: calc(
449
- var(--dock-pad-collapsed) +
450
- (var(--dock-padding-inline, 0.5rem) - var(--dock-pad-collapsed)) *
451
- var(--dock-expand-t, 1)
452
- );
453
- }
454
-
455
- /* Vertical rails grow-to-fit then clamp at `--dock-max-block-size` (the
456
- AX.W04 F6 contract). At rest a capped column scrolls its OWN block axis
457
- internally (the `.vertical.always-expanded:not([data-morphing])` port above)
458
- so a consumer setting `--dock-max-block-size` gets a viewport-anchored
459
- scrollable tool palette without hand-anchoring it. `overflow="wrap"` is
460
- horizontal-only (the class is not emitted for a vertical orientation); a
461
- vertical column's natural overflow story is this cap + internal scroll. No
462
- edge-fade mask — the scroll is scrollbar-hidden, not feathered. */
463
- .glass-dock.vertical {
464
- display: inline-flex;
465
- flex-direction: column;
466
- align-items: stretch;
467
- justify-content: flex-start;
468
- gap: 0.25rem;
469
- white-space: normal;
470
- width: auto;
471
- height: auto;
472
- max-block-size: var(--dock-max-block-size);
473
- /* overflow on the block (morph) axis is the data-morphing-gated clip
474
- shell above; the cross axis stays visible there. */
475
- padding: var(--dock-vertical-padding, var(--dock-padding-block, 0.375rem));
476
- border-radius: var(--radius-dock);
477
- /* AX.W01 — a vertical rail is ALWAYS-EXPANDED (no collapse↔expand morph),
478
- so its root padding/background/border-color never morph; the prior
479
- `padding/background/border-color var(--dock-motion-*)` transition arms
480
- are DELETED (a residual root CSS-transition on the morph-chrome props is
481
- the second-clock smell the single-scalar rebuild retires). The block-axis
482
- (height) morph is owned by the inner `.dock-layer-stack` / DockLayerGroup
483
- via the `--dock-morph-t` scalar (the size `calc()` above), never a CSS
484
- transition here. KEPT: the DECORATIVE hover-scale + press-squish (the
485
- individual `scale`/`transform`) and the box-shadow fade — these are
486
- interaction polish on the static rail, NOT the morph. */
487
- transition:
488
- box-shadow var(--dock-motion-standard),
489
- transform var(--dock-motion-resize),
490
- scale var(--dock-motion-resize);
491
- }
492
-
493
- .glass-dock.variant-rail {
494
- padding: var(--dock-rail-padding, var(--dock-padding-inline, 0.5rem));
495
- background: var(--glass-bg-dock, var(--glass-bg-wash));
496
- border-color: var(--glass-border-dock, var(--glass-border-wash));
497
- }
498
-
499
- .glass-dock.variant-rail.shape-pill {
500
- border-radius: var(--radius-pill);
501
- }
502
-
503
- .glass-dock.variant-rail.shape-rounded {
504
- border-radius: var(--radius-xl);
505
- }
506
-
507
- /* AW.W3b — horizontal-root shape rules. The `shape-*` class previously bound
508
- ONLY under `.variant-rail`; on a horizontal `variant-dock` the prop silently
509
- no-op'd (the root hardwired `--radius-dock` = `--radius-pill`). These rules
510
- close that documented-but-dead prop — `shape="rounded"` / `shape="card"` now
511
- paint on a horizontal dock. Scoped to `variant-dock:not(.vertical)` so the
512
- rail + instrument-strip variants (which own their own radius) are untouched. */
513
- .glass-dock.variant-dock:not(.vertical).shape-rounded {
514
- border-radius: var(--radius-xl);
515
- }
516
-
517
- /* The big-dock CARD shell — a FINITE concentric radius above 2xl, below pill.
518
- It does NOT collapse to a stadium. Collapsed it returns to a pill (the morph
519
- target); the pill↔card radius now MORPHS on the SAME `--dock-expand-t` scalar
520
- the box + chrome ride (ONE clock, no discrete snap) — `--radius-pill` at
521
- expand-t 0 (collapsed), `--dock-card-radius` at expand-t 1 (expanded). The
522
- prior two discrete `.shape-card` / `.shape-card.collapsed` rules + their
523
- reliance on the deleted `border-radius var(--dock-motion-resize)` transition
524
- are RETIRED for this single interpolation. */
525
- .glass-dock.variant-dock:not(.vertical).shape-card {
526
- border-radius: calc(
527
- var(--radius-pill) +
528
- (var(--dock-card-radius) - var(--radius-pill)) *
529
- var(--dock-expand-t, 1)
530
- );
531
- }
532
-
533
- /* The squircle reads markedly better at the large card radius — an
534
- `@supports`-gated ENHANCEMENT only (Chrome 139+, no FF/Safari 2026). The
535
- border-radius arc is the CONTRACT; the squircle is the better tier (the
536
- baseline arc is the acceptable contract, NOT a degraded fallback). */
537
- @supports (corner-shape: squircle) {
538
- .glass-dock.variant-dock:not(.vertical).shape-card {
539
- corner-shape: squircle;
540
- }
541
- }
542
-
543
- /* AJ-W1-δ — `instrument-strip` variant (per G-AJ-D7 Path 1).
544
- *
545
- * The speedtest survey cockpit's vertical rail needs chassis-strip
546
- * aesthetics, not the dock-rail's stadium-pill chrome. Adopts the
547
- * surface vocabulary of the `<InstrumentChassis>` family so the
548
- * strip reads as a journey-station slot — the same surface the
549
- * speedtest + complete + thankyou stations ride.
550
- *
551
- * The variant intentionally inherits the rail variant's vertical
552
- * layout (orientation: vertical; flex-direction: column) — the
553
- * orientation + fitContent computeds in GlassDock.vue branch on
554
- * `variant === "instrument-strip"` alongside the existing rail
555
- * branch. Only the surface vocabulary changes vs the rail:
556
- * - Border-radius drops from `--radius-pill` to `--radius-card`
557
- * (matches `<InstrumentChassis> { border-radius: var(--radius-card) }`).
558
- * - Background plate routes through `--glass-bg-chassis` (0.28α
559
- * per tokens.css §V) instead of `--glass-bg-wash`.
560
- * - Border routes through `--glass-border-quiet` to match the
561
- * chassis primitive's at-rest stroke.
562
- * - Box-shadow routes through `--glass-shadow-quiet` plus the
563
- * inset-bottom hairline matching the chassis primitive
564
- * (instrument-chassis.css:42-45).
565
- *
566
- * The `.shape-pill` / `.shape-rounded` class hooks are still emitted
567
- * by the template but no rule binds them under
568
- * `.variant-instrument-strip` — chassis-strip carries ONE canonical
569
- * border-radius (`--radius-card`), per the chassis family. */
570
- .glass-dock.variant-instrument-strip {
571
- padding: var(
572
- --dock-instrument-strip-padding,
573
- var(--dock-padding-inline, 0.75rem)
574
- );
575
- background: var(--glass-bg-chassis, var(--glass-bg-wash));
576
- border: 1px solid var(--glass-border-quiet, var(--glass-border-resting));
577
- border-radius: var(--radius-card);
578
- box-shadow:
579
- var(--glass-shadow-quiet, var(--shadow-dock)),
580
- inset 0 -0.5px 0 0 rgb(0 0 0 / 0.06);
581
- }
582
-
583
- /* Engraved-bezel inner stroke — matches `<InstrumentChassis>::before`
584
- * (instrument-chassis.css:85-93). The chassis-strip silhouette without
585
- * this stroke reads as a flat plate; with it the strip reads as the
586
- * polished sleeve of an instrument-panel module. The `::before`
587
- * z-index sits above the chassis backdrop but below the slotted
588
- * content (the MetricBadge stack), matching the chassis primitive's
589
- * z-order. The host `.glass-dock` is already `position: relative`. */
590
- .glass-dock.variant-instrument-strip::before {
591
- content: "";
592
- position: absolute;
593
- inset: 0.5px;
594
- border-radius: calc(var(--radius-card) - 0.5px);
595
- border: 1px solid var(--surface-tint-4);
596
- pointer-events: none;
597
- z-index: 1;
598
- }
599
-
600
- .dark .glass-dock.variant-instrument-strip::before {
601
- border-color: var(--surface-tint-6);
602
- }
603
-
604
- /* AX.W01 — the collapsed padding / background / border-color are RETIRED here
605
- (the `--dock-expand-t` morph block above is the single authority for those
606
- three axes — they interpolate, never snap). `.collapsed` keeps only its
607
- non-morphing concerns: the pointer affordance, the centred summary layout,
608
- and the collapsed shadow (box-shadow crossfades discretely — a subtle,
609
- acceptable swap that a `color-mix` cannot blend across its multi-layer
610
- value). */
611
- .glass-dock.collapsed {
612
- cursor: pointer;
613
- justify-content: center;
614
- box-shadow: var(--shadow-dock-override, var(--shadow-dock-collapsed));
615
- }
616
-
617
- .glass-dock.collapsed .dock-layer--summary {
618
- min-width: var(
619
- --dock-collapsed-summary-min-size,
620
- var(--dock-layer-height, 2.5rem)
621
- );
622
- justify-content: center;
623
- }
624
-
625
- /* AW.W3 — the collapsed-hover scale rides the unified dock spring vocabulary.
626
- The `scale var(--dock-motion-resize)` arm was added to the `.glass-dock`
627
- root transition above, so this hover scale settles on the SAME spring curve
628
- the expand morph uses — a collapsed→hover→expand gesture is one continuous
629
- spring, not a `scale`-ease handed off to the width-spring. The hover scale
630
- and the press squish (`--scale-press-dock`) are now one register on the dock
631
- surface. */
632
- .glass-dock.collapsed:hover {
633
- background: var(--glass-bg-wash);
634
- border-color: var(--glass-border-floating);
635
- box-shadow: var(--shadow-dock-override, var(--shadow-dock));
636
- scale: var(--dock-collapsed-hover-scale);
637
- }
638
-
639
- /* J.W5.C — held-state substrate response.
640
- *
641
- * When a descendant signals `dockKeepOpen` (e.g. an active slider drag),
642
- * `useDockState.isHeld` flips true and the root carries `data-held`.
643
- * The dock background lifts a tier (resting → floating-equivalent) and
644
- * the border quiets to the floating ring; the change tracks pointer
645
- * movement on `--duration-fast` so it reads as a held-cursor cue, not
646
- * a state change. The same flag is consumed by descendants (Slider
647
- * thumb halo) via `useOptionalDockContext().held` for cross-substrate
648
- * coordination (typed-key canonical shape; O.W2 invariant 25).
649
- */
650
- .glass-dock[data-held] {
651
- background: var(--glass-bg-floating, var(--glass-bg-resting));
652
- border-color: var(--glass-border-floating, var(--glass-border-resting));
653
- transition:
654
- background var(--duration-fast) var(--ease-standard),
655
- border-color var(--duration-fast) var(--ease-standard),
656
- box-shadow var(--duration-fast) var(--ease-standard);
657
- }
658
-
659
- /* AQ.W3 §W3.1b — open-descendant surface response. When a dock-hosted
660
- trigger (`[data-state="open"]`) is open, the dock root holds its elevated
661
- read natively (no consumer JS). Idempotent with `[data-held]` above (same
662
- floating paint, different trigger). Decorative; `:has()` Baseline Widely. */
663
- .glass-dock:has([data-state="open"]) {
664
- background: var(--glass-bg-floating, var(--glass-bg-resting));
665
- border-color: var(--glass-border-floating, var(--glass-border-resting));
666
- }
667
-
668
- .glass-dock:where(.fixed) {
669
- z-index: var(--z-dock);
670
- }
671
-
672
- .dock-inline {
673
- margin: 0 auto;
674
- }
675
-
676
- .dock-sticky {
677
- position: sticky;
678
- top: 0;
679
- z-index: var(--z-dock);
680
- margin: 0 auto;
681
- }
682
-
683
- /* J.W3.A — outer collapsed↔expanded pair drives width via FLIP.
684
- `useLayerTransition` measures the natural width before/after the
685
- slot swap and animates `width` between fixed pixel values via
686
- the `--dock-motion-resize` spring; this eliminates the
687
- `width: auto` non-interpolation diagnosed in R1+R6. The two
688
- layers stack on a CSS grid (1/1 area) and crossfade opacity
689
- in step with the FLIP. Spatial motion (width) is automatically
690
- suppressed by the global `prefers-reduced-motion` gate in
691
- `utilities.css` (which strips width from transition-property).
692
- Opacity transitions remain — the 0.1s shortened cadence in PRM
693
- still reads as a snap.
694
-
695
- J.W3.C — when the active layer's natural width exceeds
696
- `--dock-max-inline-size`, the dock root clips and the layer
697
- scrolls horizontally with a mask-fade. `min-width: 0` lets the
698
- grid item shrink under the cap (default min-content is the
699
- child's intrinsic width which would prevent the clamp). */
700
- .dock-layers {
701
- display: grid;
702
- min-width: 0;
703
- }
704
-
705
- /* AX.W01 — the box size morph reads the SAME `--dock-morph-t` scalar as the
706
- root chrome (ONE clock, co-temporal by construction). `useLayerTransition`
707
- measures the from→to size ONCE per swap (the allowed one-time FLIP
708
- measurement) and writes `--dock-morph-from`/`--dock-morph-to` px on the
709
- morphing element; the live scalar interpolates between them. The size lands
710
- on the morph AXIS — `inline-size` for a horizontal `.dock-layers` (always
711
- inline) + a horizontal inner stack, `block-size` for a vertical inner stack.
712
- Gated on the root `[data-morphing]`, so at REST the intrinsic / `width:100%`
713
- rules below hold (the morph vars are cleared on settle). Replaces the prior
714
- `transition: width var(--dock-motion-resize)` inline-FLIP — there is NO CSS
715
- transition on the size axis; the spring writes the scalar, CSS reads it. The
716
- active pane is REVEALED through the clip aperture as the box grows (the W2
717
- clip-reveal contract). */
718
- .glass-dock[data-morphing] .dock-layers,
719
- .glass-dock[data-morphing] .dock-layer-stack {
720
- --dock-morph-size: calc(
721
- var(--dock-morph-from, 0px) +
722
- (var(--dock-morph-to, 0px) - var(--dock-morph-from, 0px)) *
723
- var(--dock-morph-t, 0)
724
- );
725
- inline-size: var(--dock-morph-size);
726
- }
727
-
728
- /* A vertical inner group morphs the BLOCK axis — the same span lands on
729
- block-size, and the inline axis stays intrinsic. */
730
- .glass-dock[data-morphing] .dock-layer-group.vertical .dock-layer-stack {
731
- inline-size: auto;
732
- block-size: var(--dock-morph-size);
733
- }
734
-
735
- /* The inner DockLayerGroup stack has no collapsed/expanded class, so its
736
- stagger directional scalar is the per-swap progress directly: `--dock-morph-t`
737
- reads onto an inheriting `--dock-expand-t` on the morphing stack so the new
738
- pane's children cascade in as it reveals. The geometry scalar
739
- (`--dock-morph-t`) stays registered + non-inheriting on the root; this is the
740
- small-subtree inheriting alias the stagger reads (the inheritance-bomb guard
741
- only concerns the `:root`-wide write, which never happens). */
742
- .glass-dock[data-morphing] .dock-layer-stack {
743
- --dock-expand-t: var(--dock-morph-t, 1);
744
- }
745
-
746
- /* AV.W9.0 — the AU.W8b native container-morph arm (the `@supports` block that
747
- opted the subtree into auto-to-length width interpolation and declared the
748
- active layer's destination width as intrinsic) is RETIRED. It was never made
749
- mutually exclusive with the `SpringProgress` FLIP driver in
750
- `useLayerTransition.ts`: on Chrome 129+ the native arm interpolated
751
- `.dock-layers` width on the browser's own clock WHILE the spring wrote inline
752
- `width` per frame — a dual-driver race over the SAME property that cancelled
753
- the morph and froze the dock at runtime (born-RED under
754
- `proof:dock-animation-live`). One authority per concern: the size morph now
755
- has exactly ONE driver on every engine — the `SpringProgress` FLIP path above
756
- (`:382`), or the orthogonal View-Transitions path inside
757
- `startViewTransition` (which owns size + crossfade as a single browser-side
758
- mutation and never touches inline width). Neither coexists with a CSS-native
759
- intrinsic-width destination, so the race is gone. This re-expresses the
760
- `e8380d7` "one driver owns the morph" invariant on the modern
761
- FLIP+spring(+VT) architecture. */
762
-
763
- /* Horizontal dock content grows visibly when the active layer's natural
764
- width exceeds the content area (grow-to-fit; consumers needing wrap opt
765
- into `overflow="wrap"`). No edge-fade mask — content never clips or scrolls,
766
- so a feather has nothing to feather and only bleeds a stray ramp onto
767
- the edge controls. */
768
- .glass-dock.expanded:not(.dock-overflow-wrap) > .dock-layers {
769
- overflow-x: visible;
770
- }
771
-
772
- /* ── Layer crossfade + hit-test contract (ONE vocabulary) ──────────────
773
- AX.W02 — the outer `.dock-layer` (GlassDock's built-in collapsed↔expanded
774
- pair) and the inner `.dock-layer-item-host` (a DockLayer inside a
775
- DockLayerGroup) now share ONE active-state vocabulary: `.is-active` /
776
- `.is-leaving`. The prior `.layer-active` / `.is-active` / `.is-leaving`
777
- TRIPLE collapsed to the pair — both surfaces drive crossfade + hit-test off
778
- the same two state classes, and the morph orchestrator fold (one engine per
779
- dock) means the box, both panes, and every child stagger ride one
780
- `--dock-morph-t` clock. The greppable-sync tombstone that kept the two
781
- never-merged vocabularies in lockstep is DELETED — there is nothing to keep
782
- in sync once the vocabulary is one.
783
-
784
- The 3-state active/inactive/leaving visibility contract here is LOAD-BEARING
785
- (the a11y-006 anchor): the inactive layer leaves the hit-test tree
786
- (`visibility:hidden`), the leaving layer stays painted-but-untouchable
787
- through its fade (`visibility:visible` + `pointer-events:none`), and the
788
- active layer shows IMMEDIATELY (`visibility 0s`, no fade-in delay). One
789
- owner governs each concern — size = the spring scalar, opacity = the CSS
790
- crossfade below, visibility = this 3-state fork.
791
-
792
- An inactive layer must leave the paint AND hit-test tree: `opacity:0` alone
793
- leaves the box answering `elementFromPoint()`, so an inactive layer's button
794
- intercepts clicks meant for the active one. `visibility:hidden` removes it
795
- from both while KEEPING it in layout flow, so the FLIP measurement still
796
- reads its geometry (`display:none` would zero it). The delayed `visibility`
797
- transition holds a leaving layer `visible` for the length of the opacity
798
- crossfade, then flips it `hidden`.
799
-
800
- AU.W2 — the opacity-lockstep fold. The layer fade rides `--dock-motion-resize`
801
- (the SAME duration the container morph runs at) — NOT the faster
802
- `--dock-motion-fast` (0.2s), so the fade and the morph settle in lockstep.
803
- The matched `visibility` hold extends to `--duration-normal` so the leaving
804
- layer stays paintable for the FULL crossfade. */
805
- .dock-layer,
806
- .dock-layer-item-host {
807
- transition:
808
- opacity var(--dock-motion-resize),
809
- visibility 0s linear var(--duration-normal);
810
- }
811
-
812
- .dock-layer:not(.is-active):not(.is-leaving),
813
- .dock-layer-item-host:not(.is-active):not(.is-leaving) {
814
- opacity: 0;
815
- visibility: hidden;
816
- pointer-events: none;
817
- }
818
-
819
- .dock-layer.is-active,
820
- .dock-layer-item-host.is-active {
821
- opacity: 1;
822
- visibility: visible;
823
- pointer-events: auto;
824
- /* AW.W2 — the active pane is STATICALLY opacity:1, REVEALED by the clip
825
- aperture (the box is the reveal mechanism), never faded. There is ZERO
826
- opacity authority on the active pane — the aperture uncovers a layout
827
- that is laid out ONCE at natural size behind it. Only `visibility 0s`
828
- survives (the active layer must paint at once, no fade-in delay — the
829
- AU.W2 governing rule). The leaving pane keeps the shared-token opacity
830
- fade below (the ONLY surviving opacity animation). */
831
- transition: visibility 0s;
832
- }
833
-
834
- /* A leaving layer is inactive for hit-testing but still painted through its
835
- fade-out. The outer collapse↔expand pair has no separate leaving marker in
836
- markup — its `:not(.is-active):not(.is-leaving)` rule above covers the
837
- crossfade-out via the delayed `visibility` transition; the inner pane swap
838
- sets `.is-leaving` explicitly during the crossfade. */
839
- .dock-layer.is-leaving,
840
- .dock-layer-item-host.is-leaving {
841
- opacity: 0;
842
- visibility: visible;
843
- pointer-events: none;
844
- }
845
-
846
- /* AV.W9.1 — the AU.W8b native discrete-visibility arm (the second `@supports`
847
- block that expressed the 3-state contract as one discrete transition over
848
- opacity + visibility with a `@starting-style` entry anchor) is RETIRED. It
849
- was a THIRD opacity/visibility authority overlapping the hand-rolled 3-state
850
- fork above and the spring's settle — three owners for one concern. With the
851
- native size arm gone (W9.0), opacity now has ONE owner everywhere: the
852
- `opacity var(--dock-motion-resize)` crossfade on `.dock-layer{,-item-host}`
853
- (the AU.W2 lockstep token — kept), and visibility has ONE owner: the delayed
854
- `visibility 0s linear var(--duration-normal)` hold of the 3-state fork above
855
- (the a11y-006 anchor, LOAD-BEARING). One authority per concern: size = the
856
- spring scalar, opacity = the CSS crossfade, visibility = the delayed-hold
857
- fork. */
858
-
859
- /* ── Base layout — distinct per surface ─────────────────────────── */
860
- .dock-layer {
861
- display: flex;
862
- align-items: center;
863
- grid-area: 1 / 1;
864
- gap: var(--dock-layer-gap, 0.375rem);
865
- }
866
-
867
- /* AW.W2 — content-intrinsic axis follows the MORPH axis. A horizontal dock
868
- lays its inline content out ONCE at `width: max-content` (nowrap) so the
869
- width aperture uncovers a static row; the fixed `height` keeps the row a
870
- single line. Scoped to `:not(.vertical)` so a vertical rail (whose
871
- block-axis morph reveals a column) is NOT forced onto one nowrap row — the
872
- vertical group's active host block-sizes to `height: max-content` below
873
- (the AP.W3 column treatment, KEPT). */
874
- .glass-dock:not(.vertical) .dock-layer {
875
- white-space: nowrap;
876
- height: var(--dock-layer-height, 2.5rem);
877
- }
878
-
879
- .dock-layer.is-active {
880
- position: relative;
881
- }
882
-
883
- .dock-layer:not(.is-active) {
884
- position: absolute;
885
- inset: 0;
886
- }
887
-
888
- /* AX.W01 — the spring-keyed child stagger, now off the SINGLE morph scalar.
889
- The driver writes `--dock-morph-t` to the `.glass-dock` root and arms
890
- `[data-morphing]` while the spring runs; `--dock-expand-t` (the inheriting
891
- directional expanded-ness derived above — 0 collapsed, 1 expanded) cascades
892
- to the active pane's children. Each direct child reveals when `--dock-expand-t`
893
- crosses its per-child onset, so the controls cascade IN ON the physical morph
894
- — symmetric in both directions (expand fades them in outer→in, collapse fades
895
- them back out) and an interrupted morph carries the cascade with it (no
896
- orphaned timers). The active PANE stays statically opacity:1 (revealed by the
897
- clip aperture); only its CHILDREN ride this per-child onset (the W2
898
- clip-reveal contract is NOT regressed).
899
-
900
- The reveal is a pure `calc` ramp: `opacity = clamp(0, (expand-t − onset) /
901
- window, 1)`. Gated on the root `[data-morphing]` so at REST the children
902
- carry no opacity authority (default cascade → 1). Under PRM the spring's
903
- `respectReducedMotion` jumps `--dock-morph-t` 0→1 in one frame → `--dock-expand-t`
904
- lands at the endpoint → the cascade collapses to 0 stagger frames; the
905
- global PRM gate strips `translate` so the rise snaps too. The inner
906
- DockLayerGroup defines its own `--dock-expand-t` (= the per-swap progress)
907
- in DockLayerGroup's scope so a pane swap reveals the new pane's children. */
908
- .glass-dock[data-morphing] .dock-layer.is-active > *,
909
- .glass-dock[data-morphing] .dock-layer-item-host.is-active > * {
910
- --dock-stagger-window: var(--dock-stagger-window-size, 0.55);
911
- --dock-stagger-onset: 0;
912
- opacity: clamp(
913
- 0,
914
- calc(
915
- (var(--dock-expand-t, 1) - var(--dock-stagger-onset)) /
916
- var(--dock-stagger-window)
917
- ),
918
- 1
919
- );
920
- translate: 0
921
- calc(
922
- (1 - clamp(0, calc((var(--dock-expand-t, 1) - var(--dock-stagger-onset)) / var(--dock-stagger-window)), 1)) *
923
- var(--dock-stagger-rise, 4px)
924
- );
925
- }
926
-
927
- /* AX.W02 — per-child onsets off the ONE `--dock-stagger-step` token. Each
928
- successive control starts `step` later than the one before it (`onset =
929
- step × (childIndex − 1)`), so the row fills outer→in on expand. The
930
- hand-typed 0.08/0.16/0.24/0.32/0.4 ladder is RETIRED — the onsets are now
931
- derived from the single token, so retuning the cascade beat is a one-token
932
- override. The set is capped at the 6th child (`nth-child(n + 6)` holds at
933
- `step × 5`) so even a long row finishes within the morph window. ONE rule
934
- over the unified `.is-active` vocabulary drives both the outer pane and the
935
- inner pane-stack — no duplicated comma-group. (`sibling-index()` would
936
- collapse this to a single declaration, but it is not yet Baseline across
937
- Safari/Firefox — the token-keyed nth-child set is the cross-engine form, and
938
- the magic ladder is gone either way.) */
939
- .glass-dock[data-morphing] .dock-layer.is-active > *:nth-child(2),
940
- .glass-dock[data-morphing] .dock-layer-item-host.is-active > *:nth-child(2) {
941
- --dock-stagger-onset: calc(var(--dock-stagger-step) * 1);
942
- }
943
- .glass-dock[data-morphing] .dock-layer.is-active > *:nth-child(3),
944
- .glass-dock[data-morphing] .dock-layer-item-host.is-active > *:nth-child(3) {
945
- --dock-stagger-onset: calc(var(--dock-stagger-step) * 2);
946
- }
947
- .glass-dock[data-morphing] .dock-layer.is-active > *:nth-child(4),
948
- .glass-dock[data-morphing] .dock-layer-item-host.is-active > *:nth-child(4) {
949
- --dock-stagger-onset: calc(var(--dock-stagger-step) * 3);
950
- }
951
- .glass-dock[data-morphing] .dock-layer.is-active > *:nth-child(5),
952
- .glass-dock[data-morphing] .dock-layer-item-host.is-active > *:nth-child(5) {
953
- --dock-stagger-onset: calc(var(--dock-stagger-step) * 4);
954
- }
955
- .glass-dock[data-morphing] .dock-layer.is-active > *:nth-child(n + 6),
956
- .glass-dock[data-morphing] .dock-layer-item-host.is-active > *:nth-child(n + 6) {
957
- --dock-stagger-onset: calc(var(--dock-stagger-step) * 5);
958
- }
959
-
960
- /* AW.W2 — the at-rest clip-lift moved to `.expanded:not([data-morphing])` /
961
- `.always-expanded:not([data-morphing])` (the clip-shell block at the top).
962
- A plain `overflow: visible` here would lift the clip DURING the morph (the
963
- load-bearing sequencing gap), so the `overflow` declarations are dropped
964
- from these state rules; they keep only their non-overflow concerns.
965
-
966
- AX.W01 — `:not([data-morphing])` gate: at REST expanded the layers fill the
967
- box (`width:100%`), but DURING the morph the FLIP from→to px (the
968
- `--dock-morph-size` calc above) is the SOLE size authority — a `width:100%`
969
- here would win on specificity and snap the box past the spring. The gate
970
- hands the size to the scalar for the morph and back to 100% on settle. */
971
- .glass-dock.expanded:not(.fit-content):not([data-morphing]) .dock-layers {
972
- width: 100%;
973
- }
974
-
975
- .glass-dock.expanded:not(.fit-content) .dock-layer--full {
976
- width: 100%;
977
- }
978
-
979
- .glass-dock.always-expanded {
980
- cursor: default;
981
- }
982
-
983
- /* ── AW.W3b — `layout="grid"` the 2D auto-grid big dock ──────────────
984
- The active layer becomes a self-wrapping tile grid (Launchpad / Stage-Manager
985
- track symmetry — `auto-fill` columns floored at `--dock-tile-min`, explicit
986
- `grid-auto-rows` so the concentric look holds). DISTINCT from
987
- `overflow="wrap"`'s content-ordered flex-wrap. A `layout="grid"` dock is
988
- `alwaysExpanded` by contract (GlassDock.vue), so there is NO width morph and
989
- hence NO per-frame grid-column reflow. `white-space: normal` releases the
990
- single-row nowrap so the tiles wrap. */
991
- .glass-dock.layout-grid {
992
- white-space: normal;
993
- }
994
-
995
- .glass-dock.layout-grid .dock-layer--full {
996
- display: grid;
997
- grid-template-columns: repeat(
998
- auto-fill,
999
- minmax(var(--dock-tile-min, 4.5rem), 1fr)
1000
- );
1001
- grid-auto-rows: var(--dock-tile-min, 4.5rem);
1002
- gap: var(--dock-layer-gap, 0.375rem);
1003
- width: 100%;
1004
- height: auto;
1005
- white-space: normal;
1006
- }
1007
-
1008
- /* A grid dock fills the inline cap; the active layer is the panel body, not a
1009
- shrink-to-fit row. The cap is the `--dock-max-inline-size` clamp (KEPT). */
1010
- .glass-dock.layout-grid .dock-layers {
1011
- width: 100%;
1012
- }
1013
-
1014
- /* CONCENTRIC inner tiles — inner radius = outer card radius − padding (the
1015
- universal macOS / visionOS / Raycast rule). NEVER nest two pills
1016
- (stadium-in-stadium); a finite outer + a stepped-down inner. */
1017
- .glass-dock.layout-grid.shape-card .dock-layer--full > * {
1018
- border-radius: calc(
1019
- var(--dock-card-radius) - var(--dock-padding-inline, 0.5rem)
1020
- );
1021
- }
1022
-
1023
- /* ── Scroll-on-overflow opt-in (GlassDock `overflow="scroll"`) ───────
1024
- The default contract grows-to-fit then overflows visibly past the
1025
- axis cap. `overflow="scroll"` instead makes the dock the scroll port
1026
- on its layout axis, pairing the existing cap with a hidden-scrollbar
1027
- scroll region so over-cap content stays reachable. The rounded pill
1028
- masks the scroll edge; both axes keep their cap. */
1029
-
1030
- /* Inline axis (horizontal docks). The active layer becomes the scroll
1031
- port: `min-width: 0` releases the flex item's `min-content` floor so
1032
- it can shrink under the clamped `.dock-layers` track, then `overflow-x:
1033
- auto` scrolls its over-cap content. `.dock-layers` must KEEP clipping
1034
- so the pill's rounded edge masks the scroll boundary — hence the
1035
- grow-mode `overflow-x: visible` (`.dock-layers`, above) is overridden
1036
- back to `hidden` here. */
1037
- .glass-dock.dock-scroll-x.expanded:not(.dock-overflow-wrap) > .dock-layers {
1038
- overflow-x: hidden;
1039
- }
1040
-
1041
- .glass-dock.dock-scroll-x .dock-layer--full {
1042
- min-width: 0;
1043
- overflow-x: auto;
1044
- scrollbar-width: none;
1045
- /* Trap the inline-scroll momentum at the dock's own boundary so a
1046
- wheel/trackpad gesture that bottoms out the pill does NOT chain into
1047
- scrolling the page behind it (the dock is a self-contained scroll
1048
- port, not a window into the route scroller). */
1049
- overscroll-behavior-x: contain;
1050
- }
1051
-
1052
- .glass-dock.dock-scroll-x .dock-layer--full::-webkit-scrollbar {
1053
- display: none;
1054
- }
1055
-
1056
- /* Block axis (vertical rails). The root keeps its `max-block-size` cap
1057
- and scrolls its over-cap children — cap + scroll is the correct
1058
- pairing the grow-mode contract split apart. Ties the
1059
- `.vertical.always-expanded` visible default on specificity and wins on
1060
- source order. */
1061
- .glass-dock.vertical.dock-scroll-y {
1062
- overflow-y: auto;
1063
- scrollbar-width: none;
1064
- /* Same momentum-trap as the inline port — a rail that bottoms out must
1065
- not chain its remaining wheel delta into the page scroller behind it. */
1066
- overscroll-behavior-y: contain;
1067
- }
1068
-
1069
- .glass-dock.vertical.dock-scroll-y::-webkit-scrollbar {
1070
- display: none;
1071
- }
1072
-
1073
- /* ── `overflow="wrap"` recipe — CONTENT-DRIVEN intrinsic flex reflow ──
1074
- (AX.W04 F0). Parallel to the `.dock-scroll-{x,y}` hooks the
1075
- `overflow="scroll"` member emits, `overflow="wrap"` emits this class.
1076
- (HORIZONTAL-ONLY — GlassDock.vue guards the emit on
1077
- `orientation !== 'vertical'`; a vertical rail grows-to-fit + clamps via
1078
- `--dock-max-block-size`, its own overflow story.)
1079
-
1080
- The reflow is INTRINSIC: `flex-wrap: wrap` is ALWAYS-ON (no viewport MQ),
1081
- and the dock shrink-wraps to content with `max-inline-size` capping it at
1082
- the canonical `--dock-max-inline-size` token (the SAME cap every
1083
- overflow strategy reads — `min(80vw, 64rem)` default, consumer-overridable
1084
- per-instance). So the layer wraps to N rows EXACTLY when its intrinsic row
1085
- exceeds the cap — at ANY viewport width — and collapses to one row when it
1086
- fits. The magic-640 `@media` snap-back + the `--dock-overflow-bp` token are
1087
- DELETED (the inherited mobile-responsive debt, origin 078dd79). */
1088
- .glass-dock.dock-overflow-wrap {
1089
- white-space: normal;
1090
- /* The content-driven cap. The dock ALREADY shrink-wraps to its content's
1091
- intrinsic width (the base `.glass-dock` rule), so `max-inline-size` caps
1092
- that intrinsic width at `--dock-max-inline-size`: under the cap the
1093
- shrink-wrap wins → one row; over the cap the dock holds at the cap and the
1094
- inner `flex-wrap: wrap` row reflows to N rows. ONE token, no breakpoint.
1095
- (The `min(max-content, …)` form is INVALID CSS — the math functions reject
1096
- the `max-content` intrinsic keyword, computing the property to its initial
1097
- `none` and silently dropping the cap; intrinsic-shrink + this plain cap IS
1098
- the valid `min(max-content, cap)`.) */
1099
- max-inline-size: var(--dock-max-inline-size);
1100
- padding: var(--dock-wrap-padding-block, var(--dock-padding-block, 0.375rem))
1101
- var(--dock-wrap-padding-inline, 0.625rem);
1102
-
1103
- /* F4 — the pill↔card radius morph. A wrap dock is a FINITE multi-row CARD
1104
- surface when expanded and a stadium PILL when collapsed — the SAME
1105
- pill↔card morph the big-dock `shape-card` path rides. The corner glides
1106
- on the `--dock-expand-t` W01 single-scalar: `--radius-pill` at expand-t 0
1107
- (collapsed), `--dock-card-radius` at expand-t 1 (expanded — UNIFIED on
1108
- the SAME finite-radius token the big-dock card uses; the bare 16px
1109
- `--radius-2xl` literal is DELETED). ONE register, no discrete snap. */
1110
- border-radius: calc(
1111
- var(--radius-pill) +
1112
- (var(--dock-card-radius) - var(--radius-pill)) *
1113
- var(--dock-expand-t, 1)
1114
- );
1115
-
1116
- /* F1 — the card-tier elevation morph ("material thickens as glass flexes
1117
- larger", WWDC25 §219). The wrap dock lifts from the flat thin-pill
1118
- `--shadow-dock` glow onto the FLOATING-tier stack (the `--shadow-dock-wrap`
1119
- token names the canonical composite on `:root`; a consumer retints the
1120
- whole card shadow by overriding THAT one token per the cartoon-shadow
1121
- contract). The three floating layers — drop / material-rim / under-cast —
1122
- fade in driven by the SAME `--dock-expand-t` scalar the radius glides on:
1123
- `--dock-wrap-elev` is the scalar as a 0→1 percent, so each layer's alpha
1124
- tracks the corner morph in LOCKSTEP (ONE clock, ONE register). At
1125
- expand-t 0 (collapsed/pill) the floating layers are 0%-alpha (invisible)
1126
- → only the base `--shadow-dock` glow shows; at expand-t 1 (expanded/card)
1127
- the full floating stack grounds the multi-row silhouette. The per-layer
1128
- recipe mirrors `--shadow-dock-wrap` (= `--shadow-xl` + `--glass-material-rim`
1129
- + `--glass-under-shadow-vivid`) so the token stays the source-of-record. */
1130
- --dock-wrap-elev: calc(var(--dock-expand-t, 1) * 100%);
1131
- box-shadow:
1132
- var(--glass-edge-light),
1133
- var(--shadow-dock-override, var(--shadow-dock)),
1134
- 0 8px 24px color-mix(in srgb, var(--shadow-color) calc(0.14 * var(--dock-wrap-elev)), transparent),
1135
- 0 0 0 0.5px color-mix(in srgb, var(--shadow-color) calc(0.05 * var(--dock-wrap-elev)), transparent),
1136
- 0 8px 32px -4px color-mix(in srgb, oklch(0 0 0 / 0.12) var(--dock-wrap-elev), transparent);
1137
- }
1138
-
1139
- .glass-dock.dock-overflow-wrap .dock-layer--full {
1140
- flex-wrap: wrap;
1141
- justify-content: center;
1142
- height: auto;
1143
- min-height: var(--dock-wrap-layer-min-height, var(--dock-layer-height, 2rem));
1144
- gap: var(--dock-wrap-row-gap, 0.25rem)
1145
- var(--dock-wrap-column-gap, var(--dock-layer-gap, 0.375rem));
1146
- /* AX.W04 — NO `transition: min-height` JS-morph (the AW.W3b spring-keyed
1147
- min-height detector is RETIRED — the false `proof:dock-layering-polish`
1148
- wrap-reflow claim is STRUCK). The reflow is intrinsic flex: rows
1149
- appear/vanish on the cap crossing with no second clock. The radius +
1150
- shadow lift ride the `--dock-expand-t` scalar (the W01 single clock),
1151
- NOT a CSS transition on the retired `--dock-motion-resize` list. */
1152
- }
1153
-
1154
- .glass-dock.dock-overflow-wrap .dock-layer--summary {
1155
- height: auto;
1156
- min-height: var(--dock-wrap-layer-min-height, var(--dock-layer-height, 2rem));
1157
- }
1158
-
1159
- .glass-dock.dock-overflow-wrap .dock-separator {
1160
- display: none;
1161
- }
1162
-
1163
- /* Collapsed, a wrap dock is the single-row summary PILL. `white-space: nowrap`
1164
- keeps the summary on one line; the inline cap stays `max-inline-size:
1165
- var(--dock-max-inline-size)` (inherited from the `.dock-overflow-wrap` rule
1166
- above) so the collapsed box still shrink-wraps to the small summary's
1167
- max-content (NOT the full cap). The
1168
- radius morph resolves to `--radius-pill` at collapsed (--dock-expand-t 0) and
1169
- the box-shadow to the flat `--shadow-dock` glow (--dock-wrap-elev 0%). */
1170
- .glass-dock.dock-overflow-wrap.collapsed {
1171
- white-space: nowrap;
1172
- }
1173
-
1174
- .dock-separator {
1175
- @apply flex-shrink-0;
1176
- width: 1px;
1177
- height: var(--dock-separator-height);
1178
- margin: 0 0.375rem;
1179
- background: var(--surface-tint-15);
1180
- }
1181
-
1182
- .dock-spacer {
1183
- @apply flex-1;
1184
- }
1185
-
1186
- .dock-layer-group {
1187
- display: flex;
1188
- gap: var(--dock-layer-group-gap, var(--dock-layer-gap, 0.25rem));
1189
- min-width: 0;
1190
- min-height: 0;
1191
- }
1192
-
1193
- .dock-layer-group.horizontal {
1194
- flex-direction: row;
1195
- align-items: stretch;
1196
- }
1197
-
1198
- .dock-layer-group.horizontal.rail-end {
1199
- flex-direction: row-reverse;
1200
- }
1201
-
1202
- .dock-layer-group.vertical {
1203
- flex-direction: column;
1204
- align-items: stretch;
1205
- }
1206
-
1207
- .dock-layer-group.vertical.rail-end {
1208
- flex-direction: column-reverse;
1209
- }
1210
-
1211
- .dock-layer-rail {
1212
- display: flex;
1213
- flex-direction: column;
1214
- flex-shrink: 0;
1215
- gap: var(--dock-layer-rail-gap, 0.125rem);
1216
- padding: var(--dock-layer-rail-padding, 0.125rem);
1217
- border-right: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
1218
- }
1219
-
1220
- .dock-layer-group.vertical .dock-layer-rail {
1221
- flex-direction: row;
1222
- border-right: none;
1223
- border-bottom: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
1224
- }
1225
-
1226
- .dock-layer-group.horizontal.rail-end .dock-layer-rail {
1227
- border-right: none;
1228
- border-left: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
1229
- }
1230
-
1231
- .dock-layer-group.vertical.rail-end .dock-layer-rail {
1232
- border-bottom: none;
1233
- border-top: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
1234
- }
1235
-
1236
- /* AU.W8.4 — the rail is a reka-ui Tabs contract. The travelling
1237
- `.dock-layer-tab-indicator` (the reka TabsIndicator) now carries the
1238
- active affordance; the rail is `position: relative` so the indicator's
1239
- absolute placement is rail-local. */
1240
- .dock-layer-rail {
1241
- position: relative;
1242
- }
1243
-
1244
- .dock-layer-rail .dock-layer-tab {
1245
- position: relative;
1246
- display: inline-flex;
1247
- align-items: center;
1248
- justify-content: center;
1249
- width: var(--dock-layer-tab-size, var(--dock-control-size, 1.75rem));
1250
- height: var(--dock-layer-tab-size, var(--dock-control-size, 1.75rem));
1251
- border: 0;
1252
- border-radius: var(--dock-layer-tab-radius, var(--radius-sm));
1253
- background: transparent;
1254
- color: var(--muted-foreground);
1255
- cursor: pointer;
1256
- font: inherit;
1257
- font-size: 0.75rem;
1258
- /* The tab glyph rides above the travelling indicator backplate. */
1259
- z-index: 1;
1260
- transition:
1261
- background var(--dock-motion-fast),
1262
- color var(--dock-motion-fast);
1263
- }
1264
-
1265
- .dock-layer-rail .dock-layer-tab:hover {
1266
- background: color-mix(in srgb, var(--accent) 40%, transparent);
1267
- color: var(--foreground);
1268
- }
1269
-
1270
- /* AU.W8.4 — KEEP the active glyph tint on the selected tab (reka emits
1271
- `data-state="active"` + `aria-selected="true"`); the per-button active
1272
- BACKGROUND is RETIRED — the travelling indicator carries that affordance. */
1273
- .dock-layer-rail .dock-layer-tab[data-state="active"] {
1274
- color: var(--primary);
1275
- }
1276
-
1277
- /* AU.W8.4 — the dock control focus-ring contract does not cover
1278
- `.dock-layer-tab` (the rail tab is not in the `.dock-icon-button` family).
1279
- The roving tabindex means only the active tab is in the tab order; a
1280
- focus-visible tab paints the shared dock ring. */
1281
- .dock-layer-rail [role="tab"]:focus-visible {
1282
- box-shadow: var(--focus-ring-shadow);
1283
- outline: none;
1284
- }
1285
-
1286
- /* AU.W8.4 — the travelling indicator (reka TabsIndicator). reka computes
1287
- `--reka-tabs-indicator-position` + `--reka-tabs-indicator-size` from the
1288
- active tab's geometry; the indicator morphs between tabs riding
1289
- `--dock-motion-resize` (= `--spring-dock` after AU.W8.2) — NOT a hardcoded
1290
- `--spring-snappy`. It paints the active-tab backplate the per-button
1291
- `.is-active` background used to. */
1292
- .dock-layer-rail .dock-layer-tab-indicator {
1293
- position: absolute;
1294
- left: 0;
1295
- top: 0;
1296
- height: var(--dock-layer-tab-size, var(--dock-control-size, 1.75rem));
1297
- width: var(--reka-tabs-indicator-size);
1298
- transform: translateX(var(--reka-tabs-indicator-position));
1299
- border-radius: var(--dock-layer-tab-radius, var(--radius-sm));
1300
- background: color-mix(in srgb, var(--primary) 15%, transparent);
1301
- transition:
1302
- width var(--dock-motion-resize),
1303
- transform var(--dock-motion-resize);
1304
- pointer-events: none;
1305
- }
1306
-
1307
- /* For a vertical rail the indicator travels along the inline axis still
1308
- (the horizontal-always keyboard decision keeps the indicator inline);
1309
- reka's position var resolves to the active tab offset regardless. */
1310
-
1311
- .dock-layer-stack {
1312
- position: relative;
1313
- display: grid;
1314
- /* AW.W2 — the morph axis (width for a horizontal group, height for a
1315
- vertical one) is driven by the `useLayerTransition` SpringProgress in
1316
- pixel space (FLIP path) or the View-Transition (native path); a CSS
1317
- `width`/`height` transition here would be a SECOND driver on the same
1318
- axis (the AV.W9.0 dual-driver race that froze the dock). The cross axis
1319
- is intrinsic-sized and never morphs. So NO size transition on the stack
1320
- — one owner per axis. */
1321
- }
1322
-
1323
- /* Base layout — the crossfade + hit-test contract is shared with
1324
- `.dock-layer` above (see "Layer crossfade + hit-test contract"). The
1325
- default state is inactive: absolute + inset:0. */
1326
- .dock-layer-item-host {
1327
- grid-area: 1 / 1;
1328
- display: flex;
1329
- align-items: center;
1330
- white-space: nowrap;
1331
- gap: var(--dock-layer-gap, 0.375rem);
1332
- position: absolute;
1333
- inset: 0;
1334
- }
1335
-
1336
- .dock-layer-item-host.is-active {
1337
- position: relative;
1338
- inset: auto;
1339
- width: max-content;
1340
- min-width: 0;
1341
- }
1342
-
1343
- /* AP.W3 — axis-aware layer pane. The stack + `useLayerTransition` are
1344
- axis-aware, but the pane was hardcoded to a no-wrap ROW with
1345
- `width: max-content` — forcing a `vertical` group's content onto one
1346
- horizontal line that could not grow down (the vertical-overflow fight).
1347
- A vertical group stacks in a column, stretches, wraps, and block-sizes
1348
- to the height the stack animates. Horizontal byte-identical. */
1349
- .dock-layer-group.vertical .dock-layer-item-host {
1350
- flex-direction: column;
1351
- align-items: stretch;
1352
- white-space: normal;
1353
- }
1354
-
1355
- .dock-layer-group.vertical .dock-layer-item-host.is-active {
1356
- width: auto;
1357
- height: max-content;
1358
- }
1359
-
1360
- /* AU.W8b.3 — the five dock CONTROL families (.dock-icon-button,
1361
- .dark-mode-toggle-button, .dock-tab-button + tiers, the
1362
- .dock-select-trigger / .dock-dropdown-trigger pair, and the coarse-pointer
1363
- per-button touch floor) carved out to dock-controls.css (imported directly
1364
- after this file in index.css, same @layer components). dock.css keeps the
1365
- shell / density / grain / layer-crossfade contract + the shared
1366
- cross-control four-state comma-groups (the :focus-visible / :disabled groups
1367
- + the motion-token :where() group above). */
1368
-
1369
- /* J.W3.B — `.dock-popover` substrate + `pop-up-*` / `pop-down-*`
1370
- Vue Transition keyframes retired. Hover-driven dock popovers
1371
- compose `<HoverPopover keep-dock-open>`, which inherits reka-ui
1372
- HoverCard's collision-avoidance + the canonical
1373
- `popover-animate` motion vocabulary. Click-anchored popovers
1374
- (no dock-keep semantics) compose `<Popover>` directly. */
1375
-
1376
- /* AX.W04 F0 — the `@media (min-width: 640px)` desktop snap-back + the
1377
- `--dock-overflow-bp` token are DELETED. The prior viewport breakpoint forced
1378
- `flex-wrap: nowrap` + `max-width: none` above 640px, so the dock NEVER
1379
- wrapped on a wide viewport even when its row overflowed (the 800px → 1861px
1380
- viewport-bleed defect). The wrap is now CONTENT-driven: the always-on
1381
- `flex-wrap: wrap` + the `max-inline-size: var(--dock-max-inline-size)` cap
1382
- above (over the base shrink-wrap) reflow to N rows at ANY width on the
1383
- over-cap crossing — no viewport coupling, no breakpoint. */
1384
-
1385
- /* O.W6 Lane D — Touch-target floor (speedtest AC.W6 F2.AA-03 / WCAG
1386
- 2.5.5). On coarse-pointer devices (touch), every dock-control's
1387
- width/height floor lifts to the 44px WCAG target-size minimum.
1388
- The lift composes via `--dock-control-size` (the canonical knob
1389
- every dock-control surface — `.dock-icon-button`,
1390
- `.dock-tab-button`, `.dock-select-trigger`, `.dock-dropdown-trigger`
1391
- — already consumes) so the cascade reaches every control without
1392
- per-selector re-declaration. `--size-icon-btn` lifts in lockstep
1393
- so bare icon-button surfaces outside the dock (e.g.,
1394
- `.dark-mode-toggle-button[data-size="dock"]` and any consumer
1395
- that wires `var(--size-icon-btn)` for trigger sizing) inherit
1396
- the same floor.
1397
-
1398
- AP.W3 R0G-6 — selector is `.glass-dock[data-density]`, not bare
1399
- `.glass-dock`: the always-present density setter (0,2,0) shadowed a
1400
- bare (0,1,0) coarse floor, pinning the touch box at 40px. The
1401
- presence-selector (0,2,0) wins by source order, lifting
1402
- `--dock-control-size` to 44px — read by both the button box and the
1403
- dock width-math, so the slot reserves 44px (no overflow). Fine pointer
1404
- byte-identical. Proof: design/W1.2-motion-carve-and-dock.md §B.2. */
1405
- @media (pointer: coarse) {
1406
- .glass-dock[data-density] {
1407
- --dock-control-size: var(--dock-touch-target, 2.75rem);
1408
- --size-icon-btn: var(--dock-touch-target, 2.75rem);
1409
- }
1410
-
1411
- /* S-2 — the in-dock floor above lifts the control-SIZE token (shell /
1412
- width-math), which only reaches a DockIconButton that has a
1413
- `.glass-dock` ancestor. The companion button-level floor (the
1414
- `.dock-icon-button:not(--compact)` `min-{block,inline}-size` that
1415
- reaches a STANDALONE DockIconButton with no dock wrapper) moved to
1416
- dock-controls.css with the rest of the control family (AU.W8b.3). */
1417
- }
1418
111
  }