@passui/react 0.1.45 → 0.1.47

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 (141) hide show
  1. package/README.md +57 -46
  2. package/dist/types/src/components/accordion/accordion.d.ts +1 -1
  3. package/dist/types/src/components/breadcrumb/breadcrumb.d.ts +1 -1
  4. package/dist/types/src/components/inline-disclosure/inline-disclosure.d.ts +1 -1
  5. package/dist/types/src/components/popover/anchored-surface.d.ts +5 -1
  6. package/dist/types/src/components/popover/index.d.ts +41 -0
  7. package/dist/types/src/components/popover/popover-content.d.ts +38 -0
  8. package/dist/types/src/components/popover/popover-root.d.ts +22 -0
  9. package/dist/types/src/components/popover/popover-trigger.d.ts +23 -0
  10. package/dist/types/src/components/stepper/rolling-digit.d.ts +1 -1
  11. package/dist/types/src/components/table/table.d.ts +1 -1
  12. package/dist/types/src/components/view-controller/view-controller.d.ts +2 -2
  13. package/dist/types/src/index.d.ts +1 -1
  14. package/dist/types/src/machines/popover.machine.d.ts +30 -0
  15. package/package.json +307 -307
  16. package/src/appearance/index.ts +2 -2
  17. package/src/appearance/platform.tsx +76 -60
  18. package/src/appearance/theme.tsx +409 -377
  19. package/src/components/accordion/accordion.tsx +58 -49
  20. package/src/components/ambient/ambient.tsx +25 -25
  21. package/src/components/ambient/index.ts +1 -1
  22. package/src/components/aspect-ratio/aspect-ratio.tsx +21 -16
  23. package/src/components/breadcrumb/breadcrumb.tsx +63 -57
  24. package/src/components/button/button-styles.ts +39 -32
  25. package/src/components/button/button.tsx +91 -84
  26. package/src/components/button/icon-button.tsx +27 -25
  27. package/src/components/button/pending-spinner.tsx +8 -8
  28. package/src/components/button/use-pending-indicator.ts +11 -11
  29. package/src/components/chip/chip.tsx +35 -30
  30. package/src/components/code-block/code-block.tsx +40 -39
  31. package/src/components/combo-box/combo-box.tsx +5 -5
  32. package/src/components/command/command.tsx +124 -94
  33. package/src/components/compact-date-picker/calendar-month.ts +75 -53
  34. package/src/components/compact-date-picker/compact-date-picker.tsx +308 -289
  35. package/src/components/compact-time-picker/compact-time-picker.tsx +288 -281
  36. package/src/components/compact-time-picker/time-value.ts +32 -29
  37. package/src/components/content-unavailable-view/content-unavailable-view.tsx +64 -48
  38. package/src/components/content-view/content-view.tsx +168 -159
  39. package/src/components/content-view/index.ts +1 -1
  40. package/src/components/context-menu/conceal-menu-source.ts +25 -25
  41. package/src/components/context-menu/context-menu-host.tsx +439 -402
  42. package/src/components/context-menu/context-menu-store.ts +125 -123
  43. package/src/components/context-menu/menu-navigation.ts +25 -18
  44. package/src/components/context-menu/menu-position.ts +33 -29
  45. package/src/components/context-menu/menu-reveal.ts +32 -32
  46. package/src/components/context-menu/use-context-menu.ts +63 -56
  47. package/src/components/conversation/conversation.tsx +265 -263
  48. package/src/components/conversation/index.ts +1 -1
  49. package/src/components/date-picker/date-picker.tsx +95 -94
  50. package/src/components/date-picker/date-value.ts +20 -10
  51. package/src/components/dialog/dialog-host.tsx +105 -103
  52. package/src/components/dialog/dialog-store.ts +37 -36
  53. package/src/components/dialog/dialog.tsx +192 -179
  54. package/src/components/dialog/index.ts +1 -1
  55. package/src/components/disclosure-group/disclosure-group.tsx +93 -86
  56. package/src/components/divider/divider.tsx +24 -22
  57. package/src/components/inline-disclosure/inline-disclosure.tsx +40 -36
  58. package/src/components/keyboard-shortcut/keyboard-shortcut.tsx +23 -19
  59. package/src/components/labeled-content/labeled-content.tsx +68 -68
  60. package/src/components/locale-switcher/index.ts +1 -1
  61. package/src/components/locale-switcher/locale-switcher.tsx +278 -251
  62. package/src/components/menu/menu.tsx +75 -58
  63. package/src/components/morph/index.ts +2 -2
  64. package/src/components/morph/morph.tsx +227 -201
  65. package/src/components/morph/use-morph-disclosure.ts +19 -19
  66. package/src/components/one-time-code/one-time-code.tsx +216 -200
  67. package/src/components/picker/picker-trigger.ts +16 -13
  68. package/src/components/picker/picker.tsx +371 -345
  69. package/src/components/popover/anchored-surface.tsx +253 -229
  70. package/src/components/popover/index.ts +50 -0
  71. package/src/components/popover/popover-content.tsx +374 -0
  72. package/src/components/popover/popover-root.tsx +97 -0
  73. package/src/components/popover/popover-trigger.tsx +71 -0
  74. package/src/components/profile-image/profile-image.tsx +80 -64
  75. package/src/components/progress-view/progress-view.tsx +61 -59
  76. package/src/components/progress-view/use-liquid-progress.ts +38 -38
  77. package/src/components/radio-group/radio-group.tsx +71 -68
  78. package/src/components/search-field/expanding-search-field.tsx +191 -177
  79. package/src/components/search-field/search-field.tsx +59 -50
  80. package/src/components/search-list/search-list.tsx +246 -236
  81. package/src/components/section/section.tsx +58 -58
  82. package/src/components/secure-field/secure-field.tsx +3 -3
  83. package/src/components/segmented-control/segmented-control.tsx +136 -136
  84. package/src/components/sheet/modal-environment.ts +71 -64
  85. package/src/components/sheet/sheet-portal.tsx +58 -53
  86. package/src/components/sheet/sheet.tsx +146 -141
  87. package/src/components/sheet/use-sheet-focus.ts +97 -82
  88. package/src/components/sidebar/sidebar.tsx +78 -78
  89. package/src/components/skeleton/skeleton.tsx +30 -21
  90. package/src/components/slider/slider.tsx +113 -104
  91. package/src/components/slosh-slider/liquid-step.ts +32 -31
  92. package/src/components/slosh-slider/slosh-slider.tsx +265 -228
  93. package/src/components/stat-tile/index.ts +1 -1
  94. package/src/components/stat-tile/stat-tile.tsx +79 -68
  95. package/src/components/status-indicator/status-indicator.tsx +40 -37
  96. package/src/components/stepper/rolling-digit.tsx +82 -68
  97. package/src/components/stepper/stepper.tsx +122 -113
  98. package/src/components/swatch-picker/swatch-picker.tsx +120 -120
  99. package/src/components/tab-bar/tab-bar.tsx +149 -148
  100. package/src/components/tab-view/tab-view.tsx +140 -137
  101. package/src/components/table/table.tsx +117 -98
  102. package/src/components/text-editor/text-editor.tsx +34 -29
  103. package/src/components/text-field/text-field.tsx +182 -180
  104. package/src/components/toast/toast-store.ts +127 -115
  105. package/src/components/toast/toaster.tsx +361 -347
  106. package/src/components/toggle/toggle.tsx +297 -282
  107. package/src/components/tooltip/tooltip-host.tsx +157 -150
  108. package/src/components/view-controller/index.ts +1 -1
  109. package/src/components/view-controller/view-controller.tsx +64 -53
  110. package/src/components/wake-slider/wake-slider.tsx +220 -205
  111. package/src/hooks/scroll/minimize-decision.ts +11 -7
  112. package/src/hooks/use-anchored-disclosure.ts +19 -19
  113. package/src/hooks/use-anchored-placement.ts +29 -25
  114. package/src/hooks/use-dismiss.ts +33 -29
  115. package/src/hooks/use-floating-dismiss.ts +24 -22
  116. package/src/hooks/use-media-query.ts +67 -60
  117. package/src/hooks/use-minimize-on-scroll.ts +69 -60
  118. package/src/hooks/use-visual-viewport.ts +63 -63
  119. package/src/hooks/viewport/types.ts +16 -16
  120. package/src/hooks/viewport/viewport-metrics.ts +16 -10
  121. package/src/icons/icons.tsx +440 -328
  122. package/src/index.ts +91 -91
  123. package/src/lib/anchored-placement.ts +70 -65
  124. package/src/lib/class-names.ts +14 -13
  125. package/src/lib/clipboard.ts +8 -7
  126. package/src/lib/date.ts +61 -60
  127. package/src/lib/floating-layers.ts +39 -24
  128. package/src/lib/haptics.ts +10 -10
  129. package/src/lib/language.ts +9 -9
  130. package/src/lib/range-value.ts +27 -21
  131. package/src/lib/screen-edges.ts +33 -30
  132. package/src/lib/shapes.ts +38 -34
  133. package/src/machines/popover.machine.ts +51 -0
  134. package/src/motion/transitions.ts +46 -43
  135. package/src/styles/code-block.css +69 -43
  136. package/src/styles/motion-controls.css +393 -191
  137. package/src/tokens.ts +1 -1
  138. package/dist/types/src/components/popover/popover-fit.d.ts +0 -21
  139. package/dist/types/src/components/popover/popover.d.ts +0 -21
  140. package/src/components/popover/popover-fit.ts +0 -69
  141. package/src/components/popover/popover.tsx +0 -334
