@mkbabb/glass-ui 2.0.0 → 3.0.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 (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -0
  3. package/dist/{CommandShortcut-_INFUMu6.js → CommandShortcut-BNDzfFnB.js} +2 -2
  4. package/dist/{ContextMenuSubContent-DCkweFW9.js → ContextMenuSubContent-DLEyeqbh.js} +3 -3
  5. package/dist/{DialogContent-CmCijgX9.js → DialogContent-DSo7PKlU.js} +1 -1
  6. package/dist/{DialogFooter-DRdaCok0.js → DialogFooter-D5KY6sCX.js} +1 -1
  7. package/dist/{Notification-DrI1DT2v.js → Notification-D97JO0fK.js} +2 -2
  8. package/dist/{SelectScrollDownButton-yu8EYUnu.js → SelectScrollDownButton-BwTexKeY.js} +2 -2
  9. package/dist/{Toaster-DY8_jtHv.js → Toaster-CY8gJu9E.js} +69 -58
  10. package/dist/animated-digit.js +1 -1
  11. package/dist/aurora.js +100 -87
  12. package/dist/carousel.js +4 -4
  13. package/dist/{check-dwgetki8.js → check-Nuw7H9Yh.js} +1 -1
  14. package/dist/{chevron-down-DILQA1t6.js → chevron-down-Du2b9vY_.js} +1 -1
  15. package/dist/{chevron-right-fS7fal2t.js → chevron-right-CtDxpE3w.js} +1 -1
  16. package/dist/{chevron-up-BtYjYQOS.js → chevron-up-CenYokvI.js} +1 -1
  17. package/dist/command.js +1 -1
  18. package/dist/components/custom/aurora/composables/runtime.d.ts +24 -1
  19. package/dist/components/custom/sortable-list/SortableHandle.vue.d.ts +1 -1
  20. package/dist/components/custom/toggle-chip/ToggleChip.vue.d.ts +6 -4
  21. package/dist/components/custom/typewriter/TypewriterText.vue.d.ts +1 -1
  22. package/dist/components/ui/drawer/Drawer.vue.d.ts +25 -2
  23. package/dist/components/ui/drawer/DrawerContent.vue.d.ts +12 -1
  24. package/dist/components/ui/drawer/index.d.ts +9 -0
  25. package/dist/components/ui/toast/Toaster.vue.d.ts +7 -1
  26. package/dist/components/ui/toast/index.d.ts +1 -0
  27. package/dist/components/ui/toast/use-toast.d.ts +14 -1
  28. package/dist/composables/dark/index.d.ts +1 -0
  29. package/dist/composables/dom/index.d.ts +1 -0
  30. package/dist/composables/dom/useIdleReady.d.ts +63 -0
  31. package/dist/composables/index.d.ts +1 -0
  32. package/dist/composables/motion/core/index.d.ts +6 -0
  33. package/dist/composables/motion/index.d.ts +0 -7
  34. package/dist/configurator.js +1 -1
  35. package/dist/confirm-dialog.js +1 -1
  36. package/dist/constants-D-8FN28s.js +13 -0
  37. package/dist/context-menu.js +1 -1
  38. package/dist/{createLucideIcon-Bn9a1b70.js → createLucideIcon-rHu18UQW.js} +2 -2
  39. package/dist/dark.d.ts +1 -1
  40. package/dist/dark.js +12 -1
  41. package/dist/dialog.js +2 -2
  42. package/dist/dock.js +1 -1
  43. package/dist/dom.js +2 -2
  44. package/dist/{dropdown-menu-2K-SGkZU.js → dropdown-menu-gHSkffW7.js} +2 -2
  45. package/dist/dropdown-menu.js +1 -1
  46. package/dist/expandable-container.js +2 -2
  47. package/dist/forms.js +2 -2
  48. package/dist/glass-ui.css +1 -1
  49. package/dist/glass-ui.js +139 -115
  50. package/dist/icon-tooltip.js +1 -1
  51. package/dist/keyboard.js +1 -1
  52. package/dist/labeled-field.js +3 -3
  53. package/dist/{minimize-2-LsCJ_eNt.js → minimize-2-C_oyKVwZ.js} +1 -1
  54. package/dist/motion-core.d.ts +1 -0
  55. package/dist/motion-core.js +192 -0
  56. package/dist/motion.js +25 -227
  57. package/dist/notification.js +1 -1
  58. package/dist/responsive-tabs.js +1 -1
  59. package/dist/{search-ocd8tmL9.js → search-7XEx_6hq.js} +1 -1
  60. package/dist/search.js +4 -4
  61. package/dist/select.js +1 -1
  62. package/dist/{sheet-CLVkb3AO.js → sheet-BsBdO5jq.js} +1 -1
  63. package/dist/sheet.js +1 -1
  64. package/dist/sortable-list.js +6 -3
  65. package/dist/status-dot.js +16 -13
  66. package/dist/styles/dock.css +72 -95
  67. package/dist/styles/drawer.css +138 -0
  68. package/dist/styles/index.css +18 -2
  69. package/dist/styles/instrument-chassis.css +28 -1
  70. package/dist/styles/theme.css +3 -0
  71. package/dist/styles/tokens.css +109 -266
  72. package/dist/styles/typography.css +44 -131
  73. package/dist/styles/utilities.css +23 -58
  74. package/dist/toast.js +1 -1
  75. package/dist/{useAnimatedNumber-DcvTR9B4.js → useAnimatedNumber-2l13GibX.js} +9 -20
  76. package/dist/{useConfiguratorState-BlaevW0S.js → useConfiguratorState-BpZi8QJu.js} +5 -5
  77. package/dist/{useBreakpoint-BHlX-MhR.js → useIdleReady-DlzJicQH.js} +29 -1
  78. package/dist/{x-cdWAmO-q.js → x-Cb3NE2Ne.js} +1 -1
  79. package/package.json +12 -5
  80. package/src/styles/dock.css +72 -95
  81. package/src/styles/drawer.css +138 -0
  82. package/src/styles/index.css +14 -2
  83. package/src/styles/instrument-chassis.css +28 -1
  84. package/src/styles/theme.css +3 -0
  85. package/src/styles/tokens.css +109 -266
  86. package/src/styles/typography.css +44 -131
  87. package/src/styles/utilities.css +23 -58
  88. package/dist/composables/motion/useSpringOrchestrator.d.ts +0 -15
  89. /package/dist/{IconTooltip-ge_mBSWR.js → IconTooltip-GIeWdo64.js} +0 -0
  90. /package/dist/{Input-CbakTe3B.js → Input-CBvqW8kZ.js} +0 -0
  91. /package/dist/composables/{motion → dark}/installDarkModeSync.d.ts +0 -0
  92. /package/dist/{useKeyboardShortcuts-B1ev1YEC.js → useKeyboardShortcuts-CPO4AhLx.js} +0 -0
@@ -22,6 +22,29 @@
22
22
  --dock-motion-resize: var(--duration-normal) var(--spring-snappy);
23
23
  }
