@human-synthesis/norns-ui 0.0.3 → 0.0.5

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 (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -229
  3. package/package.json +7 -1
  4. package/src/auto-import.js +84 -19
  5. package/src/components/Accordion.n +38 -0
  6. package/src/components/Audio.n +24 -0
  7. package/src/components/Autocomplete.n +59 -0
  8. package/src/components/Avatar.n +27 -0
  9. package/src/components/AvatarGroup.n +25 -0
  10. package/src/components/Badge.n +20 -0
  11. package/src/components/Banner.n +25 -0
  12. package/src/components/Breadcrumbs.n +22 -0
  13. package/src/components/Btn.n +29 -8
  14. package/src/components/ButtonGroup.n +14 -0
  15. package/src/components/Calendar.n +153 -0
  16. package/src/components/Card.n +34 -0
  17. package/src/components/Carousel.n +66 -0
  18. package/src/components/Chip.n +27 -0
  19. package/src/components/Collapsible.n +32 -0
  20. package/src/components/ColorPicker.n +62 -0
  21. package/src/components/ContextMenu.n +48 -0
  22. package/src/components/CopyButton.n +50 -0
  23. package/src/components/DataTable.n +61 -0
  24. package/src/components/DatePicker.n +68 -0
  25. package/src/components/DateRangePicker.n +59 -0
  26. package/src/components/Drawer.n +49 -0
  27. package/src/components/GradientText.n +21 -0
  28. package/src/components/Header.n +23 -0
  29. package/src/components/HeroBanner.n +37 -0
  30. package/src/components/HierarchicalMenu.n +38 -0
  31. package/src/components/Icon.n +16 -0
  32. package/src/components/Image.n +31 -0
  33. package/src/components/MegaMenu.n +36 -0
  34. package/src/components/MultiSelect.n +56 -0
  35. package/src/components/NumberInput.n +49 -0
  36. package/src/components/OtpField.n +67 -0
  37. package/src/components/Pagination.n +73 -0
  38. package/src/components/PreviewCard.n +30 -0
  39. package/src/components/Progress.n +26 -0
  40. package/src/components/ProgressCircular.n +52 -0
  41. package/src/components/RichTooltip.n +33 -0
  42. package/src/components/RippleButton.n +56 -0
  43. package/src/components/ScrollArea.n +31 -0
  44. package/src/components/Separator.n +12 -0
  45. package/src/components/ShinyButton.n +36 -0
  46. package/src/components/Skeleton.n +27 -0
  47. package/src/components/Stepper.n +35 -0
  48. package/src/components/Surface.n +10 -0
  49. package/src/components/TagsInput.n +66 -0
  50. package/src/components/ThemeToggler.n +43 -0
  51. package/src/components/TimePicker.n +166 -0
  52. package/src/components/Timeline.n +27 -0
  53. package/src/components/ToggleButton.n +29 -0
  54. package/src/components/ToggleButtonGroup.n +34 -0
  55. package/src/components/Toolbar.n +15 -0
  56. package/src/components/Tree.n +57 -0
  57. package/src/components/Uploader.n +88 -0
  58. package/src/components/Video.n +26 -0
  59. package/src/index.js +66 -0
  60. package/src/motion/AnimatedNumber.n +55 -0
  61. package/src/motion/GradientBackground.n +25 -0
  62. package/src/motion/LiquidButton.n +48 -0
  63. package/src/motion/Reveal.n +37 -0
  64. package/src/motion/Sparkles.n +53 -0
  65. package/src/motion/index.js +18 -0
  66. package/src/styles/atoms.css +1673 -118
  67. package/src/styles/tokens.css +171 -40
  68. package/src/types/Accordion.d.ts +17 -0
  69. package/src/types/AnimatedNumber.d.ts +15 -0
  70. package/src/types/Audio.d.ts +18 -0
  71. package/src/types/Autocomplete.d.ts +17 -0
  72. package/src/types/Avatar.d.ts +14 -0
  73. package/src/types/AvatarGroup.d.ts +15 -0
  74. package/src/types/Badge.d.ts +14 -0
  75. package/src/types/Banner.d.ts +15 -0
  76. package/src/types/Breadcrumbs.d.ts +15 -0
  77. package/src/types/Btn.d.ts +10 -0
  78. package/src/types/ButtonGroup.d.ts +10 -0
  79. package/src/types/Calendar.d.ts +20 -0
  80. package/src/types/Card.d.ts +19 -0
  81. package/src/types/Carousel.d.ts +23 -0
  82. package/src/types/Chip.d.ts +12 -0
  83. package/src/types/Collapsible.d.ts +12 -0
  84. package/src/types/ColorPicker.d.ts +15 -0
  85. package/src/types/ContextMenu.d.ts +19 -0
  86. package/src/types/CopyButton.d.ts +16 -0
  87. package/src/types/DataTable.d.ts +27 -0
  88. package/src/types/DatePicker.d.ts +18 -0
  89. package/src/types/DateRangePicker.d.ts +17 -0
  90. package/src/types/Drawer.d.ts +18 -0
  91. package/src/types/GradientBackground.d.ts +14 -0
  92. package/src/types/GradientText.d.ts +19 -0
  93. package/src/types/Header.d.ts +12 -0
  94. package/src/types/HeroBanner.d.ts +17 -0
  95. package/src/types/HierarchicalMenu.d.ts +20 -0
  96. package/src/types/Icon.d.ts +21 -0
  97. package/src/types/Image.d.ts +15 -0
  98. package/src/types/LiquidButton.d.ts +15 -0
  99. package/src/types/MegaMenu.d.ts +29 -0
  100. package/src/types/MultiSelect.d.ts +13 -0
  101. package/src/types/NumberInput.d.ts +17 -0
  102. package/src/types/OtpField.d.ts +13 -0
  103. package/src/types/Pagination.d.ts +13 -0
  104. package/src/types/PreviewCard.d.ts +16 -0
  105. package/src/types/Progress.d.ts +14 -0
  106. package/src/types/ProgressCircular.d.ts +14 -0
  107. package/src/types/Reveal.d.ts +20 -0
  108. package/src/types/RichTooltip.d.ts +16 -0
  109. package/src/types/RippleButton.d.ts +15 -0
  110. package/src/types/ScrollArea.d.ts +11 -0
  111. package/src/types/Separator.d.ts +9 -0
  112. package/src/types/ShinyButton.d.ts +15 -0
  113. package/src/types/Skeleton.d.ts +13 -0
  114. package/src/types/Sparkles.d.ts +13 -0
  115. package/src/types/Stepper.d.ts +19 -0
  116. package/src/types/Surface.d.ts +10 -0
  117. package/src/types/TagsInput.d.ts +17 -0
  118. package/src/types/ThemeToggler.d.ts +17 -0
  119. package/src/types/TimePicker.d.ts +20 -0
  120. package/src/types/Timeline.d.ts +17 -0
  121. package/src/types/ToggleButton.d.ts +15 -0
  122. package/src/types/ToggleButtonGroup.d.ts +20 -0
  123. package/src/types/Toolbar.d.ts +12 -0
  124. package/src/types/Tree.d.ts +20 -0
  125. package/src/types/Uploader.d.ts +15 -0
  126. package/src/types/Video.d.ts +20 -0
@@ -1,26 +1,113 @@
1
1
  /*
2
2
  * Norns UI — component atom classes.
3
3
  *
4
- * Pure Tailwind utility-composition via `@layer components`. No JS, no Svelte
5
- * just classes consumers can use directly via Pug shorthand (`button.btn.btn-primary`)
6
- * or via the wrapper components (`<Btn variant="primary">`).
4
+ * Pure Tailwind utility-composition via `@layer components`. No JS, no Svelte.
5
+ * All atoms reference *role tokens* from tokens.css never raw color shades.
6
+ * That's how light/dark theme swap works through a single point of change.
7
7
  *
8
8
  * Naming convention: `<atom>-<variant>` and `<atom>-<size>`. AI-predictable.
9
9
  */
10
10
 
11
11
  @reference 'tailwindcss';
12
12
 
13
+ @layer base {
14
+ /* Body */
15
+ html {
16
+ font-family: var(--font-sans);
17
+ color: var(--color-fg);
18
+ background-color: var(--color-bg);
19
+ font-feature-settings: 'ss01';
20
+ }
21
+ body {
22
+ color: var(--color-fg);
23
+ background-color: var(--color-bg);
24
+ }
25
+
26
+ /* Reduced-motion: globally short-circuit our transitions/animations.
27
+ * Components using prefers-reduced-motion: reduce explicitly will get this.
28
+ */
29
+ @media (prefers-reduced-motion: reduce) {
30
+ *,
31
+ *::before,
32
+ *::after {
33
+ animation-duration: 0.01ms !important;
34
+ animation-iteration-count: 1 !important;
35
+ transition-duration: 0.01ms !important;
36
+ scroll-behavior: auto !important;
37
+ }
38
+ }
39
+
40
+ /* Heading hierarchy — applied to bare h1..h6 unless overridden via Tailwind. */
41
+ h1 {
42
+ @apply text-4xl font-bold tracking-tight leading-tight;
43
+ }
44
+ h2 {
45
+ @apply text-3xl font-semibold tracking-tight leading-tight;
46
+ }
47
+ h3 {
48
+ @apply text-2xl font-semibold tracking-tight leading-snug;
49
+ }
50
+ h4 {
51
+ @apply text-xl font-semibold leading-snug;
52
+ }
53
+ h5 {
54
+ @apply text-lg font-semibold leading-snug;
55
+ }
56
+ h6 {
57
+ @apply text-base font-semibold;
58
+ }
59
+
60
+ /* Inline code + pre */
61
+ code {
62
+ font-family: var(--font-mono);
63
+ @apply text-[0.9em] px-1 py-0.5 rounded;
64
+ background-color: var(--color-bg-muted);
65
+ color: var(--color-fg);
66
+ }
67
+ pre {
68
+ font-family: var(--font-mono);
69
+ @apply text-sm p-3 rounded overflow-x-auto;
70
+ background-color: var(--color-bg-muted);
71
+ color: var(--color-fg);
72
+ }
73
+ pre code {
74
+ @apply p-0 bg-transparent;
75
+ }
76
+
77
+ /* Keyboard */
78
+ kbd {
79
+ font-family: var(--font-mono);
80
+ @apply text-xs px-1.5 py-0.5 rounded border;
81
+ background-color: var(--color-bg-muted);
82
+ border-color: var(--color-border);
83
+ color: var(--color-fg);
84
+ }
85
+
86
+ /* Default form-control reset on dark */
87
+ hr {
88
+ border-color: var(--color-border);
89
+ }
90
+ }
91
+
13
92
  @layer components {
14
- /* --- Button --- */
93
+ /* ============================================================ */
94
+ /* Button */
95
+ /* ============================================================ */
15
96
  .btn {
16
- @apply inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium leading-none whitespace-nowrap;
17
- @apply transition-[background-color,color,box-shadow,opacity] duration-150 ease-out;
18
- @apply select-none cursor-pointer;
97
+ @apply inline-flex items-center justify-center gap-2 text-sm font-medium leading-none whitespace-nowrap;
98
+ @apply transition-[background-color,color,border-color,transform,opacity] duration-150 ease-out;
99
+ @apply select-none cursor-pointer relative overflow-hidden;
19
100
  @apply disabled:cursor-not-allowed disabled:opacity-60;
20
- @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-primary-500;
101
+ @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2;
102
+ --tw-ring-color: var(--color-ring);
103
+ --tw-ring-offset-color: var(--color-bg);
104
+ border-radius: var(--ui-radius);
21
105
  height: var(--ui-h-md);
22
106
  padding-inline: 0.875rem;
23
107
  }
108
+ .btn:not(:disabled):active {
109
+ transform: scale(0.97);
110
+ }
24
111
 
25
112
  .btn-sm {
26
113
  @apply text-xs;
@@ -35,211 +122,308 @@
35
122
  }
36
123
 
37
124
  .btn-primary {
38
- @apply bg-primary-600 text-white shadow-sm hover:bg-primary-700 active:bg-primary-800;
125
+ background-color: var(--color-primary-600);
126
+ color: var(--color-fg-on-primary);
127
+ }
128
+ .btn-primary:hover {
129
+ background-color: var(--color-primary-700);
130
+ }
131
+ .btn-primary:active {
132
+ background-color: var(--color-primary-800);
39
133
  }
40
134
 
41
135
  .btn-secondary {
42
- @apply bg-neutral-100 text-neutral-900 shadow-sm hover:bg-neutral-200 active:bg-neutral-300;
136
+ background-color: var(--color-bg-muted);
137
+ color: var(--color-fg);
138
+ border: 1px solid var(--color-border);
139
+ }
140
+ .btn-secondary:hover {
141
+ background-color: var(--color-bg-subtle);
142
+ }
143
+ .btn-secondary:active {
144
+ background-color: var(--color-border);
43
145
  }
44
146
 
45
147
  .btn-ghost {
46
- @apply bg-transparent text-neutral-700 hover:bg-neutral-100 active:bg-neutral-200;
148
+ background-color: transparent;
149
+ color: var(--color-fg-muted);
150
+ }
151
+ .btn-ghost:hover {
152
+ background-color: var(--color-bg-muted);
153
+ color: var(--color-fg);
47
154
  }
48
155
 
49
156
  .btn-danger {
50
- @apply bg-red-600 text-white shadow-sm hover:bg-red-700 active:bg-red-800 focus-visible:ring-red-500;
157
+ background-color: var(--color-danger-600);
158
+ color: white;
159
+ }
160
+ .btn-danger:hover {
161
+ background-color: var(--color-danger-700);
162
+ }
163
+ .btn-danger:active {
164
+ background-color: var(--color-danger-800);
165
+ }
166
+ .btn-danger:focus-visible {
167
+ --tw-ring-color: var(--color-danger-500);
51
168
  }
52
169
 
53
170
  .btn-link {
54
- @apply bg-transparent text-primary-600 underline-offset-4 hover:underline px-0;
171
+ @apply bg-transparent underline-offset-4 hover:underline px-0;
172
+ color: var(--color-primary-600);
55
173
  height: auto;
56
174
  }
175
+ [data-theme='dark'] .btn-link {
176
+ color: var(--color-primary-400);
177
+ }
57
178
 
58
- /* --- Spinner (used inside Btn loading state) --- */
179
+ /* ============================================================ */
180
+ /* Spinner (used inside Btn loading state) */
181
+ /* ============================================================ */
59
182
  .ui-spinner {
60
183
  @apply inline-block size-4 animate-spin rounded-full border-2 border-current border-t-transparent;
61
184
  }
62
185
 
63
- /* --- Text inputs --- */
186
+ /* ============================================================ */
187
+ /* Inputs */
188
+ /* ============================================================ */
64
189
  .input {
65
- @apply block w-full rounded-md border border-neutral-300 bg-white text-sm text-neutral-900 shadow-sm;
66
- @apply placeholder:text-neutral-400;
67
- @apply transition-[border-color,box-shadow] duration-150 ease-out;
68
- @apply focus:outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-500/30;
69
- @apply disabled:cursor-not-allowed disabled:bg-neutral-50 disabled:opacity-70;
190
+ @apply block w-full text-sm leading-none;
191
+ @apply transition-[border-color,background-color] duration-150 ease-out;
192
+ @apply focus:outline-none focus:ring-2;
193
+ @apply disabled:cursor-not-allowed disabled:opacity-70;
194
+ background-color: var(--color-bg);
195
+ color: var(--color-fg);
196
+ border: 1px solid var(--color-border-strong);
197
+ border-radius: var(--ui-radius);
70
198
  height: var(--ui-h-md);
71
199
  padding-inline: 0.75rem;
200
+ --tw-ring-color: color-mix(in oklab, var(--color-ring) 30%, transparent);
201
+ }
202
+ .input::placeholder {
203
+ color: var(--color-fg-subtle);
204
+ }
205
+ .input:focus {
206
+ border-color: var(--color-ring);
207
+ }
208
+ .input:disabled {
209
+ background-color: var(--color-bg-muted);
72
210
  }
73
-
74
211
  .input-sm {
75
212
  @apply text-xs;
76
213
  height: var(--ui-h-sm);
77
214
  padding-inline: 0.625rem;
78
215
  }
79
-
80
216
  .input-lg {
81
217
  @apply text-base;
82
218
  height: var(--ui-h-lg);
83
219
  padding-inline: 0.875rem;
84
220
  }
85
-
86
221
  .input-err {
87
- @apply border-red-500 focus:border-red-500 focus:ring-red-500/30;
222
+ border-color: var(--color-danger-500);
223
+ }
224
+ .input-err:focus {
225
+ border-color: var(--color-danger-500);
226
+ --tw-ring-color: color-mix(in oklab, var(--color-danger-500) 30%, transparent);
88
227
  }
89
228
 
90
- /* Textarea overrides .input's height */
229
+ /* Textarea */
91
230
  .textarea {
92
- @apply block w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm text-neutral-900 shadow-sm;
93
- @apply placeholder:text-neutral-400;
94
- @apply transition-[border-color,box-shadow] duration-150 ease-out;
95
- @apply focus:outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-500/30;
96
- @apply disabled:cursor-not-allowed disabled:bg-neutral-50 disabled:opacity-70;
231
+ @apply block w-full text-sm leading-snug px-3 py-2;
232
+ @apply transition-[border-color,background-color] duration-150 ease-out;
233
+ @apply focus:outline-none focus:ring-2;
234
+ @apply disabled:cursor-not-allowed disabled:opacity-70;
235
+ background-color: var(--color-bg);
236
+ color: var(--color-fg);
237
+ border: 1px solid var(--color-border-strong);
238
+ border-radius: var(--ui-radius);
97
239
  min-height: calc(var(--ui-h-md) * 2);
240
+ --tw-ring-color: color-mix(in oklab, var(--color-ring) 30%, transparent);
241
+ }
242
+ .textarea::placeholder {
243
+ color: var(--color-fg-subtle);
244
+ }
245
+ .textarea:focus {
246
+ border-color: var(--color-ring);
247
+ }
248
+ .textarea:disabled {
249
+ background-color: var(--color-bg-muted);
98
250
  }
99
-
100
251
  .textarea-err {
101
- @apply border-red-500 focus:border-red-500 focus:ring-red-500/30;
252
+ border-color: var(--color-danger-500);
253
+ }
254
+ .textarea-err:focus {
255
+ border-color: var(--color-danger-500);
256
+ --tw-ring-color: color-mix(in oklab, var(--color-danger-500) 30%, transparent);
102
257
  }
103
258
 
259
+ /* Select */
104
260
  .select {
105
- @apply block w-full rounded-md border border-neutral-300 bg-white text-sm text-neutral-900 shadow-sm;
106
- @apply transition-[border-color,box-shadow] duration-150 ease-out;
107
- @apply focus:outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-500/30;
108
- @apply disabled:cursor-not-allowed disabled:bg-neutral-50 disabled:opacity-70;
261
+ @apply block w-full text-sm leading-none;
262
+ @apply transition-[border-color,background-color] duration-150 ease-out;
263
+ @apply focus:outline-none focus:ring-2;
264
+ @apply disabled:cursor-not-allowed disabled:opacity-70;
265
+ background-color: var(--color-bg);
266
+ color: var(--color-fg);
267
+ border: 1px solid var(--color-border-strong);
268
+ border-radius: var(--ui-radius);
109
269
  height: var(--ui-h-md);
110
270
  padding-inline: 0.75rem;
271
+ --tw-ring-color: color-mix(in oklab, var(--color-ring) 30%, transparent);
272
+ }
273
+ .select:focus {
274
+ border-color: var(--color-ring);
275
+ }
276
+ .select:disabled {
277
+ background-color: var(--color-bg-muted);
111
278
  }
112
-
113
279
  .select-err {
114
- @apply border-red-500 focus:border-red-500 focus:ring-red-500/30;
280
+ border-color: var(--color-danger-500);
115
281
  }
116
282
 
117
- /* --- Checkbox / Radio --- */
283
+ /* Checkbox / Radio */
118
284
  .checkbox {
119
- @apply size-4 cursor-pointer rounded border-neutral-300 text-primary-600 shadow-sm;
120
- @apply focus:outline-none focus:ring-2 focus:ring-primary-500/30 focus:ring-offset-1;
285
+ @apply size-4 cursor-pointer rounded;
286
+ @apply focus:outline-none focus:ring-2 focus:ring-offset-1;
121
287
  @apply disabled:cursor-not-allowed disabled:opacity-60;
288
+ border: 1px solid var(--color-border-strong);
289
+ accent-color: var(--color-primary-600);
290
+ --tw-ring-color: color-mix(in oklab, var(--color-ring) 30%, transparent);
291
+ --tw-ring-offset-color: var(--color-bg);
122
292
  }
123
-
124
293
  .checkbox-err {
125
- @apply border-red-500 focus:ring-red-500/30;
294
+ border-color: var(--color-danger-500);
126
295
  }
127
-
128
296
  .radio {
129
- @apply size-4 cursor-pointer border-neutral-300 text-primary-600 shadow-sm;
130
- @apply focus:outline-none focus:ring-2 focus:ring-primary-500/30 focus:ring-offset-1;
297
+ @apply size-4 cursor-pointer rounded-full;
298
+ @apply focus:outline-none focus:ring-2 focus:ring-offset-1;
131
299
  @apply disabled:cursor-not-allowed disabled:opacity-60;
300
+ border: 1px solid var(--color-border-strong);
301
+ accent-color: var(--color-primary-600);
302
+ --tw-ring-color: color-mix(in oklab, var(--color-ring) 30%, transparent);
303
+ --tw-ring-offset-color: var(--color-bg);
132
304
  }
133
-
134
305
  .radio-err {
135
- @apply border-red-500 focus:ring-red-500/30;
306
+ border-color: var(--color-danger-500);
136
307
  }
137
308
 
138
- /* --- Switch (styled checkbox / toggle) ---
139
- * Implemented as a checkbox the user wraps in a label; we provide the
140
- * pill track via .switch and the moving knob via ::before.
141
- */
309
+ /* Switch */
142
310
  .switch {
143
- @apply relative inline-flex h-5 w-9 shrink-0 cursor-pointer appearance-none rounded-full bg-neutral-200 shadow-inner;
311
+ @apply relative inline-flex h-5 w-9 shrink-0 cursor-pointer appearance-none rounded-full;
144
312
  @apply transition-colors duration-150 ease-out;
145
- @apply checked:bg-primary-600;
146
- @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/30 focus-visible:ring-offset-1;
313
+ @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1;
147
314
  @apply disabled:cursor-not-allowed disabled:opacity-60;
315
+ background-color: var(--color-border-strong);
316
+ --tw-ring-color: color-mix(in oklab, var(--color-ring) 30%, transparent);
317
+ --tw-ring-offset-color: var(--color-bg);
318
+ }
319
+ .switch:checked {
320
+ background-color: var(--color-primary-600);
148
321
  }
149
322
  .switch::before {
150
323
  content: '';
151
- @apply absolute left-0.5 top-0.5 size-4 rounded-full bg-white shadow;
324
+ @apply absolute left-0.5 top-0.5 size-4 rounded-full;
152
325
  @apply transition-transform duration-150 ease-out;
326
+ background-color: white;
153
327
  }
154
328
  .switch:checked::before {
155
329
  transform: translateX(1rem);
156
330
  }
157
331
 
158
- /* --- Field (label + control + help/error) --- */
332
+ /* ============================================================ */
333
+ /* Field */
334
+ /* ============================================================ */
159
335
  .field {
160
336
  @apply flex flex-col;
161
337
  }
162
-
163
338
  .field-label {
164
- @apply mb-1 block text-sm font-medium text-neutral-700;
339
+ @apply mb-1 block text-sm font-medium;
340
+ color: var(--color-fg);
165
341
  }
166
-
167
342
  .field-required {
168
- @apply ml-0.5 text-red-600;
343
+ @apply ml-0.5;
344
+ color: var(--color-danger-600);
169
345
  }
170
-
171
346
  .field-help {
172
- @apply mt-1 text-xs text-neutral-500;
347
+ @apply mt-1 text-xs;
348
+ color: var(--color-fg-muted);
173
349
  }
174
-
175
350
  .field-error {
176
- @apply mt-1 text-xs text-red-600;
351
+ @apply mt-1 text-xs;
352
+ color: var(--color-danger-600);
177
353
  }
178
-
179
354
  .field-row {
180
355
  @apply flex items-center gap-2;
181
356
  }
182
357
 
183
- /* Field-group: border-grouped collection of fields */
184
358
  .field-group {
185
- @apply space-y-3 rounded-md border border-neutral-200 p-4;
359
+ @apply space-y-3 p-4;
360
+ border: 1px solid var(--color-border);
361
+ border-radius: var(--ui-radius);
186
362
  }
187
-
188
363
  .field-group-legend {
189
- @apply -mx-1 -mt-1 px-1 text-sm font-medium text-neutral-700;
364
+ @apply -mx-1 -mt-1 px-1 text-sm font-medium;
365
+ color: var(--color-fg);
190
366
  }
191
367
 
192
- /* --- Form (vertical stack of fields) --- */
368
+ /* Form */
193
369
  .form {
194
370
  @apply space-y-4;
195
371
  }
196
372
 
197
- /* --- Dialog (modal) ---
198
- * Bits UI toggles `data-state="open|closed"`. We use plain CSS transitions
199
- * targeting that attribute for fade + scale; no `tailwindcss-animate`
200
- * dependency, no v4 plugin.
201
- */
373
+ /* ============================================================ */
374
+ /* Dialog */
375
+ /* ============================================================ */
202
376
  .dialog-overlay {
203
- @apply fixed inset-0 z-50 bg-black/50 backdrop-blur-sm opacity-0 transition-opacity duration-150;
377
+ @apply fixed inset-0 z-50 backdrop-blur-sm opacity-0 transition-opacity duration-150;
378
+ background-color: oklch(0% 0 0 / 0.5);
204
379
  }
205
380
  .dialog-overlay[data-state='open'] {
206
381
  @apply opacity-100;
207
382
  }
208
-
209
383
  .dialog-content {
210
- @apply fixed left-1/2 top-1/2 z-50 w-full max-w-lg -translate-x-1/2 -translate-y-1/2;
211
- @apply rounded-lg border border-neutral-200 bg-white p-6 shadow-xl;
212
- @apply opacity-0 scale-95 transition duration-150;
384
+ @apply fixed left-1/2 top-1/2 z-50 w-full max-w-lg -translate-x-1/2 -translate-y-1/2 p-6;
385
+ @apply opacity-0 scale-95 transition duration-150 shadow-xl;
386
+ background-color: var(--color-bg-elevated);
387
+ color: var(--color-fg);
388
+ border: 1px solid var(--color-border);
389
+ border-radius: var(--ui-radius-lg);
213
390
  }
214
391
  .dialog-content[data-state='open'] {
215
392
  @apply opacity-100 scale-100;
216
393
  }
217
-
218
394
  .dialog-title {
219
- @apply text-lg font-semibold leading-tight tracking-tight text-neutral-900;
395
+ @apply text-lg font-semibold leading-tight tracking-tight;
396
+ color: var(--color-fg);
220
397
  }
221
-
222
398
  .dialog-description {
223
- @apply mt-1 text-sm text-neutral-600;
399
+ @apply mt-1 text-sm;
400
+ color: var(--color-fg-muted);
224
401
  }
225
-
226
402
  .dialog-body {
227
403
  @apply mt-4;
228
404
  }
229
-
230
405
  .dialog-actions {
231
406
  @apply mt-6 flex items-center justify-end gap-2;
232
407
  }
233
-
234
408
  .dialog-close {
235
- @apply absolute right-3 top-3 inline-flex size-7 items-center justify-center rounded-md text-neutral-500;
236
- @apply hover:bg-neutral-100 hover:text-neutral-900;
237
- @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500;
409
+ @apply absolute right-3 top-3 inline-flex size-7 items-center justify-center rounded-md;
410
+ @apply focus-visible:outline-none focus-visible:ring-2;
411
+ color: var(--color-fg-muted);
412
+ --tw-ring-color: var(--color-ring);
413
+ }
414
+ .dialog-close:hover {
415
+ background-color: var(--color-bg-muted);
416
+ color: var(--color-fg);
238
417
  }
239
418
 
240
- /* --- Sheet (side panel) --- */
419
+ /* ============================================================ */
420
+ /* Sheet */
421
+ /* ============================================================ */
241
422
  .sheet-content {
242
- @apply fixed z-50 border border-neutral-200 bg-white shadow-xl transition-transform duration-200 ease-out;
423
+ @apply fixed z-50 transition-transform duration-200 ease-out shadow-xl;
424
+ background-color: var(--color-bg-elevated);
425
+ color: var(--color-fg);
426
+ border: 1px solid var(--color-border);
243
427
  }
244
428
  .sheet-right {
245
429
  @apply right-0 top-0 h-full w-full max-w-md translate-x-full p-6;
@@ -265,70 +449,102 @@
265
449
  .sheet-bottom[data-state='open'] {
266
450
  @apply translate-y-0;
267
451
  }
268
-
269
452
  .sheet-body {
270
453
  @apply mt-4;
271
454
  }
272
455
 
273
- /* --- Popover --- */
456
+ /* ============================================================ */
457
+ /* Popover / Dropdown / Tooltip */
458
+ /* ============================================================ */
274
459
  .popover-content {
275
- @apply z-50 rounded-md border border-neutral-200 bg-white p-3 text-sm shadow-md;
460
+ @apply z-50 p-3 text-sm shadow-md;
276
461
  @apply opacity-0 scale-95 transition duration-150;
462
+ background-color: var(--color-bg-elevated);
463
+ color: var(--color-fg);
464
+ border: 1px solid var(--color-border);
465
+ border-radius: var(--ui-radius);
277
466
  }
278
467
  .popover-content[data-state='open'] {
279
468
  @apply opacity-100 scale-100;
280
469
  }
281
470
 
282
- /* --- Dropdown menu --- */
283
471
  .dropdown-content {
284
- @apply z-50 min-w-[8rem] rounded-md border border-neutral-200 bg-white p-1 text-sm shadow-md;
472
+ @apply z-50 min-w-[8rem] p-1 text-sm shadow-md;
285
473
  @apply opacity-0 scale-95 transition duration-150;
474
+ background-color: var(--color-bg-elevated);
475
+ color: var(--color-fg);
476
+ border: 1px solid var(--color-border);
477
+ border-radius: var(--ui-radius);
286
478
  }
287
479
  .dropdown-content[data-state='open'] {
288
480
  @apply opacity-100 scale-100;
289
481
  }
290
482
  .dropdown-item {
291
- @apply flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm text-neutral-800 outline-none;
292
- @apply data-[highlighted]:bg-neutral-100 data-[highlighted]:text-neutral-900;
293
- @apply data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50;
483
+ @apply flex cursor-pointer select-none items-center px-2 py-1.5 text-sm outline-none rounded-sm;
484
+ color: var(--color-fg);
485
+ }
486
+ .dropdown-item[data-highlighted] {
487
+ background-color: var(--color-bg-muted);
488
+ }
489
+ .dropdown-item[data-disabled] {
490
+ @apply cursor-not-allowed opacity-50;
294
491
  }
295
492
  .dropdown-separator {
296
- @apply -mx-1 my-1 h-px bg-neutral-200;
493
+ @apply -mx-1 my-1 h-px;
494
+ background-color: var(--color-border);
297
495
  }
298
496
 
299
- /* --- Tooltip --- */
300
497
  .tooltip-content {
301
- @apply z-50 overflow-hidden rounded-md bg-neutral-900 px-2 py-1 text-xs text-white shadow-md;
498
+ @apply z-50 overflow-hidden px-2 py-1 text-xs shadow-md;
302
499
  @apply opacity-0 scale-95 transition duration-150;
500
+ background-color: var(--color-bg-inverse);
501
+ color: var(--color-bg);
502
+ border-radius: var(--ui-radius-sm);
303
503
  }
304
504
  .tooltip-content[data-state='delayed-open'],
305
505
  .tooltip-content[data-state='instant-open'] {
306
506
  @apply opacity-100 scale-100;
307
507
  }
308
508
 
309
- /* --- Tabs --- */
509
+ /* ============================================================ */
510
+ /* Tabs */
511
+ /* ============================================================ */
310
512
  .tabs-root {
311
513
  @apply space-y-3;
312
514
  }
313
515
  .tabs-list {
314
- @apply inline-flex h-9 items-center justify-center rounded-md bg-neutral-100 p-1 text-neutral-600;
516
+ @apply inline-flex h-9 items-center justify-center p-1;
517
+ background-color: var(--color-bg-muted);
518
+ color: var(--color-fg-muted);
519
+ border-radius: var(--ui-radius);
315
520
  }
316
521
  .tabs-trigger {
317
- @apply inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1 text-sm font-medium;
318
- @apply transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500;
319
- @apply data-[state=active]:bg-white data-[state=active]:text-neutral-900 data-[state=active]:shadow-sm;
522
+ @apply inline-flex items-center justify-center whitespace-nowrap px-3 py-1 text-sm font-medium rounded-sm;
523
+ @apply transition-colors focus-visible:outline-none focus-visible:ring-2;
320
524
  @apply disabled:cursor-not-allowed disabled:opacity-50;
525
+ color: var(--color-fg-muted);
526
+ --tw-ring-color: var(--color-ring);
527
+ }
528
+ .tabs-trigger[data-state='active'] {
529
+ background-color: var(--color-bg-elevated);
530
+ color: var(--color-fg);
321
531
  }
322
532
  .tabs-content {
323
533
  @apply focus-visible:outline-none;
324
534
  }
325
535
 
326
- /* --- Toast --- */
536
+ /* ============================================================ */
537
+ /* Toast */
538
+ /* ============================================================ */
327
539
  .toast-region {
328
540
  @apply pointer-events-none fixed bottom-4 right-4 z-[100] flex w-full max-w-sm flex-col gap-2;
329
541
  }
330
542
  .toast {
331
- @apply pointer-events-auto flex items-start gap-3 rounded-md border border-neutral-200 bg-white p-3 shadow-md;
543
+ @apply pointer-events-auto flex items-start gap-3 p-3 shadow-md;
544
+ background-color: var(--color-bg-elevated);
545
+ color: var(--color-fg);
546
+ border: 1px solid var(--color-border);
547
+ border-radius: var(--ui-radius);
332
548
  animation: norns-toast-in 200ms ease-out;
333
549
  }
334
550
  @keyframes norns-toast-in {
@@ -342,23 +558,1362 @@
342
558
  }
343
559
  }
344
560
  .toast-info {
345
- @apply border-neutral-200;
561
+ background-color: var(--color-info-bg);
562
+ color: var(--color-info-fg);
563
+ border-color: var(--color-info-border);
346
564
  }
347
565
  .toast-success {
348
- @apply border-green-200 bg-green-50 text-green-900;
566
+ background-color: var(--color-success-bg);
567
+ color: var(--color-success-fg);
568
+ border-color: var(--color-success-border);
349
569
  }
350
570
  .toast-warning {
351
- @apply border-amber-200 bg-amber-50 text-amber-900;
571
+ background-color: var(--color-warning-bg);
572
+ color: var(--color-warning-fg);
573
+ border-color: var(--color-warning-border);
352
574
  }
353
575
  .toast-error {
354
- @apply border-red-200 bg-red-50 text-red-900;
576
+ background-color: var(--color-danger-bg);
577
+ color: var(--color-danger-fg);
578
+ border-color: var(--color-danger-border);
355
579
  }
356
580
  .toast-message {
357
581
  @apply flex-1 text-sm leading-snug;
358
582
  }
359
583
  .toast-close {
360
- @apply -mr-1 -mt-0.5 inline-flex size-6 shrink-0 items-center justify-center rounded text-neutral-500;
361
- @apply hover:bg-neutral-100 hover:text-neutral-900;
362
- @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500;
584
+ @apply -mr-1 -mt-0.5 inline-flex size-6 shrink-0 items-center justify-center rounded;
585
+ @apply focus-visible:outline-none focus-visible:ring-2;
586
+ color: currentColor;
587
+ opacity: 0.6;
588
+ --tw-ring-color: var(--color-ring);
589
+ }
590
+ .toast-close:hover {
591
+ opacity: 1;
592
+ }
593
+
594
+ /* ============================================================ */
595
+ /* Card / Surface */
596
+ /* ============================================================ */
597
+ .surface {
598
+ background-color: var(--color-bg-elevated);
599
+ color: var(--color-fg);
600
+ border: 1px solid var(--color-border);
601
+ border-radius: var(--ui-radius);
602
+ }
603
+ .surface-glass {
604
+ background-color: color-mix(in oklab, var(--color-bg-elevated) 65%, transparent);
605
+ backdrop-filter: blur(12px);
606
+ -webkit-backdrop-filter: blur(12px);
607
+ }
608
+ .card {
609
+ background-color: var(--color-bg-elevated);
610
+ color: var(--color-fg);
611
+ border: 1px solid var(--color-border);
612
+ border-radius: var(--ui-radius-lg);
613
+ /* No overflow:hidden — would clip popovers / menu dropdowns inside.
614
+ * Header & footer round their own corners explicitly. */
615
+ }
616
+ .card-padded {
617
+ @apply p-5;
618
+ }
619
+ .card-header {
620
+ @apply px-5 pt-5 pb-3;
621
+ border-bottom: 1px solid var(--color-border);
622
+ border-top-left-radius: var(--ui-radius-lg);
623
+ border-top-right-radius: var(--ui-radius-lg);
624
+ }
625
+ .card-body {
626
+ @apply p-5;
627
+ }
628
+ .card-footer {
629
+ @apply px-5 py-3;
630
+ border-top: 1px solid var(--color-border);
631
+ background-color: var(--color-bg-subtle);
632
+ border-bottom-left-radius: var(--ui-radius-lg);
633
+ border-bottom-right-radius: var(--ui-radius-lg);
634
+ }
635
+ .card-interactive {
636
+ @apply cursor-pointer no-underline;
637
+ color: inherit;
638
+ transition: border-color 150ms ease-out, transform 150ms ease-out, box-shadow 150ms ease-out;
639
+ }
640
+ .card-interactive:hover {
641
+ border-color: var(--color-border-strong);
642
+ transform: translateY(-2px);
643
+ box-shadow: 0 12px 24px -8px oklch(0% 0 0 / 0.10);
644
+ }
645
+ .card-interactive:focus-visible {
646
+ outline: 2px solid var(--color-ring);
647
+ outline-offset: 2px;
648
+ }
649
+
650
+ /* ============================================================ */
651
+ /* Banner */
652
+ /* ============================================================ */
653
+ .banner {
654
+ @apply flex items-start gap-3 p-3 text-sm;
655
+ border: 1px solid;
656
+ border-radius: var(--ui-radius);
657
+ animation: norns-banner-in 250ms cubic-bezier(0.21, 1.02, 0.73, 1) both;
658
+ }
659
+ @keyframes norns-banner-in {
660
+ from {
661
+ opacity: 0;
662
+ transform: translateY(-4px);
663
+ }
664
+ to {
665
+ opacity: 1;
666
+ transform: translateY(0);
667
+ }
668
+ }
669
+ .banner-info {
670
+ background-color: var(--color-info-bg);
671
+ color: var(--color-info-fg);
672
+ border-color: var(--color-info-border);
673
+ }
674
+ .banner-success {
675
+ background-color: var(--color-success-bg);
676
+ color: var(--color-success-fg);
677
+ border-color: var(--color-success-border);
678
+ }
679
+ .banner-warning {
680
+ background-color: var(--color-warning-bg);
681
+ color: var(--color-warning-fg);
682
+ border-color: var(--color-warning-border);
683
+ }
684
+ .banner-danger {
685
+ background-color: var(--color-danger-bg);
686
+ color: var(--color-danger-fg);
687
+ border-color: var(--color-danger-border);
688
+ }
689
+ .banner-icon {
690
+ @apply shrink-0 mt-0.5;
691
+ }
692
+ .banner-body {
693
+ @apply flex-1;
694
+ }
695
+ .banner-actions {
696
+ @apply shrink-0 flex gap-2 ml-auto;
697
+ }
698
+
699
+ /* ============================================================ */
700
+ /* Badge */
701
+ /* ============================================================ */
702
+ .badge {
703
+ @apply inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium leading-none;
704
+ border-radius: var(--ui-radius-full);
705
+ background-color: var(--color-bg-muted);
706
+ color: var(--color-fg);
707
+ }
708
+ .badge-primary {
709
+ background-color: var(--color-primary-100);
710
+ color: var(--color-primary-900);
711
+ }
712
+ [data-theme='dark'] .badge-primary {
713
+ background-color: var(--color-primary-950);
714
+ color: var(--color-primary-200);
715
+ }
716
+ .badge-success {
717
+ background-color: var(--color-success-bg);
718
+ color: var(--color-success-fg);
719
+ }
720
+ .badge-warning {
721
+ background-color: var(--color-warning-bg);
722
+ color: var(--color-warning-fg);
723
+ }
724
+ .badge-danger {
725
+ background-color: var(--color-danger-bg);
726
+ color: var(--color-danger-fg);
727
+ }
728
+ .badge-info {
729
+ background-color: var(--color-info-bg);
730
+ color: var(--color-info-fg);
731
+ }
732
+ .badge-sm {
733
+ @apply text-[10px] px-1.5;
734
+ }
735
+
736
+ /* ============================================================ */
737
+ /* Chip */
738
+ /* ============================================================ */
739
+ .chip {
740
+ @apply inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium;
741
+ background-color: var(--color-bg-muted);
742
+ color: var(--color-fg);
743
+ border: 1px solid var(--color-border);
744
+ border-radius: var(--ui-radius-full);
745
+ }
746
+ .chip-remove {
747
+ @apply -mr-1 inline-flex size-4 items-center justify-center rounded-full;
748
+ opacity: 0.6;
749
+ }
750
+ .chip-remove:hover {
751
+ opacity: 1;
752
+ background-color: var(--color-border);
753
+ }
754
+
755
+ /* ============================================================ */
756
+ /* Avatar */
757
+ /* ============================================================ */
758
+ .avatar {
759
+ @apply relative inline-flex shrink-0 items-center justify-center overflow-hidden font-medium;
760
+ background-color: var(--color-bg-muted);
761
+ color: var(--color-fg);
762
+ border-radius: var(--ui-radius-full);
763
+ }
764
+ .avatar-sm {
765
+ @apply size-6 text-xs;
766
+ }
767
+ .avatar-md {
768
+ @apply size-8 text-sm;
769
+ }
770
+ .avatar-lg {
771
+ @apply size-12 text-base;
772
+ }
773
+ .avatar-xl {
774
+ @apply size-16 text-lg;
775
+ }
776
+ .avatar img {
777
+ @apply h-full w-full object-cover;
778
+ }
779
+
780
+ /* AvatarGroup */
781
+ .avatar-group {
782
+ @apply inline-flex items-center;
783
+ }
784
+ .avatar-group-item,
785
+ .avatar-group-overflow {
786
+ @apply relative inline-flex transition-transform duration-150 ease-out;
787
+ margin-left: -0.5rem;
788
+ box-shadow: 0 0 0 2px var(--color-bg);
789
+ border-radius: var(--ui-radius-full);
790
+ }
791
+ .avatar-group-item:first-child,
792
+ .avatar-group:has(.avatar-group-item:first-child) > .avatar-group-item:first-child {
793
+ margin-left: 0;
794
+ }
795
+ .avatar-group-item:hover {
796
+ transform: translateY(-2px);
797
+ z-index: 1;
798
+ }
799
+ .avatar-group-overflow {
800
+ @apply text-xs font-medium;
801
+ background-color: var(--color-bg-muted);
802
+ color: var(--color-fg-muted);
803
+ }
804
+
805
+ /* GradientText */
806
+ .gradient-text {
807
+ background-clip: text;
808
+ -webkit-background-clip: text;
809
+ color: transparent;
810
+ background-size: 200% 200%;
811
+ background-position: 0% 50%;
812
+ }
813
+ .gradient-text-anim {
814
+ animation: norns-gradient-shift 6s ease-in-out infinite;
815
+ }
816
+ @keyframes norns-gradient-shift {
817
+ 0%, 100% { background-position: 0% 50%; }
818
+ 50% { background-position: 100% 50%; }
819
+ }
820
+
821
+ /* CopyButton */
822
+ .copy-btn-check {
823
+ animation: norns-copy-pop 250ms ease-out;
824
+ }
825
+ @keyframes norns-copy-pop {
826
+ 0% { transform: scale(0.5); opacity: 0; }
827
+ 70% { transform: scale(1.15); }
828
+ 100% { transform: scale(1); opacity: 1; }
829
+ }
830
+
831
+ /* ShinyButton — sweeping shine overlay */
832
+ .shiny-btn {
833
+ position: relative;
834
+ isolation: isolate;
835
+ }
836
+ .shiny-btn-shine {
837
+ position: absolute;
838
+ inset: 0;
839
+ pointer-events: none;
840
+ background: linear-gradient(
841
+ 110deg,
842
+ transparent 30%,
843
+ color-mix(in oklab, white 35%, transparent) 50%,
844
+ transparent 70%
845
+ );
846
+ background-size: 250% 100%;
847
+ background-position: 200% 0;
848
+ animation: norns-shiny 2.6s ease-in-out infinite;
849
+ mix-blend-mode: overlay;
850
+ border-radius: inherit;
851
+ }
852
+ @keyframes norns-shiny {
853
+ 0% { background-position: 200% 0; }
854
+ 100% { background-position: -150% 0; }
855
+ }
856
+
857
+ /* RippleButton */
858
+ .ripple-btn {
859
+ position: relative;
860
+ overflow: hidden;
861
+ }
862
+ .ripple-btn-ripple {
863
+ position: absolute;
864
+ pointer-events: none;
865
+ border-radius: 9999px;
866
+ background-color: color-mix(in oklab, currentColor 35%, transparent);
867
+ transform: scale(0);
868
+ opacity: 0.6;
869
+ animation: norns-ripple 600ms ease-out forwards;
870
+ }
871
+ @keyframes norns-ripple {
872
+ to {
873
+ transform: scale(1);
874
+ opacity: 0;
875
+ }
876
+ }
877
+
878
+ /* LiquidButton (motion subpath) */
879
+ .liquid-btn {
880
+ position: relative;
881
+ isolation: isolate;
882
+ overflow: hidden;
883
+ }
884
+ .liquid-btn-blob {
885
+ position: absolute;
886
+ inset: -25%;
887
+ background: radial-gradient(
888
+ circle,
889
+ color-mix(in oklab, white 40%, transparent),
890
+ transparent 60%
891
+ );
892
+ transform: scale(0.6);
893
+ mix-blend-mode: overlay;
894
+ pointer-events: none;
895
+ filter: blur(12px);
896
+ z-index: 0;
897
+ }
898
+ .liquid-btn-content {
899
+ @apply inline-flex items-center gap-2 relative;
900
+ z-index: 1;
901
+ }
902
+
903
+ /* Sparkles (motion subpath) */
904
+ .sparkles-wrap {
905
+ @apply relative inline-block;
906
+ }
907
+ .sparkles-overlay {
908
+ @apply pointer-events-none absolute inset-0 overflow-hidden;
909
+ }
910
+ .sparkle {
911
+ position: absolute;
912
+ display: block;
913
+ border-radius: 9999px;
914
+ background: var(--sparkle-color, var(--color-primary-400));
915
+ opacity: 0;
916
+ transform: scale(0);
917
+ animation: norns-sparkle 2.4s ease-in-out infinite;
918
+ animation-fill-mode: forwards;
919
+ filter: drop-shadow(0 0 4px var(--sparkle-color, var(--color-primary-400)));
920
+ }
921
+ .sparkles-paused .sparkle {
922
+ animation-play-state: paused;
923
+ }
924
+ @keyframes norns-sparkle {
925
+ 0%, 100% { opacity: 0; transform: scale(0) rotate(0deg); }
926
+ 20%, 80% { opacity: 1; }
927
+ 50% { transform: scale(1) rotate(180deg); }
928
+ }
929
+
930
+ /* GradientBackground (motion subpath) */
931
+ .gradient-bg {
932
+ @apply relative overflow-hidden;
933
+ background-image: linear-gradient(
934
+ var(--gradient-bg-angle, 130deg),
935
+ var(--gradient-bg-stops, var(--color-primary-500), var(--color-info-500))
936
+ );
937
+ background-size: 300% 300%;
938
+ animation: norns-gradient-loop var(--gradient-bg-duration, 18s) ease-in-out infinite;
939
+ }
940
+ @keyframes norns-gradient-loop {
941
+ 0%, 100% { background-position: 0% 50%; }
942
+ 25% { background-position: 100% 50%; }
943
+ 50% { background-position: 100% 100%; }
944
+ 75% { background-position: 0% 100%; }
945
+ }
946
+
947
+ /* ============================================================ */
948
+ /* Skeleton */
949
+ /* ============================================================ */
950
+ .skeleton {
951
+ @apply block;
952
+ background-color: var(--color-bg-muted);
953
+ border-radius: var(--ui-radius-sm);
954
+ animation: norns-skeleton-pulse 1.6s ease-in-out infinite;
955
+ }
956
+ .skeleton-circle {
957
+ border-radius: var(--ui-radius-full);
958
+ }
959
+ @keyframes norns-skeleton-pulse {
960
+ 0%, 100% { opacity: 1; }
961
+ 50% { opacity: 0.5; }
962
+ }
963
+
964
+ /* ============================================================ */
965
+ /* Progress */
966
+ /* ============================================================ */
967
+ .progress {
968
+ @apply relative h-2 w-full overflow-hidden;
969
+ background-color: var(--color-bg-muted);
970
+ border-radius: var(--ui-radius-full);
971
+ }
972
+ .progress-bar {
973
+ @apply h-full transition-[width] duration-300 ease-out;
974
+ background-color: var(--color-primary-600);
975
+ border-radius: var(--ui-radius-full);
976
+ }
977
+ .progress-bar-success {
978
+ background-color: var(--color-success-600);
979
+ }
980
+ .progress-bar-warning {
981
+ background-color: var(--color-warning-600);
982
+ }
983
+ .progress-bar-danger {
984
+ background-color: var(--color-danger-600);
985
+ }
986
+
987
+ /* Indeterminate (uses CSS keyframes only) */
988
+ .progress-indeterminate .progress-bar {
989
+ width: 35%;
990
+ animation: norns-progress-indeterminate 1.4s ease-in-out infinite;
991
+ }
992
+ @keyframes norns-progress-indeterminate {
993
+ 0% { transform: translateX(-100%); }
994
+ 100% { transform: translateX(385%); }
995
+ }
996
+
997
+ /* ============================================================ */
998
+ /* Header (composite) */
999
+ /* ============================================================ */
1000
+ .norns-header {
1001
+ @apply flex items-center justify-between gap-4 px-6 py-3;
1002
+ background-color: var(--color-bg);
1003
+ border-bottom: 1px solid var(--color-border);
1004
+ }
1005
+ .norns-header-sticky {
1006
+ @apply sticky top-0 z-30;
1007
+ background-color: color-mix(in oklab, var(--color-bg) 85%, transparent);
1008
+ backdrop-filter: blur(8px);
1009
+ }
1010
+ .norns-header-brand {
1011
+ @apply flex items-center gap-2 font-semibold;
1012
+ color: var(--color-fg);
1013
+ }
1014
+ .norns-header-nav {
1015
+ @apply flex items-center gap-1;
1016
+ }
1017
+ .norns-header-actions {
1018
+ @apply flex items-center gap-2;
1019
+ }
1020
+
1021
+ /* ============================================================ */
1022
+ /* HeroBanner (composite) */
1023
+ /* ============================================================ */
1024
+ .hero {
1025
+ @apply relative overflow-hidden p-8 md:p-12;
1026
+ background-color: var(--color-bg-elevated);
1027
+ border: 1px solid var(--color-border);
1028
+ border-radius: var(--ui-radius-lg);
1029
+ }
1030
+ .hero-center {
1031
+ @apply text-center;
1032
+ }
1033
+ .hero-title {
1034
+ @apply text-3xl font-bold tracking-tight md:text-4xl;
1035
+ color: var(--color-fg);
1036
+ }
1037
+ .hero-description {
1038
+ @apply mt-3 text-base md:text-lg max-w-prose;
1039
+ color: var(--color-fg-muted);
1040
+ }
1041
+ .hero-center .hero-description {
1042
+ @apply mx-auto;
1043
+ }
1044
+ .hero-actions {
1045
+ @apply mt-6 flex flex-wrap items-center gap-3;
1046
+ }
1047
+ .hero-center .hero-actions {
1048
+ @apply justify-center;
1049
+ }
1050
+ .hero-image {
1051
+ @apply pointer-events-none absolute inset-y-0 right-0 w-1/2 object-cover opacity-20;
1052
+ }
1053
+
1054
+ /* ============================================================ */
1055
+ /* Stepper */
1056
+ /* ============================================================ */
1057
+ .stepper {
1058
+ @apply flex items-center gap-3;
1059
+ }
1060
+ .stepper-vertical {
1061
+ @apply flex-col items-stretch;
1062
+ }
1063
+ .stepper-step {
1064
+ @apply flex items-center gap-2 text-sm;
1065
+ color: var(--color-fg-muted);
1066
+ }
1067
+ .stepper-step-current {
1068
+ color: var(--color-fg);
1069
+ font-weight: 600;
1070
+ }
1071
+ .stepper-step-complete {
1072
+ color: var(--color-fg);
1073
+ }
1074
+ .stepper-marker {
1075
+ @apply inline-flex size-7 shrink-0 items-center justify-center rounded-full text-xs font-semibold;
1076
+ background-color: var(--color-bg-muted);
1077
+ color: var(--color-fg-muted);
1078
+ border: 1px solid var(--color-border);
1079
+ }
1080
+ .stepper-step-current .stepper-marker {
1081
+ background-color: var(--color-primary-600);
1082
+ color: white;
1083
+ border-color: var(--color-primary-600);
1084
+ }
1085
+ .stepper-step-complete .stepper-marker {
1086
+ background-color: var(--color-success-600);
1087
+ color: white;
1088
+ border-color: var(--color-success-600);
1089
+ }
1090
+ .stepper-divider {
1091
+ @apply h-px flex-1;
1092
+ background-color: var(--color-border);
1093
+ }
1094
+ .stepper-vertical .stepper-divider {
1095
+ @apply h-6 w-px ml-3;
1096
+ }
1097
+
1098
+ /* ============================================================ */
1099
+ /* Breadcrumbs */
1100
+ /* ============================================================ */
1101
+ .breadcrumbs {
1102
+ @apply flex items-center gap-1 text-sm;
1103
+ color: var(--color-fg-muted);
1104
+ }
1105
+ .breadcrumb-item {
1106
+ @apply inline-flex items-center gap-1;
1107
+ }
1108
+ .breadcrumb-link {
1109
+ color: var(--color-fg-muted);
1110
+ }
1111
+ .breadcrumb-link:hover {
1112
+ color: var(--color-fg);
1113
+ }
1114
+ .breadcrumb-current {
1115
+ color: var(--color-fg);
1116
+ font-weight: 500;
1117
+ }
1118
+ .breadcrumb-separator {
1119
+ @apply mx-1 select-none;
1120
+ color: var(--color-fg-subtle);
1121
+ }
1122
+
1123
+ /* ============================================================ */
1124
+ /* Pagination */
1125
+ /* ============================================================ */
1126
+ .pagination {
1127
+ @apply inline-flex items-center gap-1;
1128
+ }
1129
+ .pagination-item {
1130
+ @apply inline-flex size-8 items-center justify-center text-sm;
1131
+ color: var(--color-fg-muted);
1132
+ border-radius: var(--ui-radius-sm);
1133
+ }
1134
+ .pagination-item:hover {
1135
+ background-color: var(--color-bg-muted);
1136
+ color: var(--color-fg);
1137
+ }
1138
+ .pagination-item-active {
1139
+ background-color: var(--color-primary-600);
1140
+ color: white;
1141
+ }
1142
+ .pagination-item-active:hover {
1143
+ background-color: var(--color-primary-700);
1144
+ color: white;
1145
+ }
1146
+ .pagination-item:disabled {
1147
+ @apply cursor-not-allowed opacity-40;
1148
+ }
1149
+ .pagination-ellipsis {
1150
+ @apply inline-flex size-8 items-center justify-center text-sm select-none;
1151
+ color: var(--color-fg-subtle);
1152
+ }
1153
+
1154
+ /* ============================================================ */
1155
+ /* Accordion */
1156
+ /* ============================================================ */
1157
+ .accordion {
1158
+ @apply divide-y;
1159
+ border: 1px solid var(--color-border);
1160
+ border-radius: var(--ui-radius);
1161
+ }
1162
+ .accordion :where(.divide-y) > * {
1163
+ border-color: var(--color-border);
1164
+ }
1165
+ .accordion-item {
1166
+ border-bottom: 1px solid var(--color-border);
1167
+ }
1168
+ .accordion-item:last-child {
1169
+ border-bottom: none;
1170
+ }
1171
+ .accordion-trigger {
1172
+ @apply flex w-full items-center justify-between gap-2 px-4 py-3 text-left text-sm font-medium;
1173
+ @apply transition-colors focus-visible:outline-none focus-visible:ring-2;
1174
+ color: var(--color-fg);
1175
+ background-color: transparent;
1176
+ --tw-ring-color: var(--color-ring);
1177
+ }
1178
+ .accordion-trigger:hover {
1179
+ background-color: var(--color-bg-muted);
1180
+ }
1181
+ .accordion-trigger[data-state='open'] .accordion-chevron {
1182
+ transform: rotate(180deg);
1183
+ }
1184
+ .accordion-chevron {
1185
+ @apply size-4 shrink-0 transition-transform duration-150;
1186
+ color: var(--color-fg-muted);
1187
+ }
1188
+ .accordion-content {
1189
+ @apply px-4 pb-3 text-sm;
1190
+ color: var(--color-fg-muted);
1191
+ }
1192
+
1193
+ /* ============================================================ */
1194
+ /* Separator */
1195
+ /* ============================================================ */
1196
+ .separator {
1197
+ background-color: var(--color-border);
1198
+ }
1199
+ .separator-horizontal {
1200
+ @apply h-px w-full;
1201
+ }
1202
+ .separator-vertical {
1203
+ @apply w-px h-full self-stretch;
1204
+ }
1205
+
1206
+ /* ============================================================ */
1207
+ /* Toolbar */
1208
+ /* ============================================================ */
1209
+ .toolbar {
1210
+ @apply flex items-center gap-1 p-1;
1211
+ background-color: var(--color-bg-muted);
1212
+ border: 1px solid var(--color-border);
1213
+ border-radius: var(--ui-radius);
1214
+ }
1215
+ .toolbar-vertical {
1216
+ @apply flex-col items-stretch;
1217
+ }
1218
+
1219
+ /* ============================================================ */
1220
+ /* ButtonGroup */
1221
+ /* ============================================================ */
1222
+ .btn-group {
1223
+ @apply inline-flex;
1224
+ }
1225
+ .btn-group > .btn,
1226
+ .btn-group > .toggle-btn {
1227
+ border-radius: 0;
1228
+ }
1229
+ .btn-group > :first-child {
1230
+ border-top-left-radius: var(--ui-radius);
1231
+ border-bottom-left-radius: var(--ui-radius);
1232
+ }
1233
+ .btn-group > :last-child {
1234
+ border-top-right-radius: var(--ui-radius);
1235
+ border-bottom-right-radius: var(--ui-radius);
1236
+ }
1237
+ .btn-group > * + * {
1238
+ margin-left: -1px;
1239
+ }
1240
+ .btn-group-vertical {
1241
+ @apply flex-col;
1242
+ }
1243
+ .btn-group-vertical > * {
1244
+ width: 100%;
1245
+ }
1246
+ .btn-group-vertical > :first-child {
1247
+ border-radius: 0;
1248
+ border-top-left-radius: var(--ui-radius);
1249
+ border-top-right-radius: var(--ui-radius);
1250
+ }
1251
+ .btn-group-vertical > :last-child {
1252
+ border-radius: 0;
1253
+ border-bottom-left-radius: var(--ui-radius);
1254
+ border-bottom-right-radius: var(--ui-radius);
1255
+ }
1256
+ .btn-group-vertical > * + * {
1257
+ margin-left: 0;
1258
+ margin-top: -1px;
1259
+ }
1260
+
1261
+ /* ============================================================ */
1262
+ /* ToggleButton */
1263
+ /* ============================================================ */
1264
+ .toggle-btn {
1265
+ @apply inline-flex items-center justify-center gap-1.5 text-sm font-medium;
1266
+ @apply transition-colors focus-visible:outline-none focus-visible:ring-2;
1267
+ @apply disabled:cursor-not-allowed disabled:opacity-60;
1268
+ height: var(--ui-h-md);
1269
+ padding-inline: 0.625rem;
1270
+ background-color: transparent;
1271
+ color: var(--color-fg-muted);
1272
+ border: 1px solid var(--color-border);
1273
+ border-radius: var(--ui-radius);
1274
+ --tw-ring-color: var(--color-ring);
1275
+ }
1276
+ .toggle-btn:hover {
1277
+ background-color: var(--color-bg-muted);
1278
+ color: var(--color-fg);
1279
+ }
1280
+ .toggle-btn[data-state='on'] {
1281
+ background-color: var(--color-bg-muted);
1282
+ color: var(--color-fg);
1283
+ border-color: var(--color-border-strong);
1284
+ }
1285
+ .toggle-btn-sm {
1286
+ height: var(--ui-h-sm);
1287
+ @apply text-xs px-2;
1288
+ }
1289
+ .toggle-btn-lg {
1290
+ height: var(--ui-h-lg);
1291
+ @apply text-base px-3;
1292
+ }
1293
+
1294
+ /* ============================================================ */
1295
+ /* Collapsible */
1296
+ /* ============================================================ */
1297
+ .collapsible {
1298
+ border: 1px solid var(--color-border);
1299
+ border-radius: var(--ui-radius);
1300
+ background-color: var(--color-bg-elevated);
1301
+ }
1302
+ .collapsible-trigger {
1303
+ @apply flex w-full items-center gap-2 px-3 py-2 text-left text-sm font-medium;
1304
+ @apply transition-colors focus-visible:outline-none focus-visible:ring-2;
1305
+ color: var(--color-fg);
1306
+ background-color: transparent;
1307
+ --tw-ring-color: var(--color-ring);
1308
+ }
1309
+ .collapsible-trigger:hover {
1310
+ background-color: var(--color-bg-muted);
1311
+ }
1312
+ .collapsible-chevron {
1313
+ @apply size-4 shrink-0 transition-transform duration-150;
1314
+ color: var(--color-fg-muted);
1315
+ }
1316
+ .collapsible-chevron-open {
1317
+ transform: rotate(90deg);
1318
+ }
1319
+ .collapsible-title {
1320
+ @apply flex-1;
1321
+ }
1322
+ .collapsible-content {
1323
+ @apply px-3 pb-3 pt-1 text-sm;
1324
+ color: var(--color-fg-muted);
1325
+ }
1326
+
1327
+ /* ============================================================ */
1328
+ /* NumberInput */
1329
+ /* ============================================================ */
1330
+ .number-input {
1331
+ @apply inline-flex items-stretch;
1332
+ border-radius: var(--ui-radius);
1333
+ }
1334
+ .number-input-field {
1335
+ @apply text-center;
1336
+ border-radius: 0;
1337
+ min-width: 4rem;
1338
+ }
1339
+ .number-step {
1340
+ @apply inline-flex items-center justify-center px-2;
1341
+ background-color: var(--color-bg-muted);
1342
+ color: var(--color-fg);
1343
+ border: 1px solid var(--color-border-strong);
1344
+ --tw-ring-color: var(--color-ring);
1345
+ height: var(--ui-h-md);
1346
+ }
1347
+ .number-step:first-child {
1348
+ border-top-left-radius: var(--ui-radius);
1349
+ border-bottom-left-radius: var(--ui-radius);
1350
+ border-right: none;
1351
+ }
1352
+ .number-step:last-child {
1353
+ border-top-right-radius: var(--ui-radius);
1354
+ border-bottom-right-radius: var(--ui-radius);
1355
+ border-left: none;
1356
+ }
1357
+ .number-step:hover:not(:disabled) {
1358
+ background-color: var(--color-bg-subtle);
1359
+ }
1360
+ .number-step:disabled {
1361
+ @apply cursor-not-allowed opacity-40;
1362
+ }
1363
+ .number-input-field {
1364
+ border-radius: 0;
1365
+ }
1366
+ /* Hide native spinners since we have our own buttons */
1367
+ .number-input-field::-webkit-outer-spin-button,
1368
+ .number-input-field::-webkit-inner-spin-button {
1369
+ -webkit-appearance: none;
1370
+ margin: 0;
1371
+ }
1372
+ .number-input-field {
1373
+ -moz-appearance: textfield;
1374
+ }
1375
+
1376
+ /* ============================================================ */
1377
+ /* OtpField */
1378
+ /* ============================================================ */
1379
+ .otp-field {
1380
+ @apply inline-flex gap-2;
1381
+ }
1382
+ .otp-cell {
1383
+ @apply text-center font-mono text-lg;
1384
+ @apply focus:outline-none focus:ring-2;
1385
+ width: 2.75rem;
1386
+ height: 3rem;
1387
+ background-color: var(--color-bg);
1388
+ color: var(--color-fg);
1389
+ border: 1px solid var(--color-border-strong);
1390
+ border-radius: var(--ui-radius);
1391
+ --tw-ring-color: color-mix(in oklab, var(--color-ring) 30%, transparent);
1392
+ }
1393
+ .otp-cell:focus {
1394
+ border-color: var(--color-ring);
1395
+ }
1396
+
1397
+ /* ============================================================ */
1398
+ /* TagsInput */
1399
+ /* ============================================================ */
1400
+ .tags-input {
1401
+ @apply flex w-full flex-wrap items-center gap-1.5 px-2 py-1.5;
1402
+ @apply transition-colors focus-within:ring-2;
1403
+ min-height: var(--ui-h-md);
1404
+ background-color: var(--color-bg);
1405
+ color: var(--color-fg);
1406
+ border: 1px solid var(--color-border-strong);
1407
+ border-radius: var(--ui-radius);
1408
+ --tw-ring-color: color-mix(in oklab, var(--color-ring) 30%, transparent);
1409
+ }
1410
+ .tags-input:focus-within {
1411
+ border-color: var(--color-ring);
1412
+ }
1413
+ .tags-input-err {
1414
+ border-color: var(--color-danger-500);
1415
+ }
1416
+ .tags-input-field {
1417
+ @apply min-w-[6rem] flex-1 text-sm outline-none;
1418
+ background: transparent;
1419
+ color: var(--color-fg);
1420
+ }
1421
+ .tags-input-field::placeholder {
1422
+ color: var(--color-fg-subtle);
1423
+ }
1424
+
1425
+ /* ============================================================ */
1426
+ /* Timeline */
1427
+ /* ============================================================ */
1428
+ .timeline {
1429
+ @apply list-none p-0 m-0 relative;
1430
+ }
1431
+ .timeline-item {
1432
+ @apply relative grid pl-10 pb-6;
1433
+ grid-template-columns: 1fr;
1434
+ }
1435
+ .timeline-item:last-child {
1436
+ @apply pb-0;
1437
+ }
1438
+ .timeline-dot {
1439
+ @apply absolute left-0 inline-flex size-7 items-center justify-center rounded-full text-white;
1440
+ background-color: var(--color-primary-600);
1441
+ top: 0;
1442
+ }
1443
+ .timeline-dot-success { background-color: var(--color-success-600); }
1444
+ .timeline-dot-warning { background-color: var(--color-warning-600); }
1445
+ .timeline-dot-danger { background-color: var(--color-danger-600); }
1446
+ .timeline-dot-info { background-color: var(--color-info-600); }
1447
+ .timeline-line {
1448
+ @apply absolute w-px;
1449
+ left: 0.875rem;
1450
+ top: 1.75rem;
1451
+ bottom: -0.5rem;
1452
+ background-color: var(--color-border);
1453
+ }
1454
+ .timeline-time {
1455
+ @apply text-xs;
1456
+ color: var(--color-fg-subtle);
1457
+ }
1458
+ .timeline-title {
1459
+ @apply text-sm font-semibold;
1460
+ color: var(--color-fg);
1461
+ }
1462
+ .timeline-description {
1463
+ @apply text-sm mt-0.5;
1464
+ color: var(--color-fg-muted);
1465
+ }
1466
+
1467
+ /* ============================================================ */
1468
+ /* Combobox / Autocomplete / MultiSelect */
1469
+ /* ============================================================ */
1470
+ .combobox-input-wrap {
1471
+ @apply relative inline-flex w-full items-stretch;
1472
+ }
1473
+ .combobox-input {
1474
+ @apply pr-9;
1475
+ }
1476
+ .combobox-trigger {
1477
+ @apply absolute right-1 top-1/2 -translate-y-1/2 inline-flex size-7 items-center justify-center rounded;
1478
+ color: var(--color-fg-muted);
1479
+ }
1480
+ .combobox-trigger:hover {
1481
+ background-color: var(--color-bg-muted);
1482
+ }
1483
+
1484
+ /* ============================================================ */
1485
+ /* Navigation menu (Hierarchical / Mega) — CSS-only hover/focus */
1486
+ /* No JS, no Bits-UI dep. Submenus open on hover OR keyboard */
1487
+ /* focus inside the parent <li>. */
1488
+ /* ============================================================ */
1489
+ .nav-menu {
1490
+ @apply relative z-10;
1491
+ }
1492
+ .nav-menu-list {
1493
+ @apply flex items-center gap-0 list-none p-0 m-0 flex-wrap;
1494
+ }
1495
+ .nav-menu-item {
1496
+ @apply relative;
1497
+ }
1498
+ .nav-menu-trigger {
1499
+ @apply inline-flex items-center gap-1 px-3 text-sm font-medium rounded-md;
1500
+ @apply transition-colors focus-visible:outline-none focus-visible:ring-2;
1501
+ @apply cursor-pointer select-none;
1502
+ height: var(--ui-h-md);
1503
+ color: var(--color-fg-muted);
1504
+ background-color: transparent;
1505
+ --tw-ring-color: var(--color-ring);
1506
+ text-decoration: none;
1507
+ border: none;
1508
+ font-family: inherit;
1509
+ }
1510
+ .nav-menu-trigger:hover {
1511
+ color: var(--color-fg);
1512
+ background-color: var(--color-bg-muted);
1513
+ }
1514
+ .nav-menu-chevron {
1515
+ @apply transition-transform duration-200 shrink-0;
1516
+ }
1517
+ .nav-menu-content {
1518
+ @apply absolute top-full left-0 mt-1 z-50;
1519
+ @apply opacity-0 invisible -translate-y-1 transition-[opacity,transform,visibility] duration-200 ease-out;
1520
+ background-color: var(--color-bg-elevated);
1521
+ border: 1px solid var(--color-border);
1522
+ border-radius: var(--ui-radius);
1523
+ box-shadow:
1524
+ 0 10px 32px -8px oklch(0% 0 0 / 0.18),
1525
+ 0 4px 8px -4px oklch(0% 0 0 / 0.06);
1526
+ min-width: 14rem;
1527
+ padding: 0.25rem;
1528
+ }
1529
+ /* Open on hover, keyboard focus inside, OR aria-expanded=true. */
1530
+ .nav-menu-item:hover > .nav-menu-content,
1531
+ .nav-menu-item:focus-within > .nav-menu-content {
1532
+ @apply opacity-100 visible translate-y-0;
1533
+ }
1534
+ .nav-menu-item:hover > .nav-menu-trigger,
1535
+ .nav-menu-item:focus-within > .nav-menu-trigger {
1536
+ color: var(--color-fg);
1537
+ background-color: var(--color-bg-muted);
1538
+ }
1539
+ .nav-menu-item:hover > .nav-menu-trigger > .nav-menu-chevron,
1540
+ .nav-menu-item:focus-within > .nav-menu-trigger > .nav-menu-chevron {
1541
+ transform: rotate(180deg);
1542
+ }
1543
+ .nav-menu-link {
1544
+ @apply flex items-start gap-2 px-3 py-2 text-sm rounded transition-colors;
1545
+ color: var(--color-fg);
1546
+ text-decoration: none;
1547
+ }
1548
+ .nav-menu-link:hover {
1549
+ background-color: var(--color-bg-muted);
1550
+ }
1551
+
1552
+ /* MegaMenu — wider, multi-column, anchored to the row not the trigger. */
1553
+ .mega-menu-grid {
1554
+ @apply grid gap-6 p-5;
1555
+ min-width: 32rem;
1556
+ }
1557
+ .mega-menu-grid.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
1558
+ .mega-menu-grid.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
1559
+ .mega-menu-grid.grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
1560
+ .mega-menu-column {
1561
+ @apply flex flex-col gap-1;
1562
+ }
1563
+ .mega-menu-title {
1564
+ @apply text-[11px] uppercase tracking-wider px-3 mb-1 font-semibold;
1565
+ color: var(--color-fg-subtle);
1566
+ }
1567
+
1568
+ /* ============================================================ */
1569
+ /* ScrollArea */
1570
+ /* ============================================================ */
1571
+ .scroll-area-viewport {
1572
+ @apply h-full w-full rounded-[inherit];
1573
+ }
1574
+ .scroll-area-scrollbar {
1575
+ @apply flex select-none touch-none p-0.5 transition-colors;
1576
+ background-color: transparent;
1577
+ }
1578
+ .scroll-area-scrollbar[data-orientation='vertical'] {
1579
+ width: 0.5rem;
1580
+ }
1581
+ .scroll-area-scrollbar[data-orientation='horizontal'] {
1582
+ height: 0.5rem;
1583
+ }
1584
+ .scroll-area-thumb {
1585
+ @apply relative flex-1 rounded-full;
1586
+ background-color: var(--color-border-strong);
1587
+ }
1588
+ .scroll-area-thumb:hover {
1589
+ background-color: var(--color-fg-subtle);
1590
+ }
1591
+
1592
+ /* ============================================================ */
1593
+ /* ColorPicker */
1594
+ /* ============================================================ */
1595
+ .color-picker {
1596
+ @apply inline-flex items-center gap-2;
1597
+ }
1598
+ .color-picker-swatch {
1599
+ @apply inline-block size-9 cursor-pointer overflow-hidden;
1600
+ border: 1px solid var(--color-border-strong);
1601
+ border-radius: var(--ui-radius);
1602
+ }
1603
+ .color-picker-text {
1604
+ @apply font-mono;
1605
+ width: 8.5rem;
1606
+ }
1607
+
1608
+ /* ============================================================ */
1609
+ /* Uploader */
1610
+ /* ============================================================ */
1611
+ .uploader-root {
1612
+ @apply flex w-full flex-col items-center justify-center gap-2 p-6 text-center cursor-pointer;
1613
+ @apply transition-colors focus-visible:outline-none focus-visible:ring-2;
1614
+ background-color: var(--color-bg-subtle);
1615
+ color: var(--color-fg);
1616
+ border: 2px dashed var(--color-border-strong);
1617
+ border-radius: var(--ui-radius);
1618
+ --tw-ring-color: var(--color-ring);
1619
+ }
1620
+ .uploader-root:hover {
1621
+ background-color: var(--color-bg-muted);
1622
+ }
1623
+ .uploader-dragging {
1624
+ background-color: color-mix(in oklab, var(--color-primary-500) 6%, var(--color-bg-muted));
1625
+ border-color: var(--color-primary-500);
1626
+ }
1627
+ .uploader-text {
1628
+ @apply flex flex-col items-center gap-0.5;
1629
+ }
1630
+ .uploader-primary {
1631
+ @apply text-sm font-medium;
1632
+ }
1633
+ .uploader-helper {
1634
+ @apply text-xs;
1635
+ color: var(--color-fg-muted);
1636
+ }
1637
+ .uploader-list {
1638
+ @apply mt-2 flex flex-col gap-1 list-none p-0;
1639
+ }
1640
+ .uploader-item {
1641
+ @apply flex items-center gap-2 px-2 py-1.5;
1642
+ background-color: var(--color-bg-muted);
1643
+ border-radius: var(--ui-radius-sm);
1644
+ }
1645
+
1646
+ /* ============================================================ */
1647
+ /* Date pickers (Popover-based) + custom Calendar */
1648
+ /* ============================================================ */
1649
+ .date-picker-trigger {
1650
+ @apply inline-flex items-center gap-2 cursor-pointer text-left w-full;
1651
+ min-width: 12rem;
1652
+ }
1653
+ .date-picker-trigger[disabled] {
1654
+ @apply opacity-60 cursor-not-allowed;
1655
+ }
1656
+
1657
+ .calendar {
1658
+ @apply select-none;
1659
+ }
1660
+ .calendar-header {
1661
+ @apply flex items-center justify-between gap-1 mb-2;
1662
+ }
1663
+ .calendar-heading-btn {
1664
+ @apply flex-1 text-sm font-semibold rounded py-1 px-2 transition-colors;
1665
+ color: var(--color-fg);
1666
+ background: transparent;
1667
+ border: none;
1668
+ cursor: pointer;
1669
+ }
1670
+ .calendar-heading-btn:hover {
1671
+ background-color: var(--color-bg-muted);
1672
+ }
1673
+ .calendar-nav {
1674
+ @apply inline-flex size-7 items-center justify-center rounded transition-colors;
1675
+ color: var(--color-fg-muted);
1676
+ background: transparent;
1677
+ border: none;
1678
+ cursor: pointer;
1679
+ }
1680
+ .calendar-nav:hover {
1681
+ background-color: var(--color-bg-muted);
1682
+ color: var(--color-fg);
1683
+ }
1684
+ .calendar-row {
1685
+ @apply grid gap-0.5;
1686
+ grid-template-columns: repeat(7, minmax(0, 1fr));
1687
+ }
1688
+ .calendar-weekdays {
1689
+ @apply mb-1;
1690
+ }
1691
+ .calendar-weekday {
1692
+ @apply text-center text-[11px] font-medium uppercase py-1;
1693
+ color: var(--color-fg-subtle);
1694
+ }
1695
+ .calendar-day {
1696
+ @apply h-9 w-full inline-flex items-center justify-center rounded-md text-sm transition-colors;
1697
+ color: var(--color-fg);
1698
+ background: transparent;
1699
+ border: none;
1700
+ cursor: pointer;
1701
+ }
1702
+ .calendar-day:hover:not(:disabled) {
1703
+ background-color: var(--color-bg-muted);
1704
+ }
1705
+ .calendar-day:focus-visible {
1706
+ outline: 2px solid var(--color-ring);
1707
+ outline-offset: -2px;
1708
+ }
1709
+ .calendar-day-outside {
1710
+ color: var(--color-fg-subtle);
1711
+ }
1712
+ .calendar-day-today {
1713
+ font-weight: 600;
1714
+ box-shadow: inset 0 0 0 1px var(--color-border-strong);
1715
+ }
1716
+ .calendar-day-selected {
1717
+ background-color: var(--color-primary-600);
1718
+ color: white;
1719
+ font-weight: 600;
1720
+ }
1721
+ .calendar-day-selected:hover:not(:disabled) {
1722
+ background-color: var(--color-primary-700);
1723
+ }
1724
+ .calendar-day-in-range {
1725
+ background-color: color-mix(in oklab, var(--color-primary-500) 12%, transparent);
1726
+ border-radius: 0;
1727
+ }
1728
+ .calendar-day:disabled {
1729
+ @apply opacity-30 cursor-not-allowed;
1730
+ }
1731
+
1732
+ /* TimePicker — scrollable hour/minute columns */
1733
+ .time-picker-popover {
1734
+ min-width: 14rem;
1735
+ }
1736
+ .time-picker-grid {
1737
+ @apply flex items-stretch gap-1;
1738
+ }
1739
+ .time-picker-col {
1740
+ @apply flex-1 flex flex-col gap-0.5 overflow-y-auto py-1;
1741
+ max-height: 14rem;
1742
+ scroll-snap-type: y mandatory;
1743
+ scrollbar-width: thin;
1744
+ }
1745
+ .time-picker-col::-webkit-scrollbar {
1746
+ width: 4px;
1747
+ }
1748
+ .time-picker-col::-webkit-scrollbar-thumb {
1749
+ background-color: var(--color-border-strong);
1750
+ border-radius: 2px;
1751
+ }
1752
+ .time-picker-sep {
1753
+ @apply self-center text-xl font-mono;
1754
+ color: var(--color-fg-muted);
1755
+ }
1756
+ .time-picker-cell {
1757
+ @apply h-8 inline-flex items-center justify-center rounded text-sm font-mono tabular-nums transition-colors;
1758
+ color: var(--color-fg);
1759
+ background: transparent;
1760
+ border: none;
1761
+ cursor: pointer;
1762
+ scroll-snap-align: center;
1763
+ flex-shrink: 0;
1764
+ }
1765
+ .time-picker-cell:hover {
1766
+ background-color: var(--color-bg-muted);
1767
+ }
1768
+ .time-picker-cell-selected {
1769
+ background-color: var(--color-primary-600);
1770
+ color: white;
1771
+ font-weight: 600;
1772
+ }
1773
+ .time-picker-cell-selected:hover {
1774
+ background-color: var(--color-primary-700);
1775
+ }
1776
+
1777
+ /* ============================================================ */
1778
+ /* DataTable */
1779
+ /* ============================================================ */
1780
+ .data-table-root {
1781
+ @apply w-full overflow-x-auto;
1782
+ border: 1px solid var(--color-border);
1783
+ border-radius: var(--ui-radius);
1784
+ }
1785
+ .data-table {
1786
+ @apply w-full text-sm;
1787
+ border-collapse: collapse;
1788
+ }
1789
+ .data-table th,
1790
+ .data-table td {
1791
+ @apply px-3 py-2 text-left align-middle;
1792
+ }
1793
+ .data-table thead th {
1794
+ @apply font-semibold;
1795
+ color: var(--color-fg);
1796
+ background-color: var(--color-bg-muted);
1797
+ border-bottom: 1px solid var(--color-border);
1798
+ }
1799
+ .data-table-sticky thead th {
1800
+ @apply sticky top-0 z-10;
1801
+ }
1802
+ .data-table tbody tr {
1803
+ border-bottom: 1px solid var(--color-border);
1804
+ }
1805
+ .data-table tbody tr:last-child {
1806
+ border-bottom: none;
1807
+ }
1808
+ .data-table tbody tr:hover {
1809
+ background-color: var(--color-bg-muted);
1810
+ }
1811
+ .data-table-striped tbody tr:nth-child(even) {
1812
+ background-color: var(--color-bg-subtle);
1813
+ }
1814
+ .data-table-dense th,
1815
+ .data-table-dense td {
1816
+ @apply py-1;
1817
+ }
1818
+ .data-table-row-clickable {
1819
+ @apply cursor-pointer;
1820
+ }
1821
+ .data-table-empty td {
1822
+ @apply text-center py-6;
1823
+ color: var(--color-fg-muted);
1824
+ }
1825
+ .data-table-sort {
1826
+ @apply inline-flex items-center gap-1;
1827
+ background: transparent;
1828
+ color: inherit;
1829
+ font: inherit;
1830
+ cursor: pointer;
1831
+ }
1832
+ .data-table-sort:hover {
1833
+ color: var(--color-primary-600);
1834
+ }
1835
+
1836
+ /* ============================================================ */
1837
+ /* Tree (recursive) */
1838
+ /* ============================================================ */
1839
+ .tree {
1840
+ @apply text-sm;
1841
+ color: var(--color-fg);
1842
+ }
1843
+ .tree ul {
1844
+ @apply list-none p-0 m-0;
1845
+ }
1846
+ .tree-row {
1847
+ @apply flex w-full items-center gap-2 py-1 pr-2 text-left rounded-sm;
1848
+ @apply transition-colors focus-visible:outline-none focus-visible:ring-2;
1849
+ color: var(--color-fg);
1850
+ background-color: transparent;
1851
+ --tw-ring-color: var(--color-ring);
1852
+ }
1853
+ .tree-row:hover {
1854
+ background-color: var(--color-bg-muted);
1855
+ }
1856
+ .tree-row-selected {
1857
+ background-color: color-mix(in oklab, var(--color-primary-500) 12%, transparent);
1858
+ color: var(--color-fg);
1859
+ }
1860
+ .tree-row-selected:hover {
1861
+ background-color: color-mix(in oklab, var(--color-primary-500) 18%, transparent);
1862
+ }
1863
+ .tree-chevron {
1864
+ @apply inline-flex size-4 shrink-0 items-center justify-center transition-transform duration-150;
1865
+ color: var(--color-fg-muted);
1866
+ }
1867
+ .tree-chevron-open {
1868
+ transform: rotate(90deg);
1869
+ }
1870
+ .tree-chevron-spacer {
1871
+ @apply inline-block size-4 shrink-0;
1872
+ }
1873
+ .tree-label {
1874
+ @apply truncate;
1875
+ }
1876
+
1877
+ /* ============================================================ */
1878
+ /* Carousel */
1879
+ /* ============================================================ */
1880
+ .carousel {
1881
+ @apply relative overflow-hidden w-full;
1882
+ border-radius: var(--ui-radius);
1883
+ background-color: var(--color-bg-muted);
1884
+ }
1885
+ .carousel-track {
1886
+ @apply flex h-full transition-transform duration-500 ease-out;
1887
+ }
1888
+ .carousel-slide {
1889
+ @apply h-full w-full shrink-0;
1890
+ }
1891
+ .carousel-arrow {
1892
+ @apply absolute top-1/2 -translate-y-1/2 inline-flex size-9 items-center justify-center;
1893
+ @apply transition-colors focus-visible:outline-none focus-visible:ring-2;
1894
+ background-color: color-mix(in oklab, var(--color-bg) 70%, transparent);
1895
+ color: var(--color-fg);
1896
+ border: 1px solid var(--color-border);
1897
+ border-radius: var(--ui-radius-full);
1898
+ --tw-ring-color: var(--color-ring);
1899
+ }
1900
+ .carousel-arrow:hover {
1901
+ background-color: var(--color-bg);
1902
+ }
1903
+ .carousel-arrow-prev {
1904
+ @apply left-3;
1905
+ }
1906
+ .carousel-arrow-next {
1907
+ @apply right-3;
1908
+ }
1909
+ .carousel-dots {
1910
+ @apply absolute bottom-3 left-1/2 -translate-x-1/2 inline-flex items-center gap-1.5;
1911
+ }
1912
+ .carousel-dot {
1913
+ @apply size-2 rounded-full transition-colors;
1914
+ background-color: color-mix(in oklab, var(--color-bg-inverse) 50%, transparent);
1915
+ }
1916
+ .carousel-dot-active {
1917
+ background-color: var(--color-bg-inverse);
363
1918
  }
364
1919
  }