@vvedantb/ui 0.1.5 → 0.2.1

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 (87) hide show
  1. package/README.md +49 -0
  2. package/app/README.md +100 -0
  3. package/app/src/auth/useStableAuth.ts +69 -0
  4. package/app/src/cn.ts +6 -0
  5. package/app/src/confirm/ConfirmSkipHint.tsx +35 -0
  6. package/app/src/confirm/confirmSkip.ts +26 -0
  7. package/app/src/confirm/useAltHeld.ts +128 -0
  8. package/app/src/confirm/useRequestConfirm.ts +25 -0
  9. package/app/src/index.ts +43 -0
  10. package/app/src/settings/SettingsEmptyState.tsx +38 -0
  11. package/app/src/settings/SettingsField.tsx +42 -0
  12. package/app/src/settings/SettingsPage.tsx +88 -0
  13. package/app/src/settings/SettingsSection.tsx +92 -0
  14. package/app/src/settings/SettingsStack.tsx +17 -0
  15. package/app/src/settings/SettingsToggleRow.tsx +51 -0
  16. package/app/src/update/deployFingerprint.ts +27 -0
  17. package/app/src/update/updateWatcher.ts +100 -0
  18. package/package.json +71 -11
  19. package/shell/src/PillTabs.tsx +86 -0
  20. package/shell/src/SidebarHeader.tsx +21 -0
  21. package/shell/src/cn.ts +5 -0
  22. package/shell/src/index.ts +6 -0
  23. package/src/ai-elements/artifact.tsx +1 -1
  24. package/src/ai-elements/attachments.tsx +3 -3
  25. package/src/ai-elements/chain-of-thought.tsx +5 -5
  26. package/src/ai-elements/code-block.tsx +2 -2
  27. package/src/ai-elements/conversation.tsx +3 -3
  28. package/src/ai-elements/inline-citation.tsx +6 -6
  29. package/src/ai-elements/message.tsx +2 -2
  30. package/src/ai-elements/prompt-input.tsx +3 -3
  31. package/src/ai-elements/queue.tsx +2 -2
  32. package/src/ai-elements/reasoning.tsx +1 -1
  33. package/src/ai-elements/shimmer.tsx +5 -5
  34. package/src/ai-elements/sources.tsx +2 -2
  35. package/src/ai-elements/speech-input.tsx +1 -1
  36. package/src/ai-elements/suggestion.tsx +2 -2
  37. package/src/index.ts +18 -18
  38. package/src/ui/{_menu-classes.vibot.ts → _menu-classes.alt.ts} +4 -4
  39. package/src/ui/_menu-classes.ts +4 -4
  40. package/src/ui/{accordion.verve.tsx → accordion.alt.tsx} +2 -2
  41. package/src/ui/badge.tsx +1 -1
  42. package/src/ui/breadcrumb.tsx +2 -2
  43. package/src/ui/button.tsx +3 -3
  44. package/src/ui/checkbox.tsx +1 -1
  45. package/src/ui/{clear-input.vibot.tsx → clear-input.alt.tsx} +1 -1
  46. package/src/ui/clear-input.tsx +1 -1
  47. package/src/ui/command.tsx +5 -5
  48. package/src/ui/dialog.tsx +1 -1
  49. package/src/ui/{input-group.verve.tsx → input-group.alt.tsx} +1 -1
  50. package/src/ui/input-group.tsx +2 -2
  51. package/src/ui/kbd.tsx +1 -1
  52. package/src/ui/labeled-switch-row.tsx +2 -2
  53. package/src/ui/progress.tsx +1 -1
  54. package/src/ui/radix-select.tsx +3 -3
  55. package/src/ui/search-input.tsx +1 -1
  56. package/src/ui/select.tsx +4 -4
  57. package/src/ui/sonner.tsx +5 -5
  58. package/src/ui/spinner.tsx +1 -1
  59. package/src/ui/switch.tsx +2 -2
  60. package/src/ui/table.tsx +2 -2
  61. package/src/ui/time-picker.tsx +3 -3
  62. package/tokens/README.md +98 -0
  63. package/tokens/examples/compat-shadcn-rgb/Fixture.tsx +45 -0
  64. package/tokens/examples/compat-shadcn-rgb/README.md +11 -0
  65. package/tokens/examples/compat-shadcn-rgb/index.html +12 -0
  66. package/tokens/examples/compat-shadcn-rgb/main.tsx +5 -0
  67. package/tokens/examples/compat-shadcn-rgb/package.json +14 -0
  68. package/tokens/examples/compat-shadcn-rgb/styles.css +67 -0
  69. package/tokens/examples/compat-shadcn-rgb/tsconfig.json +14 -0
  70. package/tokens/examples/compat-shadcn-rgb/vite.config.ts +11 -0
  71. package/tokens/examples/migration.md +303 -0
  72. package/tokens/src/css/animations.css +55 -0
  73. package/tokens/src/css/base.css +51 -0
  74. package/tokens/src/css/compat-shadcn-rgb.css +75 -0
  75. package/tokens/src/css/dialog.css +13 -0
  76. package/tokens/src/css/index.css +19 -0
  77. package/tokens/src/css/modal.css +44 -0
  78. package/tokens/src/css/scrollbar-thin.css +40 -0
  79. package/tokens/src/css/tabs-pills.css +58 -0
  80. package/tokens/src/css/tabs.css +57 -0
  81. package/tokens/src/css/utilities.css +34 -0
  82. package/tokens/src/native.ts +39 -0
  83. package/tokens/src/tailwind-preset.d.mts +25 -0
  84. package/tokens/src/tailwind-preset.mjs +183 -0
  85. package/tokens/src/tokens.css +207 -0
  86. /package/src/motion/{presets.vibot.ts → presets.alt.ts} +0 -0
  87. /package/src/ui/{scroll-area.verve.tsx → scroll-area.alt.tsx} +0 -0