24
24
 
25
+ /* Shared four-state contract — the focus-visible ring + disabled paint are
26
+ identical across every dock control, so they are expressed ONCE here as a
27
+ comma group over the control family rather than copied per rule-set. The
28
+ group keeps full class+pseudo specificity (NOT `:where()`, which would
29
+ zero the class and let a per-control :hover box-shadow override the focus
30
+ ring). Per-control transition extensions (the box-shadow fade on
31
+ focus-visible) stay with their own selectors below where they differ. */
32
+ .dock-icon-button:focus-visible,
33
+ .dock-tab-button:focus-visible,
34
+ .dock-select-trigger:focus-visible,
35
+ .dock-dropdown-trigger:focus-visible {
36
+ box-shadow: var(--focus-ring-shadow);
37
+ outline: none;
38
+ }
39
+
40
+ .dock-icon-button:disabled,
41
+ .dock-tab-button:disabled,
42
+ .dock-select-trigger:disabled,
43
+ .dock-dropdown-trigger:disabled {
44
+ opacity: var(--opacity-disabled);
45
+ cursor: not-allowed;
46
+ }
47
+
25
48
  .glass-dock {
26
49
  --dock-surface-blur: var(--glass-blur-dock, var(--glass-blur-wash));
27
50
  /* J.W3.C — vertical-rail max-block + horizontal max-inline caps
@@ -68,6 +91,7 @@
68
91
  --dock-trigger-padding-inline: var(--dock-density-compact-trigger-padding-inline, 0.4375rem);
69
92
  --dock-tab-padding-block: var(--dock-density-compact-tab-padding-block, 0.3125rem);
70
93
  --dock-tab-padding-inline: var(--dock-density-compact-tab-padding-inline, 0.625rem);
94
+ --dock-tab-h: 32px;
71
95
  }
72
96
 
73
97
  /* Comfortable density — the GlassDock default. Sits between compact
@@ -82,6 +106,7 @@
82
106
  --dock-trigger-padding-inline: var(--dock-density-comfortable-trigger-padding-inline, 0.5rem);
83
107
  --dock-tab-padding-block: var(--dock-density-comfortable-tab-padding-block, 0.4375rem);
84
108
  --dock-tab-padding-inline: var(--dock-density-comfortable-tab-padding-inline, 0.75rem);
109
+ --dock-tab-h: 38px;
85
110
  }
86
111
 
87
112
  .glass-dock[data-density="spacious"] {
@@ -94,6 +119,7 @@
94
119
  --dock-trigger-padding-inline: var(--dock-density-spacious-trigger-padding-inline, 0.625rem);
95
120
  --dock-tab-padding-block: var(--dock-density-spacious-tab-padding-block, 0.5rem);
96
121
  --dock-tab-padding-inline: var(--dock-density-spacious-tab-padding-inline, 0.875rem);
122
+ --dock-tab-h: 44px;
97
123
  }
98
124
 
99
125
  /* Audacious density — typography-forward chrome where the dock reads as
@@ -111,41 +137,13 @@
111
137
  --dock-tab-padding-block: var(--dock-density-audacious-tab-padding-block, 0.5rem);
112
138
  --dock-tab-padding-inline: var(--dock-density-audacious-tab-padding-inline, 1.5rem);
113
139
  --dock-tab-min-height: var(--dock-density-audacious-tab-min-height, 3.5rem);
140
+ --dock-tab-h: var(--dock-density-audacious-tab-min-height, 3.5rem);
114
141
  }
115
142
 
116
- /* Dock tab-button density-keyed height (R3-spec / audit-E P0-3;
117
- consolidated into dock.css at Q.W3 Lane A formerly split into
118
- utilities.css and surviving only by import-cascade accident).
119
- The four bare DockIconButton consumers solve their density-keyed
120
- height via `--dock-control-size` (set per density tier above);
121
- DockTabButton's analogous `--dock-tab-h` knob lives in the same
122
- density rungs so the tab-button row no longer free-floats to the
123
- glyph + padding sum. Compact density lands at 32px to match the
124
- deployed product's bottom-dock height. The `.dock-tab-button` rule
125
- (further down this file) consumes `--dock-tab-h`. */
126
- .glass-dock[data-density="compact"] {
127
- --dock-tab-h-compact: 32px;
128
- --dock-tab-h: var(--dock-tab-h-compact);
129
- }
130
- .glass-dock[data-density="comfortable"] {
131
- --dock-tab-h-comfortable: 38px;
132
- --dock-tab-h: var(--dock-tab-h-comfortable);
133
- }
134
- .glass-dock[data-density="spacious"] {
135
- --dock-tab-h-spacious: 44px;
136
- --dock-tab-h: var(--dock-tab-h-spacious);
137
- }
138
- .glass-dock[data-density="audacious"] {
139
- --dock-tab-h-audacious: var(--dock-density-audacious-tab-min-height, 3.5rem);
140
- --dock-tab-h: var(--dock-tab-h-audacious);
141
- }
142
-
143
- /* Density-audacious mobile carve (R3-spec / audit-D D-Rec-5;
144
- consolidated into dock.css at Q.W3 Lane A). At narrow viewports
145
- the audacious-dock label glyphs compress so the audacious chrome
146
- doesn't overflow its host: `≤14px at <480` and `15px at 480–719`.
147
- Consumer dock label spans bind `font-size: var(--dock-label-size)`
148
- (typography.css `.dock-label` recipe is the canonical consumer). */
143
+ /* Density-audacious mobile carve at narrow viewports the audacious-dock
144
+ label glyphs compress so the chrome doesn't overflow its host
145
+ (≤14px at <480, 15px at 480–719). Consumer dock label spans bind
146
+ `font-size: var(--dock-label-size)` (typography.css `.dock-label`). */
149
147
  @media (max-width: 479px) {
150
148
  .glass-dock[data-density="audacious"] {
151
149
  --dock-label-size: 14px;
@@ -188,22 +186,11 @@
188
186
  border-color var(--dock-motion-standard);
189
187
  }
190
188
 
191
- /* Z.W2.T2 — vertical rails grow-to-fit + clamp, no scroll. Per A2 §B6
192
- + A4 special-focus: canon-intent is grow-and-wrap, not scroll. The
193
- prior `overflow-y: auto` produced the "dock scrolls when it
194
- shouldn't" regression at narrow viewports; the right escape is to
195
- let descendants wrap to multiple rows (consumers opt-in via
196
- `dock-wrap`) or to clamp via `--dock-max-block-size` and let the
197
- cap fail visibly rather than mask the overflow as a scroll
198
- affordance.
199
-
200
- The edge-fade `mask-image` retired here in lockstep with the
201
- horizontal `.dock-layers` rule. With grow-to-fit + visible
202
- overflow the rail never scrolls, so a scroll-feather mask has
203
- nothing to feather — it only bled a transparent ramp onto the
204
- top and bottom rail items, casting a stray directional shadow
205
- on those edge controls. J.W3.C's auto + mask-fade shape is fully
206
- superseded: grow-to-fit is the whole contract, no feather. */
189
+ /* Vertical rails grow-to-fit + clamp, no scroll: descendants wrap to
190
+ multiple rows (opt-in via `dock-wrap`) or clamp via
191
+ `--dock-max-block-size`; the cap fails visibly rather than masking
192
+ overflow as a scroll affordance. No edge-fade mask a rail that never
193
+ scrolls has nothing to feather. */
207
194
  .glass-dock.vertical {
208
195
  display: inline-flex;
209
196
  flex-direction: column;
@@ -382,23 +369,11 @@
382
369
  transition: width var(--dock-motion-resize);
383
370
  }
384
371
 
385
- /* Z.W2.T2 — horizontal dock content grows visibly when the active
386
- layer's natural width exceeds the dock's content area. Per
387
- A2 §B6 + A4 special-focus: the canon-intent for the bottom dock
388
- is grow-to-fit; consumers needing wrap opt into `dock-wrap`. The
389
- prior `overflow-x: auto` produced the user-reported "dock scrolls
390
- when it shouldn't" — the wrong escape valve.
391
-
392
- The edge-fade `mask-image` retired here. Once Z.W2.T2 made the
393
- dock grow-to-fit, content is never clipped and never scrolls, so
394
- the mask had nothing left to feather — it was kept only "for
395
- visual symmetry" with the vertical rule. In practice the 1rem
396
- transparent ramp at each edge bled onto the first and last dock
397
- items: the rightmost control (e.g. a settings cog) had its outer
398
- edge faded into the glass backdrop, which read as a stray
399
- directional shadow on that item. A scroll-affordance feather on
400
- a surface that never scrolls is pure cosmetic damage, so it is
401
- removed at the root rather than masked per-consumer. */
372
+ /* Horizontal dock content grows visibly when the active layer's natural
373
+ width exceeds the content area (grow-to-fit; consumers needing wrap opt
374
+ into `dock-wrap`). No edge-fade mask content never clips or scrolls,
375
+ so a feather has nothing to feather and only bleeds a stray ramp onto
376
+ the edge controls. */
402
377
  .glass-dock.expanded:not(.dock-wrap) > .dock-layers {
403
378
  overflow-x: visible;
404
379
  }
@@ -650,6 +625,23 @@
650
625
  min-width: 0;
651
626
  }
652
627
 
628
+ /* AP.W3 — axis-aware layer pane. The stack + `useLayerTransition` are
629
+ axis-aware, but the pane was hardcoded to a no-wrap ROW with
630
+ `width: max-content` — forcing a `vertical` group's content onto one
631
+ horizontal line that could not grow down (the vertical-overflow fight).
632
+ A vertical group stacks in a column, stretches, wraps, and block-sizes
633
+ to the height the stack animates. Horizontal byte-identical. */
634
+ .dock-layer-group.vertical .dock-layer-item-host {
635
+ flex-direction: column;
636
+ align-items: stretch;
637
+ white-space: normal;
638
+ }
639
+
640
+ .dock-layer-group.vertical .dock-layer-item-host.is-active {
641
+ width: auto;
642
+ height: max-content;
643
+ }
644
+
653
645
  .dock-icon-button {
654
646
  display: inline-flex;
655
647
  align-items: center;
@@ -693,9 +685,9 @@
693
685
  outline: none;
694
686
  }
695
687
 
688
+ /* focus-visible ring + disabled paint hoisted to the shared `:where()`
689
+ group at the top; this rule adds only the box-shadow fade transition. */
696
690
  .dock-icon-button:focus-visible {
697
- box-shadow: var(--focus-ring-shadow);
698
- outline: none;
699
691
  transition:
700
692
  background-color var(--dock-motion-fast),
701
693
  color var(--dock-motion-fast),
@@ -704,11 +696,6 @@
704
696
  box-shadow var(--dock-motion-fast);
705
697
  }
706
698
 
707
- .dock-icon-button:disabled {
708
- opacity: var(--opacity-disabled);
709
- cursor: not-allowed;
710
- }
711
-
712
699
  /* O.W6 Lane B — token-ladder active paint. Defaults match the prior
713
700
  hardcoded recipe (--muted bg + --foreground color, no transform / border
714
701
  / shadow). Consumers override the active variant via the
@@ -841,15 +828,8 @@
841
828
  box-shadow: none;
842
829
  }
843
830
 
844
- .dock-tab-button:focus-visible {
845
- box-shadow: var(--focus-ring-shadow);
846
- outline: none;
847
- }
848
-
849
- .dock-tab-button:disabled {
850
- opacity: var(--opacity-disabled);
851
- cursor: not-allowed;
852
- }
831
+ /* focus-visible ring + disabled paint hoisted to the shared `:where()`
832
+ group at the top. */
853
833
 
854
834
  .dock-tab-button:is(.is-active, .active, [aria-current="page"], [aria-pressed="true"]) {
855
835
  background: var(--surface-tint-10);
@@ -998,10 +978,10 @@
998
978
  outline: none;
999
979
  }
1000
980
 
981
+ /* focus-visible ring + disabled paint hoisted to the shared `:where()`
982
+ group at the top; this rule adds only the box-shadow fade transition. */
1001
983
  .dock-select-trigger:focus-visible,
1002
984
  .dock-dropdown-trigger:focus-visible {
1003
- box-shadow: var(--focus-ring-shadow);
1004
- outline: none;
1005
985
  transition:
1006
986
  background-color var(--dock-motion-fast),
1007
987
  color var(--dock-motion-fast),
@@ -1010,12 +990,6 @@
1010
990
  box-shadow var(--dock-motion-fast);
1011
991
  }
1012
992
 
1013
- .dock-select-trigger:disabled,
1014
- .dock-dropdown-trigger:disabled {
1015
- opacity: var(--opacity-disabled);
1016
- cursor: not-allowed;
1017
- }
1018
-
1019
993
  .dock-select-trigger:is(.is-active, .active, [aria-expanded="true"], [aria-pressed="true"]),
1020
994
  .dock-dropdown-trigger:is(.is-active, .active, [aria-expanded="true"], [aria-pressed="true"]) {
1021
995
  background: var(--muted);
@@ -1072,12 +1046,15 @@
1072
1046
  that wires `var(--size-icon-btn)` for trigger sizing) inherit
1073
1047
  the same floor.
1074
1048
 
1075
- Density overrides (`[data-density="compact"]` etc.) still win
1076
- inside `@layer components` ordering consumers that explicitly
1077
- opt into compact density on touch devices get their compact size
1078
- back. The media-query only lifts the *default* path. */
1049
+ AP.W3 R0G-6 — selector is `.glass-dock[data-density]`, not bare
1050
+ `.glass-dock`: the always-present density setter (0,2,0) shadowed a
1051
+ bare (0,1,0) coarse floor, pinning the touch box at 40px. The
1052
+ presence-selector (0,2,0) wins by source order, lifting
1053
+ `--dock-control-size` to 44px — read by both the button box and the
1054
+ dock width-math, so the slot reserves 44px (no overflow). Fine pointer
1055
+ byte-identical. Proof: design/W1.2-motion-carve-and-dock.md §B.2. */
1079
1056
  @media (pointer: coarse) {
1080
- .glass-dock {
1057
+ .glass-dock[data-density] {
1081
1058
  --dock-control-size: var(--dock-touch-target, 2.75rem);
1082
1059
  --size-icon-btn: var(--dock-touch-target, 2.75rem);
1083
1060
  }
@@ -0,0 +1,138 @@
1
+ /*
2
+ * drawer.css — Detented bottom-sheet grammar (AN.W3).
3
+ *
4
+ * vaul-vue owns the snap math (the spring transform between snap-points, the
5
+ * drag-resistance overshoot, the `data-vaul-*` state attributes). glass-ui owns
6
+ * the LOOK: the glass sheet surface, the rounded peek handle, the snap-stop
7
+ * indicator rules, and the drag-resistance feel-tuning. This file is the
8
+ * substrate so a consumer mounting `<Drawer mode="live-behind">` does not
9
+ * re-author the three detents from scratch.
10
+ *
11
+ * Token-first — every visual axis reads a custom property. Consumers retune
12
+ * the sheet by overriding the rungs below, never by editing this file.
13
+ */
14
+
15
+ :root {
16
+ /* Detent grammar knobs — overridable per-consumer */
17
+ --drawer-handle-w: 2.25rem;
18
+ --drawer-handle-h: 0.3125rem; /* 5px — vaul's hit-target rhythm */
19
+ --drawer-handle-color: var(--muted-foreground);
20
+ --drawer-handle-opacity: 0.45;
21
+ --drawer-handle-opacity-active: 0.85;
22
+ --drawer-snap-rule-color: color-mix(in srgb, var(--border) 60%, transparent);
23
+ }
24
+
25
+ /*
26
+ * The sheet surface. Replaces the prior inline-Tailwind triplet on
27
+ * DrawerContent.vue so the glass tier + the snap transform feel live in one
28
+ * token-driven place. The fixed/inset/z-index layout stays here too.
29
+ *
30
+ * NOTE: vaul-vue sets `transition: transform .5s cubic-bezier(.32,.72,0,1)` and
31
+ * `touch-action: none` on `[data-vaul-drawer]` from its injected stylesheet —
32
+ * that IS the drag-resistance spring curve. We do NOT override `transition` so
33
+ * the snap feel is preserved; we layer border-radius + glass surface on top.
34
+ */
35
+ .glass-drawer {
36
+ position: fixed;
37
+ inset-inline: 0;
38
+ bottom: 0;
39
+ z-index: var(--z-modal);
40
+ display: flex;
41
+ flex-direction: column;
42
+ height: auto;
43
+ max-height: 97vh;
44
+ margin-top: 6rem;
45
+ border: 1px solid var(--border);
46
+ border-bottom: 0;
47
+ border-start-start-radius: var(--radius-panel);
48
+ border-start-end-radius: var(--radius-panel);
49
+ background-color: var(--background);
50
+ box-shadow: var(--shadow-2xl);
51
+ }
52
+
53
+ /*
54
+ * Detented (snap-point) sheets fill the viewport height. vaul positions the
55
+ * sheet by translating it DOWN by `innerHeight - (fraction * innerHeight)` —
56
+ * so for a snap fraction to read as that fraction OF THE VIEWPORT, the sheet's
57
+ * top must sit at the viewport top when fully open (offset 0). A `height: auto`
58
+ * content sheet would instead translate off-screen. vaul tags the snap case via
59
+ * `[data-vaul-snap-points=true]`; we fill height there and let the content
60
+ * scroll inside.
61
+ */
62
+ .glass-drawer[data-vaul-snap-points="true"] {
63
+ height: 100%;
64
+ max-height: 100%;
65
+ margin-top: 0;
66
+ }
67
+
68
+ /* Live-behind direction variants — vaul tags the root with the drag axis.
69
+ Bottom is the default + the F-side pattern; the others ride the same surface
70
+ so a top/side live-behind sheet inherits the grammar. */
71
+ .glass-drawer[data-vaul-drawer-direction="top"] {
72
+ bottom: auto;
73
+ top: 0;
74
+ margin-top: 0;
75
+ margin-bottom: 6rem;
76
+ border-top: 0;
77
+ border-bottom: 1px solid var(--border);
78
+ border-radius: 0 0 var(--radius-panel) var(--radius-panel);
79
+ }
80
+
81
+ /*
82
+ * Peek handle — the rounded grip at the top of the sheet. It is the visual
83
+ * affordance the user drags to cycle peek → half → full. vaul-vue intensifies
84
+ * the grip on `:active` via its own `[data-vaul-handle]` rules when the consumer
85
+ * uses `<DrawerHandle>`; this is the glass-ui default grip for the common case.
86
+ */
87
+ .glass-drawer-handle {
88
+ flex-shrink: 0;
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+ padding-block: 0.75rem 0.5rem;
93
+ touch-action: none;
94
+ }
95
+
96
+ .glass-drawer-grip {
97
+ display: block;
98
+ width: var(--drawer-handle-w);
99
+ height: var(--drawer-handle-h);
100
+ border-radius: var(--radius-pill);
101
+ background-color: var(--drawer-handle-color);
102
+ opacity: var(--drawer-handle-opacity);
103
+ transition: opacity var(--duration-fast) var(--ease-out),
104
+ width var(--duration-fast) var(--ease-out);
105
+ }
106
+
107
+ /* While the sheet is being dragged vaul exposes the gesture; intensify the grip
108
+ so the affordance reads as "live". `:active` covers the pointer-held case for
109
+ the default grip; consumers using vaul's `<DrawerHandle>` get its own active
110
+ rule in addition. */
111
+ .glass-drawer-handle:active .glass-drawer-grip,
112
+ .glass-drawer:hover .glass-drawer-grip {
113
+ opacity: var(--drawer-handle-opacity-active);
114
+ width: calc(var(--drawer-handle-w) * 1.15);
115
+ }
116
+
117
+ /*
118
+ * Snap-stop indicators — subtle full-width hairline rules that mark where the
119
+ * sheet content sections break across detents. A consumer opts in by adding
120
+ * `.glass-drawer-snap-rule` to a separator element inside the sheet; it reads as
121
+ * the detent boundary line without competing with the content. This is the
122
+ * "snap-stop visual indicator" the spec asks for, expressed as an opt-in class
123
+ * (the detents themselves are positional, owned by vaul; the RULE is the look).
124
+ */
125
+ .glass-drawer-snap-rule {
126
+ height: 1px;
127
+ margin-inline: calc(var(--radius-panel) * -0.5);
128
+ border: 0;
129
+ background-color: var(--drawer-snap-rule-color);
130
+ }
131
+
132
+ /* Reduced-motion — vaul's transform transition is its own; we only suppress the
133
+ grip's affordance animation so the handle does not pulse for PRM users. */
134
+ @media (prefers-reduced-motion: reduce) {
135
+ .glass-drawer-grip {
136
+ transition: none;
137
+ }
138
+ }
@@ -4,9 +4,18 @@
4
4
  * Import this in your project's main CSS file:
5
5
  * @import "tailwindcss";
6
6
  * @import "tw-animate-css";
7
- * @import "@mkbabb/glass-ui/styles"; (critical no font payload)
7
+ * @import "@mkbabb/glass-ui/styles"; (token cascade + SFC scoped CSS)
8
8
  * @import "@mkbabb/glass-ui/styles/fonts"; (OFL woff2 corpus, loaded once)
9
9
  * (then add your project-specific token overrides locally)
10
+ *
11
+ * AN.W1 — the single `@mkbabb/glass-ui/styles` import resolves the COMPLETE
12
+ * stylesheet: this @import cascade PLUS the per-component `<style scoped>`
13
+ * payload (Aurora grid layering, Progress/Slider/Notification/… scoped rules).
14
+ * The build folds the SFC bundle (`dist/glass-ui.css`) into the dist copy of
15
+ * this file (vite.config.ts `publishStyleAssets`), so a consumer never needs
16
+ * a second `@import "@mkbabb/glass-ui/styles.css"` line. The `./styles.css`
17
+ * export stays reachable as a transparent SFC-only entry (NOT a back-compat
18
+ * alias) for a cascade-free consumer.
10
19
  */
11
20
 
12
21
  /*
@@ -69,8 +78,10 @@
69
78
  * 14. glyph-face.css — P-tranche component CSS (cap + backplate).
70
79
  * 15. disco-glyph.css — P-tranche component CSS (layered fills).
71
80
  * 16. hover-popover.css — V.W3 popover-animation grammar.
81
+ * 17. drawer.css — AN.W3 detented bottom-sheet grammar (glass
82
+ * sheet surface + peek handle + snap-stop rules).
72
83
  *
73
- * Per-package component CSS lives at (12)-(16) (loaded after utilities so
84
+ * Per-package component CSS lives at (12)-(17) (loaded after utilities so
74
85
  * component-local recipes can override). New per-package CSS files append
75
86
  * to that tail.
76
87
  */
@@ -90,7 +101,12 @@
90
101
  @import "./glyph-face.css";
91
102
  @import "./disco-glyph.css";
92
103
  @import "./hover-popover.css";
104
+ @import "./drawer.css";
93
105
 
94
106
  /* Ensure consumer's Tailwind scans glass-ui components for utility classes
95
107
  (e.g. CVA button variants like text-destructive-foreground) */
108
+ /* AN.W1 — SFC scoped component CSS (folded so a single
109
+ @import "@mkbabb/glass-ui/styles" carries cascade + components) */
110
+ @import "../glass-ui.css";
111
+
96
112
  @source "../components";
@@ -292,7 +292,34 @@
292
292
  @media (max-width: 720px) {
293
293
  .instrument-chassis .instrument-dial {
294
294
  grid-template-columns: 1fr;
295
- grid-template-rows: auto auto auto;
295
+ /* R0G-2 (AO.W3) reserve the dial's FINAL box from frame 0. The
296
+ desktop axis is already CLS-clean (the 3-column `align-items:
297
+ center` grid reserves row height from intrinsic min + the AP-Pγ
298
+ transform-only idle recentre). The mobile reflow previously
299
+ collapsed to `grid-template-rows: auto auto auto`, which reserves
300
+ NO height — the meter row, divider, and readout column sit at
301
+ zero height until the consumer's meter <canvas> + readout
302
+ numbers hydrate, then grow collapsed → final and push everything
303
+ below down ~326-331px → mobile-390 CLS 0.32-0.38.
304
+
305
+ The fix pins the dial box from frame 0: the meter row carries the
306
+ dominant reserve (`minmax(0, 1fr)` — it expands to fill the
307
+ envelope the `min-height` establishes), divider + readout stay
308
+ intrinsic (`auto`). The load-bearing reserve is `min-height` —
309
+ it fixes the dial's outer box so the rows distribute WITHIN a
310
+ reserved envelope rather than growing it post-hydration.
311
+
312
+ Both knobs are CSS custom properties (token-first). The
313
+ `--instrument-dial-min-height-mobile` canonical rung lands in
314
+ tokens.css at AO.W4; until then the inline `24rem` fallback IS
315
+ the default and carries the contract. A consumer whose meter is
316
+ a different size retunes the token — a tuning knob, not a
317
+ workaround for a missing reserve. */
318
+ grid-template-rows:
319
+ var(--instrument-dial-meter-reserve-mobile, minmax(0, 1fr))
320
+ auto
321
+ auto;
322
+ min-height: var(--instrument-dial-min-height-mobile, 24rem);
296
323
  gap: var(--instrument-dial-gap-mobile, 1.5rem);
297
324
  }
298
325
 
@@ -81,6 +81,9 @@
81
81
  --color-card: var(--card);
82
82
  --color-card-foreground: var(--card-foreground);
83
83
 
84
+ /* R0G-5 — bg-surface-public-data-panel utility. */
85
+ --color-surface-public-data-panel: var(--surface-public-data-panel);
86
+
84
87
  --color-shadow: var(--shadow);
85
88
 
86
89
  /* Semantic foregrounds — bg-{success,warning,info}-foreground +