@@ -3,202 +3,404 @@
3
3
  background; pane-edge to separator; font-ui to font-sans. Color mixes
4
4
  replace rgba RGB triples, so no Bencho variables or new globals leak. */
5
5
  @layer components {
6
- .passui-search-pill { transition: scale 90ms ease; }
7
- .passui-search-pill:active { scale: 0.98; }
8
- /* The outline stays whole; its legend opens a real gap under the label.
6
+ .passui-search-pill {
7
+ transition: scale 90ms ease;
8
+ }
9
+ .passui-search-pill:active {
10
+ scale: 0.98;
11
+ }
12
+ /* The outline stays whole; its legend opens a real gap under the label.
9
13
  No painted background patch needs to match the field's surroundings. */
10
- .passui-field-outline {
11
- position: absolute;
12
- inset: calc(var(--text-footnote) / -2) 0 0;
13
- min-width: 0;
14
- margin: 0;
15
- padding: 0 0.5rem;
16
- border: 1px solid var(--color-separator);
17
- border-radius: inherit;
18
- pointer-events: none;
19
- transition: border-color 200ms ease;
20
- }
21
- .passui-field-outline legend {
22
- max-width: 0;
23
- overflow: hidden;
24
- padding: 0;
25
- font-size: var(--text-footnote);
26
- line-height: 1;
27
- height: 1em;
28
- white-space: nowrap;
29
- visibility: hidden;
30
- transition: max-width 320ms ease, padding 320ms ease;
31
- }
32
- .passui-floating-label {
33
- position: absolute;
34
- inset-inline-start: 0.75rem;
35
- top: 50%;
36
- z-index: 1;
37
- max-width: calc(100% - 1.5rem);
38
- overflow: hidden;
39
- text-overflow: ellipsis;
40
- white-space: nowrap;
41
- color: var(--color-label-secondary);
42
- font-size: var(--text-body);
43
- line-height: 1;
44
- transform: translateY(-50%);
45
- transform-origin: left center;
46
- transition: top 320ms ease, transform 320ms ease, color 200ms ease;
47
- pointer-events: none;
48
- }
49
- .passui-floating-label:dir(rtl) { transform-origin: right center; }
50
- .passui-floating-label-with-icon { inset-inline-start: 2.5rem; }
51
- .passui-floating-field input::placeholder { opacity: 0; }
52
- .passui-floating-field:focus-within input::placeholder { opacity: 1; }
53
- .passui-floating-field:focus-within .passui-field-outline { border-color: var(--color-label); }
54
- .passui-floating-field:has(input[aria-invalid="true"]) .passui-field-outline { border-color: var(--color-danger); }
55
- .passui-floating-field:is(:focus-within, :has(input:not(:placeholder-shown))) .passui-floating-label {
56
- top: 0;
57
- inset-inline-start: 0.75rem;
58
- transform: translateY(-50%) scale(0.78);
59
- color: var(--color-label);
60
- }
61
- .passui-floating-field:is(:focus-within, :has(input:not(:placeholder-shown))) legend {
62
- max-width: 100%;
63
- padding-inline: 0.25rem;
64
- }
65
- .passui-one-time-code { width: 100%; max-width: calc(var(--otp-length) * 2.5rem + (var(--otp-length) - 1) * 0.5rem); }
66
- .passui-otp-row { position: relative; height: 3rem; }
67
- .passui-otp-materials, .passui-otp-digits { display: flex; position: absolute; inset: 0; gap: 0.5rem; pointer-events: none; }
68
- .passui-otp-cell, .passui-otp-digit { flex: 1; min-width: 0; border-radius: var(--radius-xl); }
69
- .passui-otp-cell {
70
- background: var(--color-fill);
71
- transition: transform 560ms cubic-bezier(0.22, 1, 0.36, 1), background-color 200ms;
72
- }
73
- .passui-otp-digit { display: grid; place-items: center; font-size: var(--text-title3); color: var(--color-label); font-variant-numeric: tabular-nums; }
74
- .passui-otp-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: text; }
75
- .passui-one-time-code:focus-within .passui-otp-digit[data-active] { box-shadow: inset 0 0 0 2px var(--color-accent-text); }
76
- .passui-otp-glyph { animation: passui-otp-in 200ms ease-out both; }
77
- @keyframes passui-otp-in { from { transform: translateY(45%) scale(0.86); opacity: 0; filter: blur(3px); } }
78
- .passui-otp-capsule { position: absolute; inset-block: 0; left: 50%; width: 60%; border-radius: var(--radius-full); background: var(--color-accent); transform: translateX(-50%) scaleX(0); opacity: 0; transition: transform 560ms cubic-bezier(0.22, 1, 0.36, 1), opacity 160ms; }
79
- .passui-one-time-code:is([data-status="verifying"], [data-status="success"]) .passui-otp-cell { transform: translateX(calc((var(--otp-middle) - var(--otp-index)) * (100% + 0.5rem))); background: var(--color-accent); }
80
- .passui-one-time-code:is([data-status="verifying"], [data-status="success"]) .passui-otp-capsule { opacity: 1; transform: translateX(-50%) scaleX(1); }
81
- .passui-one-time-code:is([data-status="verifying"], [data-status="success"]) .passui-otp-digits { opacity: 0; }
82
- .passui-otp-check { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); color: var(--color-on-accent); opacity: 0; pointer-events: none; }
83
- .passui-one-time-code[data-size="sm"] .passui-otp-row { height: 2.5rem; }
84
- .passui-one-time-code[data-size="sm"] .passui-otp-digit { font-size: var(--text-title); }
85
- .passui-one-time-code[data-size="lg"] .passui-otp-row { height: 3.5rem; }
86
- .passui-one-time-code[data-size="lg"] .passui-otp-digit { font-size: var(--text-title2); }
87
- .passui-one-time-code[data-status="success"] .passui-otp-check { opacity: 1; transition: opacity 160ms 240ms; }
88
- /* A wrong code shivers, then drains right to left. Selecting the single
14
+ .passui-field-outline {
15
+ position: absolute;
16
+ inset: calc(var(--text-footnote) / -2) 0 0;
17
+ min-width: 0;
18
+ margin: 0;
19
+ padding: 0 0.5rem;
20
+ border: 1px solid var(--color-separator);
21
+ border-radius: inherit;
22
+ pointer-events: none;
23
+ transition: border-color 200ms ease;
24
+ }
25
+ .passui-field-outline legend {
26
+ max-width: 0;
27
+ overflow: hidden;
28
+ padding: 0;
29
+ font-size: var(--text-footnote);
30
+ line-height: 1;
31
+ height: 1em;
32
+ white-space: nowrap;
33
+ visibility: hidden;
34
+ transition:
35
+ max-width 320ms ease,
36
+ padding 320ms ease;
37
+ }
38
+ .passui-floating-label {
39
+ position: absolute;
40
+ inset-inline-start: 0.75rem;
41
+ top: 50%;
42
+ z-index: 1;
43
+ max-width: calc(100% - 1.5rem);
44
+ overflow: hidden;
45
+ text-overflow: ellipsis;
46
+ white-space: nowrap;
47
+ color: var(--color-label-secondary);
48
+ font-size: var(--text-body);
49
+ line-height: 1;
50
+ transform: translateY(-50%);
51
+ transform-origin: left center;
52
+ transition:
53
+ top 320ms ease,
54
+ transform 320ms ease,
55
+ color 200ms ease;
56
+ pointer-events: none;
57
+ }
58
+ .passui-floating-label:dir(rtl) {
59
+ transform-origin: right center;
60
+ }
61
+ .passui-floating-label-with-icon {
62
+ inset-inline-start: 2.5rem;
63
+ }
64
+ .passui-floating-field input::placeholder {
65
+ opacity: 0;
66
+ }
67
+ .passui-floating-field:focus-within input::placeholder {
68
+ opacity: 1;
69
+ }
70
+ .passui-floating-field:focus-within .passui-field-outline {
71
+ border-color: var(--color-label);
72
+ }
73
+ .passui-floating-field:has(input[aria-invalid="true"]) .passui-field-outline {
74
+ border-color: var(--color-danger);
75
+ }
76
+ .passui-floating-field:is(:focus-within, :has(input:not(:placeholder-shown)))
77
+ .passui-floating-label {
78
+ top: 0;
79
+ inset-inline-start: 0.75rem;
80
+ transform: translateY(-50%) scale(0.78);
81
+ color: var(--color-label);
82
+ }
83
+ .passui-floating-field:is(:focus-within, :has(input:not(:placeholder-shown)))
84
+ legend {
85
+ max-width: 100%;
86
+ padding-inline: 0.25rem;
87
+ }
88
+ .passui-one-time-code {
89
+ width: 100%;
90
+ max-width: calc(
91
+ var(--otp-length) *
92
+ 2.5rem +
93
+ (var(--otp-length) - 1) *
94
+ 0.5rem
95
+ );
96
+ }
97
+ .passui-otp-row {
98
+ position: relative;
99
+ height: 3rem;
100
+ }
101
+ .passui-otp-materials,
102
+ .passui-otp-digits {
103
+ display: flex;
104
+ position: absolute;
105
+ inset: 0;
106
+ gap: 0.5rem;
107
+ pointer-events: none;
108
+ }
109
+ .passui-otp-cell,
110
+ .passui-otp-digit {
111
+ flex: 1;
112
+ min-width: 0;
113
+ border-radius: var(--radius-xl);
114
+ }
115
+ .passui-otp-cell {
116
+ background: var(--color-fill);
117
+ transition:
118
+ transform 560ms cubic-bezier(0.22, 1, 0.36, 1),
119
+ background-color 200ms;
120
+ }
121
+ .passui-otp-digit {
122
+ display: grid;
123
+ place-items: center;
124
+ font-size: var(--text-title3);
125
+ color: var(--color-label);
126
+ font-variant-numeric: tabular-nums;
127
+ }
128
+ .passui-otp-input {
129
+ position: absolute;
130
+ inset: 0;
131
+ width: 100%;
132
+ height: 100%;
133
+ opacity: 0;
134
+ cursor: text;
135
+ }
136
+ .passui-one-time-code:focus-within .passui-otp-digit[data-active] {
137
+ box-shadow: inset 0 0 0 2px var(--color-accent-text);
138
+ }
139
+ .passui-otp-glyph {
140
+ animation: passui-otp-in 200ms ease-out both;
141
+ }
142
+ @keyframes passui-otp-in {
143
+ from {
144
+ transform: translateY(45%) scale(0.86);
145
+ opacity: 0;
146
+ filter: blur(3px);
147
+ }
148
+ }
149
+ .passui-otp-capsule {
150
+ position: absolute;
151
+ inset-block: 0;
152
+ left: 50%;
153
+ width: 60%;
154
+ border-radius: var(--radius-full);
155
+ background: var(--color-accent);
156
+ transform: translateX(-50%) scaleX(0);
157
+ opacity: 0;
158
+ transition:
159
+ transform 560ms cubic-bezier(0.22, 1, 0.36, 1),
160
+ opacity 160ms;
161
+ }
162
+ .passui-one-time-code:is([data-status="verifying"], [data-status="success"])
163
+ .passui-otp-cell {
164
+ transform: translateX(
165
+ calc((var(--otp-middle) - var(--otp-index)) * (100% + 0.5rem))
166
+ );
167
+ background: var(--color-accent);
168
+ }
169
+ .passui-one-time-code:is([data-status="verifying"], [data-status="success"])
170
+ .passui-otp-capsule {
171
+ opacity: 1;
172
+ transform: translateX(-50%) scaleX(1);
173
+ }
174
+ .passui-one-time-code:is([data-status="verifying"], [data-status="success"])
175
+ .passui-otp-digits {
176
+ opacity: 0;
177
+ }
178
+ .passui-otp-check {
179
+ position: absolute;
180
+ left: 50%;
181
+ top: 50%;
182
+ transform: translate(-50%, -50%);
183
+ color: var(--color-on-accent);
184
+ opacity: 0;
185
+ pointer-events: none;
186
+ }
187
+ .passui-one-time-code[data-size="sm"] .passui-otp-row {
188
+ height: 2.5rem;
189
+ }
190
+ .passui-one-time-code[data-size="sm"] .passui-otp-digit {
191
+ font-size: var(--text-title);
192
+ }
193
+ .passui-one-time-code[data-size="lg"] .passui-otp-row {
194
+ height: 3.5rem;
195
+ }
196
+ .passui-one-time-code[data-size="lg"] .passui-otp-digit {
197
+ font-size: var(--text-title2);
198
+ }
199
+ .passui-one-time-code[data-status="success"] .passui-otp-check {
200
+ opacity: 1;
201
+ transition: opacity 160ms 240ms;
202
+ }
203
+ /* A wrong code shivers, then drains right to left. Selecting the single
89
204
  input afterward lets the next keystroke replace it without a trap. */