@@ -0,0 +1,303 @@
1
+ # Migrating the apps to `@vvedantb/ui/tokens` 0.2.0 shared CSS
2
+
3
+ Generated by `pnpm --filter @vvedantb/ui verify` from `verify/migrations/<app>.json`; do not edit by hand. Line numbers refer to each clone's HEAD shown below. Line counts come from `git diff --numstat` of the original and migrated files. The harness also writes a ready-to-apply `<app>.patch` (paths relative to the app repo root) to `$VERIFY_DIR/patches/`.
4
+
5
+ Bump the app's `@vvedantb/ui/tokens` dependency to `^0.2.0` first (app-d: add it). Removed blocks include the comment directly above them. `@layer` / `@media` / `@supports` wrappers left empty are removed with their comment; the line ranges below list the blocks themselves, the patch has every line.
6
+
7
+ ## Totals
8
+
9
+ | App | HEAD | Shared files | Deleted | Added | Net | Verified |
10
+ | --- | --- | --- | --- | --- | --- | --- |
11
+ | app-a | `8c972b1a2cf7` | animations.css, base.css, tabs-pills.css, dialog.css, modal.css, tabs.css, scrollbar-thin.css | 257 | 10 | -247 | identical CSS |
12
+ | app-b | `f7b61259270b` | utilities.css, base.css, dialog.css, scrollbar-thin.css, tailwind-preset | 341 | 8 | -333 | identical CSS |
13
+ | app-c | `4d0ab45cbbc6` | base.css, tabs-pills.css, dialog.css, utilities.css, tailwind-preset | 257 | 9 | -248 | identical CSS |
14
+ | app-d | `3bdce79780d0` | utilities.css, base.css, modal.css, tabs.css, scrollbar-thin.css, tailwind-preset | 300 | 11 | -289 | identical CSS |
15
+ | app-e | `7077eaf9ff8a` | tabs-pills.css, dialog.css, modal.css, tabs.css | 132 | 6 | -126 | identical CSS |
16
+ | **Total** | | | **1287** | **44** | **-1243** | |
17
+
18
+ Package side: the shared files total 573 lines (`css/animations.css` 55, `css/base.css` 51, `css/dialog.css` 13, `css/index.css` 19, `css/modal.css` 44, `css/scrollbar-thin.css` 40, `css/tabs-pills.css` 58, `css/tabs.css` 57, `css/utilities.css` 34, `tailwind-preset.mjs` 177, `tailwind-preset.d.mts` 25), comments included. Net across apps + package: -670.
19
+
20
+ The duplication report (R3) estimated about 1,917 removable repo-side lines for CSS + Tailwind theme. This migration removes 1287 lines and adds 44 (net -1243). The gap is real, not rounding: R3 counted rule blocks shared by only 2 apps, near-identical blocks that drift in values or selectors, app-a/app-e `landing.css` (a separate, byte-identical 271-line file) and per-app theme colour blocks. Those stay app-local here because moving them would change at least one app's compiled CSS or would serve fewer than 3 apps (see `verify/ANALYSIS.md`, "Not extracted").
21
+
22
+ ## app-a
23
+
24
+ Clone HEAD `8c972b1a2cf7b341c3282a26d904fce39aed3232`. Shared files: `animations.css`, `base.css`, `tabs-pills.css`, `dialog.css`, `modal.css`, `tabs.css`, `scrollbar-thin.css`.
25
+
26
+ ### `apps/web/src/globals.css` (-257 +10)
27
+
28
+ Add (each `@import` replaces the block at that position):
29
+
30
+ ```css
31
+ @import "@vvedantb/ui/tokens/css/animations.css"; /* before original line 11 */
32
+ @import "@vvedantb/ui/tokens/css/base.css"; /* before original line 101 */
33
+ @import "@vvedantb/ui/tokens/css/tabs-pills.css"; /* before original line 101 */
34
+ @import "@vvedantb/ui/tokens/css/dialog.css"; /* before original line 228 */
35
+ @import "@vvedantb/ui/tokens/css/modal.css"; /* before original line 239 */
36
+ @import "@vvedantb/ui/tokens/css/tabs.css"; /* before original line 239 */
37
+ @import "@vvedantb/ui/tokens/css/scrollbar-thin.css"; /* after original line 470 */
38
+ ```
39
+
40
+ Delete:
41
+
42
+ | Lines | Context | Selector | Replaced by |
43
+ | --- | --- | --- | --- |
44
+ | 17-19 | (top level) | `@theme (declarations --animate-aurora, --animate-fade-in-up, --animate-indeterminate only)` | `@vvedantb/ui/tokens/css/animations.css` |
45
+ | 21-42 | @theme | `@keyframes aurora` | `@vvedantb/ui/tokens/css/animations.css` |
46
+ | 44-53 | @theme | `@keyframes fade-in-up` | `@vvedantb/ui/tokens/css/animations.css` |
47
+ | 55-62 | @theme | `@keyframes indeterminate` | `@vvedantb/ui/tokens/css/animations.css` |
48
+ | 106-109 | @layer base | `html` | `@vvedantb/ui/tokens/css/base.css` |
49
+ | 111-121 | @layer base | `:root, .light, .default, [data-theme="light"], [data-theme="default"]` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
50
+ | 123-130 | @layer base | `.dark, [data-theme="dark"]` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
51
+ | 134-137 | @layer base | `::selection` | `@vvedantb/ui/tokens/css/base.css` |
52
+ | 139-148 | @layer base | `button, a, input, textarea, [role="button"]` | `@vvedantb/ui/tokens/css/base.css` |
53
+ | 150-156 | @layer base | `button:not(:disabled), [role="button"]:not(:disabled), a[href]` | `@vvedantb/ui/tokens/css/base.css` |
54
+ | 173-175 | @layer base | `input[type="range"]` | `@vvedantb/ui/tokens/css/base.css` |
55
+ | 228-237 | (top level) | `[role="dialog"][data-compact="true"], [role="alertdialog"][data-compact="true"]` | `@vvedantb/ui/tokens/css/dialog.css` |
56
+ | 240-250 | @layer components | `.t-modal` | `@vvedantb/ui/tokens/css/modal.css` |
57
+ | 251-255 | @layer components | `.t-modal.is-open` | `@vvedantb/ui/tokens/css/modal.css` |
58
+ | 256-265 | @layer components | `.t-modal.is-closing` | `@vvedantb/ui/tokens/css/modal.css` |
59
+ | 267-274 | @layer components | `@keyframes t-modal-presence-wait` | `@vvedantb/ui/tokens/css/modal.css` |
60
+ | 283-289 | @layer components | `.t-tabs--segmented` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
61
+ | 290-296 | @layer components | `.t-tabs--pills` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
62
+ | 298-302 | @layer components > @media (max-width: 639px) | `.t-tabs` | `@vvedantb/ui/tokens/css/tabs.css` |
63
+ | 303-305 | @layer components > @media (max-width: 639px) | `.t-tabs::-webkit-scrollbar` | `@vvedantb/ui/tokens/css/tabs.css` |
64
+ | 307-319 | @layer components | `.t-tab` | `@vvedantb/ui/tokens/css/tabs.css` |
65
+ | 320-324 | @layer components | `.t-tab:not([aria-selected="true"]):not([data-state="active"]):hover, .t-tab[aria-selected="true"], .t-tab[data-state="active"]` | `@vvedantb/ui/tokens/css/tabs.css` |
66
+ | 335-342 | @layer components | `.t-tabs--pills .t-tab:not([aria-selected="true"]):not([data-state="active"]):hover` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
67
+ | 343-346 | @layer components | `.t-tabs--pills .t-tab[aria-selected="true"], .t-tabs--pills .t-tab[data-state="active"]` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
68
+ | 347-364 | @layer components | `.t-tabs-pill` | `@vvedantb/ui/tokens/css/tabs.css` |
69
+ | 442-446 | @layer utilities > @supports not selector(::-webkit-scrollbar) | `.scrollbar-thin` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
70
+ | 448-451 | @layer utilities > @supports not selector(::-webkit-scrollbar) | `.scrollbar-thin:hover` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
71
+ | 454-456 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
72
+ | 458-460 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar-track` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
73
+ | 462-465 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar-thumb` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
74
+ | 467-469 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar-thumb:hover` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
75
+
76
+ ## app-b
77
+
78
+ Clone HEAD `f7b61259270bb6152227574b8d249b392ec94f53`. Shared files: `utilities.css`, `base.css`, `dialog.css`, `scrollbar-thin.css`, `tailwind-preset`.
79
+
80
+ ### `apps/web/src/styles/globals.css` (-208 +6)
81
+
82
+ Add (each `@import` replaces the block at that position):
83
+
84
+ ```css
85
+ @import "@vvedantb/ui/tokens/css/utilities.css"; /* before original line 43 */
86
+ @import "@vvedantb/ui/tokens/css/base.css"; /* before original line 107 */
87
+ @import "@vvedantb/ui/tokens/css/dialog.css"; /* before original line 376 */
88
+ @import "@vvedantb/ui/tokens/css/scrollbar-thin.css"; /* before original line 589 */
89
+ ```
90
+
91
+ Delete:
92
+
93
+ | Lines | Context | Selector | Replaced by |
94
+ | --- | --- | --- | --- |
95
+ | 43-55 | (top level) | `@utility hit-target` | `@vvedantb/ui/tokens/css/utilities.css` |
96
+ | 57-69 | (top level) | `@utility reveal-on-hover` | `@vvedantb/ui/tokens/css/utilities.css` |
97
+ | 145-148 | @layer base | `html` | `@vvedantb/ui/tokens/css/base.css` |
98
+ | 155-206 | @layer base | `:root, .light, .default, [data-theme="light"], [data-theme="default"] (declarations color-scheme, --accent, --accent-foreground, --backdrop, --background, --border, --danger, --danger-foreground, --default, --default-foreground, --field-background, --field-border, --field-foreground, --field-placeholder, --focus, --focus-ring, --focus-border, --danger-ring, --danger-border, --foreground, --muted, --overlay, --overlay-foreground, --overlay-shadow, --scrollbar, --segment, --segment-foreground, --separator, --success, --success-foreground, --surface, --surface-foreground, --surface-secondary, --surface-secondary-foreground, --surface-tertiary, --surface-tertiary-foreground, --surface-card, --warning, --warning-foreground, --radius, --field-radius only)` | `@vvedantb/ui/tokens (already imported)` |
99
+ | 232-281 | @layer base | `.dark, [data-theme="dark"] (declarations color-scheme, --accent, --accent-foreground, --backdrop, --background, --border, --danger, --danger-foreground, --default, --default-foreground, --field-background, --field-border, --field-foreground, --field-placeholder, --focus, --focus-ring, --focus-border, --danger-ring, --danger-border, --foreground, --muted, --overlay, --overlay-foreground, --overlay-shadow, --scrollbar, --segment, --segment-foreground, --separator, --success, --success-foreground, --surface, --surface-foreground, --surface-secondary, --surface-secondary-foreground, --surface-tertiary, --surface-tertiary-foreground, --surface-card, --warning, --warning-foreground, --radius, --field-radius only)` | `@vvedantb/ui/tokens (already imported)` |
100
+ | 307-309 | @layer base | `*` | `@vvedantb/ui/tokens (already imported)` |
101
+ | 311-316 | @layer base | `body` | `@vvedantb/ui/tokens (already imported)` |
102
+ | 318-321 | @layer base | `::selection` | `@vvedantb/ui/tokens/css/base.css` |
103
+ | 323-332 | @layer base | `button, a, input, textarea, [role="button"]` | `@vvedantb/ui/tokens/css/base.css` |
104
+ | 334-340 | @layer base | `button:not(:disabled), [role="button"]:not(:disabled), a[href]` | `@vvedantb/ui/tokens/css/base.css` |
105
+ | 365-367 | @layer base | `input[type="range"]` | `@vvedantb/ui/tokens/css/base.css` |
106
+ | 376-385 | (top level) | `[role="dialog"][data-compact="true"], [role="alertdialog"][data-compact="true"]` | `@vvedantb/ui/tokens/css/dialog.css` |
107
+ | 640-644 | @layer utilities > @supports not selector(::-webkit-scrollbar) | `.scrollbar-thin` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
108
+ | 646-649 | @layer utilities > @supports not selector(::-webkit-scrollbar) | `.scrollbar-thin:hover` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
109
+ | 652-654 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
110
+ | 656-658 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar-track` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
111
+ | 660-663 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar-thumb` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
112
+ | 665-667 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar-thumb:hover` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
113
+
114
+ ### `apps/web/tailwind.config.ts` (-133 +2)
115
+
116
+ Edits (original lines -> replacement):
117
+
118
+ - Lines 2-2 become:
119
+
120
+ ```ts
121
+ import type { Config } from "tailwindcss";
122
+ import { tokensPreset } from "@vvedantb/ui/tokens/tailwind-preset";
123
+ ```
124
+ - Lines 13-13 become:
125
+
126
+ ```ts
127
+ presets: [tokensPreset()],
128
+ ```
129
+ - Lines 25-28: delete.
130
+ - Lines 33-42: delete.
131
+ - Lines 45-53: delete.
132
+ - Lines 56-95: delete.
133
+ - Lines 97-104: delete.
134
+ - Lines 133-136: delete.
135
+ - Lines 141-148: delete.
136
+ - Lines 155-162: delete.
137
+ - Lines 164-201: delete.
138
+ - Lines 228-230: delete.
139
+
140
+ ## app-c
141
+
142
+ Clone HEAD `4d0ab45cbbc640e3bb0cdbde21a9f42a5b6de54e`. Shared files: `base.css`, `tabs-pills.css`, `dialog.css`, `utilities.css`, `tailwind-preset`.
143
+
144
+ ### `apps/web/src/globals.css` (-117 +7)
145
+
146
+ Add (each `@import` replaces the block at that position):
147
+
148
+ ```css
149
+ @import "@vvedantb/ui/tokens/css/base.css"; /* before original line 100 */
150
+ @import "@vvedantb/ui/tokens/css/tabs-pills.css"; /* before original line 100 */
151
+ @import "@vvedantb/ui/tokens/css/dialog.css"; /* before original line 204 */
152
+ @import "@vvedantb/ui/tokens/css/utilities.css"; /* before original line 580 */
153
+ ```
154
+
155
+ Delete:
156
+
157
+ | Lines | Context | Selector | Replaced by |
158
+ | --- | --- | --- | --- |
159
+ | 101-104 | @layer base | `html` | `@vvedantb/ui/tokens/css/base.css` |
160
+ | 111-115 | @layer base | `:root, .light, .default, [data-theme="light"], [data-theme="default"] (declarations --tabs-text-muted, --tabs-text-active, --tabs-bar-bg, --tabs-pill-bg, --tabs-pill-active-bg only)` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
161
+ | 126-130 | @layer base | `.dark, [data-theme="dark"] (declarations --tabs-text-muted, --tabs-text-active, --tabs-bar-bg, --tabs-pill-bg, --tabs-pill-active-bg only)` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
162
+ | 141-143 | @layer base | `*` | `@vvedantb/ui/tokens (already imported)` |
163
+ | 145-150 | @layer base | `body` | `@vvedantb/ui/tokens (already imported)` |
164
+ | 164-167 | @layer base | `::selection` | `@vvedantb/ui/tokens/css/base.css` |
165
+ | 169-177 | @layer base | `button, a, input, textarea, [role="button"]` | `@vvedantb/ui/tokens/css/base.css` |
166
+ | 179-183 | @layer base | `button:not(:disabled), [role="button"]:not(:disabled), a[href]` | `@vvedantb/ui/tokens/css/base.css` |
167
+ | 199-201 | @layer base | `input[type="range"]` | `@vvedantb/ui/tokens/css/base.css` |
168
+ | 204-214 | (top level) | `[role="dialog"][data-compact="true"], [role="alertdialog"][data-compact="true"]` | `@vvedantb/ui/tokens/css/dialog.css` |
169
+ | 289-294 | @layer components | `.t-tabs--segmented` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
170
+ | 295-299 | @layer components | `.t-tabs--pills` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
171
+ | 340-347 | @layer components | `.t-tabs--pills .t-tab:not([aria-selected="true"]):not([data-state="active"]):hover` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
172
+ | 348-351 | @layer components | `.t-tabs--pills .t-tab[aria-selected="true"], .t-tabs--pills .t-tab[data-state="active"]` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
173
+ | 580-593 | (top level) | `@utility hit-target` | `@vvedantb/ui/tokens/css/utilities.css` |
174
+ | 595-609 | (top level) | `@utility reveal-on-hover` | `@vvedantb/ui/tokens/css/utilities.css` |
175
+
176
+ ### `apps/web/tailwind.config.ts` (-140 +2)
177
+
178
+ Edits (original lines -> replacement):
179
+
180
+ - Lines 2-2 become:
181
+
182
+ ```ts
183
+ import tailwindcssAnimate from "tailwindcss-animate";
184
+ import { tokensPreset } from "@vvedantb/ui/tokens/tailwind-preset";
185
+ ```
186
+ - Lines 16-16 become:
187
+
188
+ ```ts
189
+ presets: [tokensPreset({ color: v })],
190
+ ```
191
+ - Lines 42-66: delete.
192
+ - Lines 69-72: delete.
193
+ - Lines 74-97: delete.
194
+ - Lines 102-117: delete.
195
+ - Lines 126-130: delete.
196
+ - Lines 150-173: delete.
197
+ - Lines 178-215: delete.
198
+ - Lines 238-238: delete.
199
+ - Lines 240-240: delete.
200
+ - Lines 242-242: delete.
201
+
202
+ ## app-d
203
+
204
+ Clone HEAD `3bdce79780d0226465be491854618d5f02281316`. Shared files: `utilities.css`, `base.css`, `modal.css`, `tabs.css`, `scrollbar-thin.css`, `tailwind-preset`.
205
+
206
+ ### `apps/web/src/globals.css` (-162 +9)
207
+
208
+ Add (each `@import` replaces the block at that position):
209
+
210
+ ```css
211
+ @import "@vvedantb/ui/tokens/css/utilities.css"; /* before original line 34 */
212
+ @import "@vvedantb/ui/tokens/css/base.css"; /* before original line 59 */
213
+ @import "@vvedantb/ui/tokens/css/modal.css"; /* before original line 273 */
214
+ @import "@vvedantb/ui/tokens/css/tabs.css"; /* after original line 360; close the enclosing block before it and reopen it after */
215
+ @import "@vvedantb/ui/tokens/css/scrollbar-thin.css"; /* before original line 425 */
216
+ ```
217
+
218
+ Delete:
219
+
220
+ | Lines | Context | Selector | Replaced by |
221
+ | --- | --- | --- | --- |
222
+ | 34-43 | (top level) | `@utility hit-target` | `@vvedantb/ui/tokens/css/utilities.css` |
223
+ | 45-57 | (top level) | `@utility reveal-on-hover` | `@vvedantb/ui/tokens/css/utilities.css` |
224
+ | 98-99 | @layer base | `html (declarations touch-action, accent-color only)` | `@vvedantb/ui/tokens/css/base.css` |
225
+ | 232-235 | @layer base | `::selection` | `@vvedantb/ui/tokens/css/base.css` |
226
+ | 237-246 | @layer base | `button, a, input, textarea, [role="button"]` | `@vvedantb/ui/tokens/css/base.css` |
227
+ | 248-252 | @layer base | `button:not(:disabled), [role="button"]:not(:disabled), a[href]` | `@vvedantb/ui/tokens/css/base.css` |
228
+ | 268-270 | @layer base | `input[type="range"]` | `@vvedantb/ui/tokens/css/base.css` |
229
+ | 274-283 | @layer components | `.t-modal` | `@vvedantb/ui/tokens/css/modal.css` |
230
+ | 284-288 | @layer components | `.t-modal.is-open` | `@vvedantb/ui/tokens/css/modal.css` |
231
+ | 289-297 | @layer components | `.t-modal.is-closing` | `@vvedantb/ui/tokens/css/modal.css` |
232
+ | 299-306 | @layer components | `@keyframes t-modal-presence-wait` | `@vvedantb/ui/tokens/css/modal.css` |
233
+ | 362-366 | @layer components > @media (max-width: 639px) | `.t-tabs` | `@vvedantb/ui/tokens/css/tabs.css` |
234
+ | 367-369 | @layer components > @media (max-width: 639px) | `.t-tabs::-webkit-scrollbar` | `@vvedantb/ui/tokens/css/tabs.css` |
235
+ | 371-383 | @layer components | `.t-tab` | `@vvedantb/ui/tokens/css/tabs.css` |
236
+ | 384-388 | @layer components | `.t-tab:not([aria-selected="true"]):not([data-state="active"]):hover, .t-tab[aria-selected="true"], .t-tab[data-state="active"]` | `@vvedantb/ui/tokens/css/tabs.css` |
237
+ | 389-406 | @layer components | `.t-tabs-pill` | `@vvedantb/ui/tokens/css/tabs.css` |
238
+ | 427-431 | @layer utilities > @supports not selector(::-webkit-scrollbar) | `.scrollbar-thin` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
239
+ | 433-436 | @layer utilities > @supports not selector(::-webkit-scrollbar) | `.scrollbar-thin:hover` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
240
+ | 439-441 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
241
+ | 443-445 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar-track` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
242
+ | 447-450 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar-thumb` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
243
+ | 452-454 | @layer utilities | `.scrollbar-thin::-webkit-scrollbar-thumb:hover` | `@vvedantb/ui/tokens/css/scrollbar-thin.css` |
244
+
245
+ ### `apps/web/tailwind.config.ts` (-138 +2)
246
+
247
+ Edits (original lines -> replacement):
248
+
249
+ - Lines 2-2 become:
250
+
251
+ ```ts
252
+ import type { Config } from "tailwindcss";
253
+ import { tokensPreset } from "@vvedantb/ui/tokens/tailwind-preset";
254
+ ```
255
+ - Lines 9-9 become:
256
+
257
+ ```ts
258
+ presets: [tokensPreset()],
259
+ ```
260
+ - Lines 24-27: delete.
261
+ - Lines 31-51: delete.
262
+ - Lines 57-105: delete.
263
+ - Lines 107-112: delete.
264
+ - Lines 116-123: delete.
265
+ - Lines 135-142: delete.
266
+ - Lines 144-181: delete.
267
+ - Lines 198-200: delete.
268
+
269
+ ## app-e
270
+
271
+ Clone HEAD `7077eaf9ff8a4b2e9ac76c4f3b5ec712669b061f`. Shared files: `tabs-pills.css`, `dialog.css`, `modal.css`, `tabs.css`.
272
+
273
+ ### `apps/web/src/globals.css` (-132 +6)
274
+
275
+ Add (each `@import` replaces the block at that position):
276
+
277
+ ```css
278
+ @import "@vvedantb/ui/tokens/css/tabs-pills.css"; /* before original line 57 */
279
+ @import "@vvedantb/ui/tokens/css/dialog.css"; /* before original line 86 */
280
+ @import "@vvedantb/ui/tokens/css/modal.css"; /* before original line 95 */
281
+ @import "@vvedantb/ui/tokens/css/tabs.css"; /* before original line 95 */
282
+ ```
283
+
284
+ Delete:
285
+
286
+ | Lines | Context | Selector | Replaced by |
287
+ | --- | --- | --- | --- |
288
+ | 58-68 | @layer base | `:root, .light, .default, [data-theme="light"], [data-theme="default"]` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
289
+ | 70-77 | @layer base | `.dark, [data-theme="dark"]` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
290
+ | 86-93 | (top level) | `[role="dialog"][data-compact="true"], [role="alertdialog"][data-compact="true"]` | `@vvedantb/ui/tokens/css/dialog.css` |
291
+ | 96-105 | @layer components | `.t-modal` | `@vvedantb/ui/tokens/css/modal.css` |
292
+ | 106-110 | @layer components | `.t-modal.is-open` | `@vvedantb/ui/tokens/css/modal.css` |
293
+ | 111-119 | @layer components | `.t-modal.is-closing` | `@vvedantb/ui/tokens/css/modal.css` |
294
+ | 121-128 | @layer components | `@keyframes t-modal-presence-wait` | `@vvedantb/ui/tokens/css/modal.css` |
295
+ | 136-141 | @layer components | `.t-tabs--segmented` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
296
+ | 142-146 | @layer components | `.t-tabs--pills` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
297
+ | 148-152 | @layer components > @media (max-width: 639px) | `.t-tabs` | `@vvedantb/ui/tokens/css/tabs.css` |
298
+ | 153-155 | @layer components > @media (max-width: 639px) | `.t-tabs::-webkit-scrollbar` | `@vvedantb/ui/tokens/css/tabs.css` |
299
+ | 157-169 | @layer components | `.t-tab` | `@vvedantb/ui/tokens/css/tabs.css` |
300
+ | 170-174 | @layer components | `.t-tab:not([aria-selected="true"]):not([data-state="active"]):hover, .t-tab[aria-selected="true"], .t-tab[data-state="active"]` | `@vvedantb/ui/tokens/css/tabs.css` |
301
+ | 185-192 | @layer components | `.t-tabs--pills .t-tab:not([aria-selected="true"]):not([data-state="active"]):hover` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
302
+ | 193-196 | @layer components | `.t-tabs--pills .t-tab[aria-selected="true"], .t-tabs--pills .t-tab[data-state="active"]` | `@vvedantb/ui/tokens/css/tabs-pills.css` |
303
+ | 197-214 | @layer components | `.t-tabs-pill` | `@vvedantb/ui/tokens/css/tabs.css` |
@@ -0,0 +1,55 @@
1
+ /*
2
+ * @vvedantb/ui/tokens/css/animations.css: `animate-aurora`, `animate-fade-in-up`
3
+ * and `animate-indeterminate` for CSS-first apps (`@theme`, no config).
4
+ * Apps that load a tailwind.config through `@config` get the same values
5
+ * from `@vvedantb/ui/tokens/tailwind-preset`; use one or the other, not both.
6
+ */
7
+
8
+ @theme {
9
+ --animate-aurora: aurora 8s ease-in-out infinite alternate;
10
+ --animate-fade-in-up: fade-in-up 0.4s ease-out forwards;
11
+ --animate-indeterminate: indeterminate 1.5s ease-in-out infinite;
12
+
13
+ @keyframes aurora {
14
+ 0% {
15
+ background-position: 0% 50%;
16
+ transform: rotate(-5deg) scale(0.9);
17
+ }
18
+ 25% {
19
+ background-position: 50% 100%;
20
+ transform: rotate(5deg) scale(1.1);
21
+ }
22
+ 50% {
23
+ background-position: 100% 50%;
24
+ transform: rotate(-3deg) scale(0.95);
25
+ }
26
+ 75% {
27
+ background-position: 50% 0%;
28
+ transform: rotate(3deg) scale(1.05);
29
+ }
30
+ 100% {
31
+ background-position: 0% 50%;
32
+ transform: rotate(-5deg) scale(0.9);
33
+ }
34
+ }
35
+
36
+ @keyframes fade-in-up {
37
+ 0% {
38
+ opacity: 0;
39
+ transform: translateY(10px);
40
+ }
41
+ 100% {
42
+ opacity: 1;
43
+ transform: translateY(0);
44
+ }
45
+ }
46
+
47
+ @keyframes indeterminate {
48
+ 0% {
49
+ transform: translateX(-100%);
50
+ }
51
+ 100% {
52
+ transform: translateX(400%);
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,51 @@
1
+ /*
2
+ * @vvedantb/ui/tokens/css/base.css: element defaults shared by the web apps.
3
+ * Needs --accent and --foreground (tokens.css or the app theme).
4
+ *
5
+ * Parameters (set on :root to change; defaults shown):
6
+ * --tokens-control-transition-property: background-color, border-color,
7
+ * color, box-shadow, transform, opacity
8
+ * --tokens-control-transition-duration: 240ms
9
+ */
10
+
11
+ @layer base {
12
+ html {
13
+ touch-action: manipulation;
14
+ accent-color: var(--accent);
15
+ }
16
+
17
+ ::selection {
18
+ background-color: color-mix(in oklch, var(--accent) 18%, transparent);
19
+ color: var(--foreground);
20
+ }
21
+
22
+ button,
23
+ a,
24
+ input,
25
+ textarea,
26
+ [role="button"] {
27
+ transition-property: var(
28
+ --tokens-control-transition-property,
29
+ background-color,
30
+ border-color,
31
+ color,
32
+ box-shadow,
33
+ transform,
34
+ opacity
35
+ );
36
+ transition-duration: var(--tokens-control-transition-duration, 240ms);
37
+ transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
38
+ }
39
+
40
+ /* Tailwind v4 preflight sets `cursor: default` on buttons. Restore the
41
+ pointer for enabled interactive controls. */
42
+ button:not(:disabled),
43
+ [role="button"]:not(:disabled),
44
+ a[href] {
45
+ cursor: pointer;
46
+ }
47
+
48
+ input[type="range"] {
49
+ accent-color: var(--accent);
50
+ }
51
+ }
@@ -0,0 +1,75 @@
1
+ /*
2
+ * @vvedantb/ui/tokens/css/compat-shadcn-rgb.css
3
+ *
4
+ * For apps that already define shadcn-style variables as space-separated RGB
5
+ * channels (for example `--background: 244 245 246`) and read them with
6
+ * `rgb(var(--x) / alpha)`.
7
+ *
8
+ * Import this file INSTEAD OF tokens.css. Never import both: they define the
9
+ * same `--color-*` names in different forms.
10
+ *
11
+ * @import "tailwindcss";
12
+ * @import "@vvedantb/ui/tokens/css/compat-shadcn-rgb.css";
13
+ *
14
+ * It defines only the names that @vvedantb/ui components use and that a
15
+ * shadcn theme lacks. Every value is a reference to an app variable; no
16
+ * colour is copied. It never defines --background, --foreground, --muted,
17
+ * --accent, --primary, --card, --popover, --border or --radius. The app owns
18
+ * those and may change them at runtime.
19
+ *
20
+ * The app must define: --background, --foreground, --card, --card-foreground,
21
+ * --popover, --popover-foreground, --primary, --primary-foreground,
22
+ * --secondary, --secondary-foreground, --muted, --muted-foreground,
23
+ * --accent, --accent-foreground, --destructive, --destructive-foreground,
24
+ * --border, --input, --ring and --radius (all colours as "R G B").
25
+ *
26
+ * Alpha values follow common shadcn-RGB classes (ring/35, ring/50,
27
+ * destructive/20, placeholder /90, scrollbar /35).
28
+ */
29
+
30
+ @theme inline {
31
+ --color-default: rgb(var(--secondary));
32
+ --color-default-foreground: rgb(var(--secondary-foreground));
33
+ --color-surface: rgb(var(--card));
34
+ --color-surface-foreground: rgb(var(--card-foreground));
35
+ --color-surface-card: rgb(var(--card));
36
+ --color-surface-secondary: rgb(var(--muted));
37
+ --color-surface-secondary-foreground: rgb(var(--foreground));
38
+ --color-surface-tertiary: rgb(var(--secondary));
39
+ --color-surface-tertiary-foreground: rgb(var(--secondary-foreground));
40
+ --color-overlay: rgb(var(--popover));
41
+ --color-overlay-foreground: rgb(var(--popover-foreground));
42
+ --color-field-background: rgb(var(--card));
43
+ --color-field-foreground: rgb(var(--foreground));
44
+ --color-field-border: rgb(var(--input));
45
+ --color-field-placeholder: rgb(var(--muted-foreground) / 0.9);
46
+ --color-focus: rgb(var(--ring));
47
+ --color-focus-ring: rgb(var(--ring) / 0.35);
48
+ --color-focus-border: rgb(var(--ring) / 0.5);
49
+ --color-danger: rgb(var(--destructive));
50
+ --color-danger-foreground: rgb(var(--destructive-foreground));
51
+ --color-danger-ring: rgb(var(--destructive) / 0.2);
52
+ --color-danger-border: rgb(var(--destructive) / 0.55);
53
+ --color-separator: rgb(var(--border));
54
+ --color-segment: rgb(var(--card));
55
+ --color-segment-foreground: rgb(var(--foreground));
56
+ --color-backdrop: rgb(0 0 0 / 0.5);
57
+ --color-scrollbar: rgb(var(--muted-foreground) / 0.35);
58
+ --radius-field: min(calc(var(--radius) + 0.25rem), 1.25rem);
59
+ }
60
+
61
+ :root {
62
+ --scrollbar: rgb(var(--muted-foreground) / 0.35);
63
+ --field-radius: min(calc(var(--radius) + 0.25rem), 1.25rem);
64
+ }
65
+
66
+ /*
67
+ * Fallback fill for toasts, dialogs and hover cards (`glass-panel-strong`).
68
+ * Equivalent to `bg-card/95 backdrop-blur-md`. An app may override it by
69
+ * declaring its own `@utility glass-panel-strong` after this import.
70
+ */
71
+ @utility glass-panel-strong {
72
+ background-color: rgb(var(--card) / 0.95);
73
+ -webkit-backdrop-filter: blur(12px);
74
+ backdrop-filter: blur(12px);
75
+ }
@@ -0,0 +1,13 @@
1
+ /*
2
+ * @vvedantb/ui/tokens/css/dialog.css
3
+ *
4
+ * @vvedantb/ui >=0.1.5 sets data-compact="true" on DialogContent at
5
+ * max-width 639px or max-height 500px and renders it as a bottom sheet.
6
+ * Per-dialog max-w-* utilities must not shrink or left-align that sheet.
7
+ * Unlayered on purpose, so it wins over Tailwind utilities.
8
+ */
9
+
10
+ [role="dialog"][data-compact="true"],
11
+ [role="alertdialog"][data-compact="true"] {
12
+ max-width: none;
13
+ }
@@ -0,0 +1,19 @@
1
+ /*
2
+ * @vvedantb/ui/tokens/css/index.css: every shared file that needs only the
3
+ * variables tokens.css defines. For new apps. Import after tokens.css:
4
+ *
5
+ * @import "tailwindcss";
6
+ * @import "@vvedantb/ui/tokens";
7
+ * @import "@vvedantb/ui/tokens/css/index.css";
8
+ *
9
+ * modal.css, tabs.css and tabs-pills.css need app motion variables
10
+ * (--modal-*, --tabs-dur, --tabs-ease) and are not included. Apps that use
11
+ * the tailwind preset must not import animations.css; import the other
12
+ * files one by one instead.
13
+ */
14
+
15
+ @import "./base.css";
16
+ @import "./dialog.css";
17
+ @import "./utilities.css";
18
+ @import "./scrollbar-thin.css";
19
+ @import "./animations.css";
@@ -0,0 +1,44 @@
1
+ /*
2
+ * @vvedantb/ui/tokens/css/modal.css: .t-modal open / close transition used by
3
+ * @vvedantb/ui Dialog (transitions.dev pattern).
4
+ *
5
+ * Needs on :root: --modal-open-dur, --modal-close-dur, --modal-scale,
6
+ * --modal-scale-close, --modal-ease.
7
+ */
8
+
9
+ @layer components {
10
+ .t-modal {
11
+ transform-origin: center;
12
+ transform: translate(-50%, -50%) scale(var(--modal-scale));
13
+ opacity: 0;
14
+ pointer-events: none;
15
+ transition:
16
+ transform var(--modal-open-dur) var(--modal-ease),
17
+ opacity var(--modal-open-dur) var(--modal-ease);
18
+ will-change: transform, opacity;
19
+ }
20
+ .t-modal.is-open {
21
+ transform: translate(-50%, -50%) scale(1);
22
+ opacity: 1;
23
+ pointer-events: auto;
24
+ }
25
+ .t-modal.is-closing {
26
+ transform: translate(-50%, -50%) scale(var(--modal-scale-close));
27
+ opacity: 0;
28
+ pointer-events: none;
29
+ transition:
30
+ transform var(--modal-close-dur) var(--modal-ease),
31
+ opacity var(--modal-close-dur) var(--modal-ease);
32
+ /* Radix Presence waits on animationend, not transitionend */
33
+ animation: t-modal-presence-wait var(--modal-close-dur) linear forwards;
34
+ }
35
+
36
+ @keyframes t-modal-presence-wait {
37
+ from {
38
+ clip-path: inset(0);
39
+ }
40
+ to {
41
+ clip-path: inset(0);
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,40 @@
1
+ /*
2
+ * @vvedantb/ui/tokens/css/scrollbar-thin.css: `.scrollbar-thin` (always emitted,
3
+ * plain @layer utilities). Needs --scrollbar.
4
+ *
5
+ * Standard scrollbar properties are ignored by Chrome 121+ when webkit rules
6
+ * exist, so the Firefox fallback is gated with @supports. Chrome gets the
7
+ * 6px thumb; Firefox gets thin themed.
8
+ */
9
+
10
+ @layer utilities {
11
+ @supports not selector(::-webkit-scrollbar) {
12
+ .scrollbar-thin {
13
+ scrollbar-width: thin;
14
+ scrollbar-color: color-mix(in oklch, var(--scrollbar) 80%, transparent)
15
+ transparent;
16
+ }
17
+
18
+ .scrollbar-thin:hover {
19
+ scrollbar-color: color-mix(in oklch, var(--scrollbar) 100%, transparent)
20
+ transparent;
21
+ }
22
+ }
23
+
24
+ .scrollbar-thin::-webkit-scrollbar {
25
+ width: 6px;
26
+ }
27
+
28
+ .scrollbar-thin::-webkit-scrollbar-track {
29
+ background: transparent;
30
+ }
31
+
32
+ .scrollbar-thin::-webkit-scrollbar-thumb {
33
+ background-color: color-mix(in oklch, var(--scrollbar) 80%, transparent);
34
+ border-radius: 3px;
35
+ }
36
+
37
+ .scrollbar-thin::-webkit-scrollbar-thumb:hover {
38
+ background-color: color-mix(in oklch, var(--scrollbar) 100%, transparent);
39
+ }
40
+ }
@@ -0,0 +1,58 @@
1
+ /*
2
+ * @vvedantb/ui/tokens/css/tabs-pills.css: tab colour variables plus the
3
+ * .t-tabs--segmented (connected bar) and .t-tabs--pills (isolated pills)
4
+ * variants. The `.t-tabs--pills .t-tab` sizing rule stays in the app.
5
+ *
6
+ * Needs: --muted, --segment, --segment-foreground, --default (tokens.css).
7
+ */
8
+
9
+ @layer base {
10
+ :root,
11
+ .light,
12
+ .default,
13
+ [data-theme="light"],
14
+ [data-theme="default"] {
15
+ --tabs-text-muted: var(--muted);
16
+ --tabs-text-active: var(--segment-foreground);
17
+ --tabs-bar-bg: var(--default);
18
+ --tabs-pill-bg: var(--segment);
19
+ --tabs-pill-active-bg: var(--default);
20
+ }
21
+
22
+ .dark,
23
+ [data-theme="dark"] {
24
+ --tabs-text-muted: var(--muted);
25
+ --tabs-text-active: var(--segment-foreground);
26
+ --tabs-bar-bg: var(--default);
27
+ --tabs-pill-bg: var(--segment);
28
+ --tabs-pill-active-bg: var(--default);
29
+ }
30
+ }
31
+
32
+ @layer components {
33
+ /* Connected segmented bar with a sliding pill. */
34
+ .t-tabs--segmented {
35
+ gap: 3px;
36
+ padding: 3px;
37
+ border-radius: 48px;
38
+ background: var(--tabs-bar-bg);
39
+ }
40
+ /* Isolated pills. Each tab owns its outline; the selected tab is filled. */
41
+ .t-tabs--pills {
42
+ gap: 6px;
43
+ padding: 0;
44
+ background: transparent;
45
+ }
46
+ .t-tabs--pills
47
+ .t-tab:not([aria-selected="true"]):not([data-state="active"]):hover {
48
+ background: color-mix(
49
+ in oklch,
50
+ var(--tabs-pill-active-bg) 50%,
51
+ transparent
52
+ );
53
+ }
54
+ .t-tabs--pills .t-tab[aria-selected="true"],
55
+ .t-tabs--pills .t-tab[data-state="active"] {
56
+ background: var(--tabs-pill-active-bg);
57
+ }
58
+ }