90
- .passui-one-time-code[data-status="error"] .passui-otp-digit { animation: passui-otp-shiver 380ms ease-out; }
91
- .passui-one-time-code[data-status="error"] .passui-otp-glyph { animation: passui-otp-drain 220ms calc(380ms + (var(--otp-length) - var(--otp-index)) * 35ms) both; }
92
- @keyframes passui-otp-shiver { 20% { transform: translateX(4px); } 40% { transform: translateX(-3px); } 60% { transform: translateX(2px); } 80% { transform: translateX(-1px); } }
93
- @keyframes passui-otp-drain { to { transform: translateY(65%); opacity: 0; filter: blur(3px); } }
94
- .passui-progress-fill {
95
- clip-path: polygon(0 0, calc(100% + var(--progress-lean, 0px)) 0,
96
- calc(100% - var(--progress-lean, 0px)) 100%, 0 100%);
97
- }
98
- .passui-slosh-slider {
99
- width: 100%;
100
- min-width: 0;
101
- font-family: var(--font-sans);
102
- color: var(--color-label);
103
- }
205
+ .passui-one-time-code[data-status="error"] .passui-otp-digit {
206
+ animation: passui-otp-shiver 380ms ease-out;
207
+ }
208
+ .passui-one-time-code[data-status="error"] .passui-otp-glyph {
209
+ animation: passui-otp-drain 220ms
210
+ calc(380ms + (var(--otp-length) - var(--otp-index)) * 35ms) both;
211
+ }
212
+ @keyframes passui-otp-shiver {
213
+ 20% {
214
+ transform: translateX(4px);
215
+ }
216
+ 40% {
217
+ transform: translateX(-3px);
218
+ }
219
+ 60% {
220
+ transform: translateX(2px);
221
+ }
222
+ 80% {
223
+ transform: translateX(-1px);
224
+ }
225
+ }
226
+ @keyframes passui-otp-drain {
227
+ to {
228
+ transform: translateY(65%);
229
+ opacity: 0;
230
+ filter: blur(3px);
231
+ }
232
+ }
233
+ .passui-progress-fill {
234
+ clip-path: polygon(
235
+ 0 0,
236
+ calc(100% + var(--progress-lean, 0px)) 0,
237
+ calc(100% - var(--progress-lean, 0px)) 100%,
238
+ 0 100%
239
+ );
240
+ }
241
+ .passui-slosh-slider {
242
+ width: 100%;
243
+ min-width: 0;
244
+ font-family: var(--font-sans);
245
+ color: var(--color-label);
246
+ }
104
247
 
105
- /* No transitions: every geometry value is written by the loop, and
248
+ /* No transitions: every geometry value is written by the loop, and
106
249
  a transition on top of a spring is two opinions about one number. */
107
- .passui-slosh-track {
108
- position: relative;
109
- height: 2.5rem;
110
- border-radius: var(--slosh-radius, var(--radius-full));
111
- /* Opaque, mixed from the surface's own pair. A translucent wash
250
+ .passui-slosh-track {
251
+ position: relative;
252
+ height: 2.5rem;
253
+ border-radius: var(--slosh-radius, var(--radius-full));
254
+ /* Opaque, mixed from the surface's own pair. A translucent wash
112
255
  changes with the stage behind it and can disappear in dark mode. */
113
- background: color-mix(in srgb, var(--color-label) 11%, var(--color-elevated));
114
- overflow: hidden;
115
- cursor: ew-resize;
116
- touch-action: pan-y;
117
- }
118
- .passui-slosh-track[aria-disabled="true"],
119
- .passui-wake-track[aria-disabled="true"] { cursor: not-allowed; }
120
- .passui-slosh-track:focus-visible,
121
- .passui-wake-track:focus-visible {
122
- outline: 2px solid var(--color-accent-text);
123
- outline-offset: 4px;
124
- }
125
- .passui-slosh-fill {
126
- position: absolute;
127
- inset-block: 0;
128
- inset-inline-start: 0;
129
- background: light-dark(var(--color-elevated), color-mix(in srgb, var(--color-label) 15%, var(--color-elevated)));
130
- /* Clipped, never skewed: a skew lifts the whole body off the floor;
256
+ background: color-mix(
257
+ in srgb,
258
+ var(--color-label) 11%,
259
+ var(--color-elevated)
260
+ );
261
+ overflow: hidden;
262
+ cursor: ew-resize;
263
+ touch-action: pan-y;
264
+ }
265
+ .passui-slosh-track[aria-disabled="true"],
266
+ .passui-wake-track[aria-disabled="true"] {
267
+ cursor: not-allowed;
268
+ }
269
+ .passui-slosh-track:focus-visible,
270
+ .passui-wake-track:focus-visible {
271
+ outline: 2px solid var(--color-accent-text);
272
+ outline-offset: 4px;
273
+ }
274
+ .passui-slosh-fill {
275
+ position: absolute;
276
+ inset-block: 0;
277
+ inset-inline-start: 0;
278
+ background: light-dark(
279
+ var(--color-elevated),
280
+ color-mix(in srgb, var(--color-label) 15%, var(--color-elevated))
281
+ );
282
+ /* Clipped, never skewed: a skew lifts the whole body off the floor;
131
283
  a meniscus leans only the moving edge. The track clips end stops. */
132
- clip-path: polygon(0 0, calc(100% + var(--slosh-lean, 0px)) 0,
133
- calc(100% - var(--slosh-lean, 0px)) 100%, 0 100%);
134
- }
135
- .passui-slosh-track:dir(rtl) .passui-slosh-fill { transform: scaleX(-1); }
136
- .passui-slosh-slider[data-size="sm"] .passui-slosh-track { height: 2rem; }
137
- .passui-slosh-slider[data-size="sm"] .passui-slosh-knob { top: 4px; bottom: 4px; }
138
- .passui-slosh-slider[data-size="lg"] .passui-slosh-track { height: 3rem; }
139
- .passui-slosh-slider[data-size="lg"] .passui-slosh-knob { top: 8px; bottom: 8px; width: 4px; margin-inline-start: -2px; }
140
- .passui-slosh-knob {
141
- position: absolute;
142
- top: 6px;
143
- bottom: 6px;
144
- width: 3px;
145
- margin-inline-start: -1.5px;
146
- border-radius: var(--radius-full);
147
- background: var(--color-label);
148
- pointer-events: none;
149
- }
150
- /* Each bar holds energy. Its height is written directly, while React
284
+ clip-path: polygon(
285
+ 0 0,
286
+ calc(100% + var(--slosh-lean, 0px)) 0,
287
+ calc(100% - var(--slosh-lean, 0px)) 100%,
288
+ 0 100%
289
+ );
290
+ }
291
+ .passui-slosh-track:dir(rtl) .passui-slosh-fill {
292
+ transform: scaleX(-1);
293
+ }
294
+ .passui-slosh-slider[data-size="sm"] .passui-slosh-track {
295
+ height: 2rem;
296
+ }
297
+ .passui-slosh-slider[data-size="sm"] .passui-slosh-knob {
298
+ top: 4px;
299
+ bottom: 4px;
300
+ }
301
+ .passui-slosh-slider[data-size="lg"] .passui-slosh-track {
302
+ height: 3rem;
303
+ }
304
+ .passui-slosh-slider[data-size="lg"] .passui-slosh-knob {
305
+ top: 8px;
306
+ bottom: 8px;
307
+ width: 4px;
308
+ margin-inline-start: -2px;
309
+ }
310
+ .passui-slosh-knob {
311
+ position: absolute;
312
+ top: 6px;
313
+ bottom: 6px;
314
+ width: 3px;
315
+ margin-inline-start: -1.5px;
316
+ border-radius: var(--radius-full);
317
+ background: var(--color-label);
318
+ pointer-events: none;
319
+ }
320
+ /* Each bar holds energy. Its height is written directly, while React
151
321
  owns the value and lit marks. No standing animation runs at rest. */
152
- .passui-wake-slider {
153
- display: flex;
154
- align-items: center;
155
- gap: 1rem;
156
- width: 100%;
157
- min-width: 0;
158
- color: var(--color-label);
159
- }
160
- .passui-wake-track {
161
- display: flex;
162
- flex: 1;
163
- min-width: 0;
164
- align-items: center;
165
- justify-content: space-between;
166
- gap: min(4px, calc(25% / var(--wake-bars, 36)));
167
- cursor: ew-resize;
168
- touch-action: pan-y;
169
- user-select: none;
170
- -webkit-user-select: none;
171
- -webkit-tap-highlight-color: transparent;
172
- border-radius: var(--radius-sm);
173
- }
174
- .passui-wake-bar {
175
- flex: 1 1 3px;
176
- max-width: 3px;
177
- min-width: 0;
178
- border-radius: var(--radius-full);
179
- background: color-mix(in srgb, var(--color-label) 24%, var(--color-elevated));
180
- transform-origin: 50% 50%;
181
- transition: background-color 160ms ease;
182
- }
183
- .passui-wake-bar[data-on] { background: var(--color-label); }
184
- .passui-wake-value {
185
- min-width: 3ch;
186
- font-family: var(--font-sans);
187
- font-size: var(--text-footnote);
188
- font-weight: 500;
189
- font-variant-numeric: tabular-nums;
190
- color: var(--color-label-secondary);
191
- }
192
- .passui-wake-slider[data-size="sm"] { gap: 0.75rem; }
193
- .passui-wake-slider[data-size="sm"] .passui-wake-value { font-size: var(--text-caption); }
194
- .passui-wake-slider[data-size="lg"] { gap: 1.25rem; }
195
- .passui-wake-slider[data-size="lg"] .passui-wake-value { font-size: var(--text-subheadline); }
196
- @media (prefers-reduced-motion: reduce) {
197
- .passui-wake-bar, .passui-search-pill { transition: none; }
198
- .passui-search-pill:active { scale: 1; }
199
- .passui-floating-label, .passui-field-outline, .passui-field-outline legend,
200
- .passui-otp-cell, .passui-otp-capsule, .passui-otp-check { transition: none; }
201
- .passui-otp-glyph, .passui-otp-digit { animation: none !important; }
202
- .passui-otp-materials { filter: none !important; }
203
- }
322
+ .passui-wake-slider {
323
+ display: flex;
324
+ align-items: center;
325
+ gap: 1rem;
326
+ width: 100%;
327
+ min-width: 0;
328
+ color: var(--color-label);
329
+ }
330
+ .passui-wake-track {
331
+ display: flex;
332
+ flex: 1;
333
+ min-width: 0;
334
+ align-items: center;
335
+ justify-content: space-between;
336
+ gap: min(4px, calc(25% / var(--wake-bars, 36)));
337
+ cursor: ew-resize;
338
+ touch-action: pan-y;
339
+ user-select: none;
340
+ -webkit-user-select: none;
341
+ -webkit-tap-highlight-color: transparent;
342
+ border-radius: var(--radius-sm);
343
+ }
344
+ .passui-wake-bar {
345
+ flex: 1 1 3px;
346
+ max-width: 3px;
347
+ min-width: 0;
348
+ border-radius: var(--radius-full);
349
+ background: color-mix(
350
+ in srgb,
351
+ var(--color-label) 24%,
352
+ var(--color-elevated)
353
+ );
354
+ transform-origin: 50% 50%;
355
+ transition: background-color 160ms ease;
356
+ }
357
+ .passui-wake-bar[data-on] {
358
+ background: var(--color-label);
359
+ }
360
+ .passui-wake-value {
361
+ min-width: 3ch;
362
+ font-family: var(--font-sans);
363
+ font-size: var(--text-footnote);
364
+ font-weight: 500;
365
+ font-variant-numeric: tabular-nums;
366
+ color: var(--color-label-secondary);
367
+ }
368
+ .passui-wake-slider[data-size="sm"] {
369
+ gap: 0.75rem;
370
+ }
371
+ .passui-wake-slider[data-size="sm"] .passui-wake-value {
372
+ font-size: var(--text-caption);
373
+ }
374
+ .passui-wake-slider[data-size="lg"] {
375
+ gap: 1.25rem;
376
+ }
377
+ .passui-wake-slider[data-size="lg"] .passui-wake-value {
378
+ font-size: var(--text-subheadline);
379
+ }
380
+ @media (prefers-reduced-motion: reduce) {
381
+ .passui-wake-bar,
382
+ .passui-search-pill {
383
+ transition: none;
384
+ }
385
+ .passui-search-pill:active {
386
+ scale: 1;
387
+ }
388
+ .passui-floating-label,
389
+ .passui-field-outline,
390
+ .passui-field-outline legend,
391
+ .passui-otp-cell,
392
+ .passui-otp-capsule,
393
+ .passui-otp-check {
394
+ transition: none;
395
+ }
396
+ .passui-otp-glyph,
397
+ .passui-otp-digit {
398
+ /* biome-ignore lint/complexity/noImportantStyles: CSS utility override */
399
+ animation: none !important;
400
+ }
401
+ .passui-otp-materials {
402
+ /* biome-ignore lint/complexity/noImportantStyles: CSS utility override */
403
+ filter: none !important;
404
+ }
405
+ }
204
406
  }
package/src/tokens.ts CHANGED
@@ -1 +1 @@
1
- export * from "@passui/tokens"
1
+ export * from "@passui/tokens";
@@ -1,21 +0,0 @@
1
- import { type PlacementFrame } from "../../lib/anchored-placement";
2
- interface PanelRect {
3
- left: number;
4
- top: number;
5
- width: number;
6
- height: number;
7
- }
8
- interface AnchorRect {
9
- top: number;
10
- bottom: number;
11
- }
12
- export interface PopoverFit {
13
- shiftX: number;
14
- shiftY: number;
15
- maxWidth: number;
16
- maxHeight?: number;
17
- above: boolean;
18
- }
19
- /** Keep an absolutely positioned popover visible without scaling its contents. */
20
- export declare function fitPopover(panel: PanelRect, anchor: AnchorRect | undefined, frame: Pick<PlacementFrame, "viewport" | "edges">, gap?: number, origin?: "top-left" | "top-right"): PopoverFit;
21
- export {};
@@ -1,21 +0,0 @@
1
- import { type HTMLMotionProps } from "motion/react";
2
- import { type ReactNode, type RefObject } from "react";
3
- export type PopoverOrigin = "top-left" | "top-right";
4
- export type PopoverSize = "sm" | "md";
5
- export interface PopoverProps extends Omit<HTMLMotionProps<"div">, "children"> {
6
- open: boolean;
7
- children?: ReactNode;
8
- /** Which corner the popover grows from. */
9
- origin?: PopoverOrigin;
10
- /** Trigger whose shape the popover uses during its opening and closing motion. */
11
- anchorRef?: RefObject<HTMLElement | null>;
12
- /** Surface corner radius. @default "md" */
13
- size?: PopoverSize;
14
- }
15
- /**
16
- * A glass surface that springs open from its anchor. Position it with
17
- * `className` (it is absolutely positioned within a relative parent); it
18
- * flips above its relative parent when the bottom edge is short, and
19
- * stays inside the visible viewport as it resizes or scrolls.
20
- */
21
- export declare function Popover({ open, ...props }: PopoverProps): import("react").JSX.Element;