@a4ui/core 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +80 -37
  2. package/dist/elements.css +79 -9
  3. package/dist/full.css +3112 -0
  4. package/dist/index.d.ts +13 -1
  5. package/dist/index.js +3392 -2537
  6. package/dist/lib/flyToCart.d.ts +25 -0
  7. package/dist/ui/Curtain.d.ts +44 -0
  8. package/dist/ui/Expandable.d.ts +34 -0
  9. package/dist/ui/FillText.d.ts +18 -0
  10. package/dist/ui/HoldToConfirm.d.ts +24 -0
  11. package/dist/ui/LoadingDots.d.ts +20 -0
  12. package/dist/ui/MultiStateBadge.d.ts +23 -0
  13. package/dist/ui/NotificationStack.d.ts +32 -0
  14. package/dist/ui/NowPlaying.d.ts +21 -0
  15. package/dist/ui/Parallax.d.ts +21 -0
  16. package/dist/ui/ScrambleText.d.ts +22 -0
  17. package/dist/ui/TextReveal.d.ts +23 -0
  18. package/package.json +8 -1
  19. package/src/charts/BarChart.tsx +55 -0
  20. package/src/charts/DonutChart.tsx +127 -0
  21. package/src/charts/Sparkline.tsx +101 -0
  22. package/src/charts/index.ts +5 -0
  23. package/src/commerce/CartLine.tsx +84 -0
  24. package/src/commerce/CartSummary.tsx +70 -0
  25. package/src/commerce/FilterGroup.tsx +70 -0
  26. package/src/commerce/PriceTag.tsx +62 -0
  27. package/src/commerce/ProductCard.tsx +121 -0
  28. package/src/commerce/ProductGrid.tsx +27 -0
  29. package/src/commerce/QuantityStepper.tsx +59 -0
  30. package/src/commerce/index.ts +10 -0
  31. package/src/elements.tsx +167 -0
  32. package/src/index.ts +186 -0
  33. package/src/layout/AppShell.tsx +102 -0
  34. package/src/layout/ChristmasBackground.tsx +177 -0
  35. package/src/layout/EffectsToggle.tsx +41 -0
  36. package/src/layout/NavGroup.tsx +45 -0
  37. package/src/layout/SnowScenery.tsx +76 -0
  38. package/src/layout/SpaceBackground.tsx +459 -0
  39. package/src/layout/ThemeToggle.tsx +40 -0
  40. package/src/layout/ThemedScenery.tsx +179 -0
  41. package/src/layout/sceneEffects.ts +49 -0
  42. package/src/lib/cn.ts +17 -0
  43. package/src/lib/effects.ts +71 -0
  44. package/src/lib/flyToCart.ts +103 -0
  45. package/src/lib/media.ts +27 -0
  46. package/src/lib/motion.ts +191 -0
  47. package/src/lib/theme.ts +113 -0
  48. package/src/lib/virtual.ts +33 -0
  49. package/src/styles/space.css +510 -0
  50. package/src/styles/tokens.css +213 -0
  51. package/src/themes/index.ts +102 -0
  52. package/src/themes/palettes.ts +368 -0
  53. package/src/ui/Accordion.tsx +65 -0
  54. package/src/ui/Affix.tsx +72 -0
  55. package/src/ui/Alert.tsx +55 -0
  56. package/src/ui/AlertDialog.tsx +58 -0
  57. package/src/ui/Anchor.tsx +100 -0
  58. package/src/ui/Avatar.tsx +34 -0
  59. package/src/ui/AvatarGroup.tsx +55 -0
  60. package/src/ui/BackToTop.tsx +51 -0
  61. package/src/ui/Badge.tsx +46 -0
  62. package/src/ui/BottomNavigation.tsx +65 -0
  63. package/src/ui/Breadcrumb.tsx +64 -0
  64. package/src/ui/Button.tsx +49 -0
  65. package/src/ui/Calendar.tsx +40 -0
  66. package/src/ui/CalendarHeatmap.tsx +180 -0
  67. package/src/ui/Card.tsx +84 -0
  68. package/src/ui/Carousel.tsx +121 -0
  69. package/src/ui/Cascader.tsx +158 -0
  70. package/src/ui/Checkbox.tsx +35 -0
  71. package/src/ui/Clock.tsx +221 -0
  72. package/src/ui/Collapse.tsx +48 -0
  73. package/src/ui/ColorPicker.tsx +89 -0
  74. package/src/ui/Combobox.tsx +67 -0
  75. package/src/ui/Command.tsx +158 -0
  76. package/src/ui/Comment.tsx +91 -0
  77. package/src/ui/ContextMenu.tsx +58 -0
  78. package/src/ui/Countdown.tsx +123 -0
  79. package/src/ui/Curtain.tsx +348 -0
  80. package/src/ui/DataGrid.tsx +177 -0
  81. package/src/ui/DateField.tsx +177 -0
  82. package/src/ui/DateRangePicker.tsx +204 -0
  83. package/src/ui/DateTimeField.tsx +68 -0
  84. package/src/ui/Descriptions.tsx +56 -0
  85. package/src/ui/Drawer.tsx +72 -0
  86. package/src/ui/Dropdown.tsx +71 -0
  87. package/src/ui/Dropzone.tsx +88 -0
  88. package/src/ui/Empty.tsx +53 -0
  89. package/src/ui/Expandable.tsx +229 -0
  90. package/src/ui/FileUpload.tsx +198 -0
  91. package/src/ui/FillText.tsx +63 -0
  92. package/src/ui/FloatingActionButton.tsx +48 -0
  93. package/src/ui/Form.tsx +108 -0
  94. package/src/ui/Highlight.tsx +53 -0
  95. package/src/ui/HoldToConfirm.tsx +141 -0
  96. package/src/ui/HoverCard.tsx +42 -0
  97. package/src/ui/Image.tsx +66 -0
  98. package/src/ui/Input.tsx +38 -0
  99. package/src/ui/Kbd.tsx +25 -0
  100. package/src/ui/List.tsx +71 -0
  101. package/src/ui/LoadingDots.tsx +75 -0
  102. package/src/ui/Marquee.tsx +48 -0
  103. package/src/ui/Mentions.tsx +170 -0
  104. package/src/ui/Meter.tsx +57 -0
  105. package/src/ui/Modal.tsx +106 -0
  106. package/src/ui/MultiSelect.tsx +236 -0
  107. package/src/ui/MultiStateBadge.tsx +93 -0
  108. package/src/ui/NotificationCenter.tsx +103 -0
  109. package/src/ui/NotificationStack.tsx +104 -0
  110. package/src/ui/NowPlaying.tsx +100 -0
  111. package/src/ui/NumberInput.tsx +48 -0
  112. package/src/ui/PageHeader.tsx +60 -0
  113. package/src/ui/Pagination.tsx +62 -0
  114. package/src/ui/Parallax.tsx +51 -0
  115. package/src/ui/Popover.tsx +42 -0
  116. package/src/ui/Portal.tsx +34 -0
  117. package/src/ui/Progress.tsx +55 -0
  118. package/src/ui/RadioGroup.tsx +65 -0
  119. package/src/ui/Rating.tsx +98 -0
  120. package/src/ui/Result.tsx +75 -0
  121. package/src/ui/RingProgress.tsx +75 -0
  122. package/src/ui/ScrambleText.tsx +95 -0
  123. package/src/ui/SegmentedControl.tsx +61 -0
  124. package/src/ui/Select.tsx +55 -0
  125. package/src/ui/Separator.tsx +39 -0
  126. package/src/ui/Skeleton.tsx +23 -0
  127. package/src/ui/Slider.tsx +57 -0
  128. package/src/ui/Sortable.tsx +174 -0
  129. package/src/ui/SpeedDial.tsx +85 -0
  130. package/src/ui/Spinner.tsx +33 -0
  131. package/src/ui/Splitter.tsx +115 -0
  132. package/src/ui/Stat.tsx +74 -0
  133. package/src/ui/Stepper.tsx +134 -0
  134. package/src/ui/Switch.tsx +44 -0
  135. package/src/ui/Table.tsx +128 -0
  136. package/src/ui/Tabs.tsx +72 -0
  137. package/src/ui/TagInput.tsx +85 -0
  138. package/src/ui/TextReveal.tsx +93 -0
  139. package/src/ui/Textarea.tsx +38 -0
  140. package/src/ui/TimeField.tsx +298 -0
  141. package/src/ui/Timeline.tsx +89 -0
  142. package/src/ui/Toast.tsx +68 -0
  143. package/src/ui/Toggle.tsx +41 -0
  144. package/src/ui/ToggleGroup.tsx +59 -0
  145. package/src/ui/Tooltip.tsx +42 -0
  146. package/src/ui/Tour.tsx +197 -0
  147. package/src/ui/Transfer.tsx +112 -0
  148. package/src/ui/Tree.tsx +100 -0
  149. package/src/ui/TreeSelect.tsx +151 -0
  150. package/src/ui/VirtualList.tsx +81 -0
  151. package/src/ui/internal/CalendarCore.tsx +276 -0
@@ -0,0 +1,510 @@
1
+ /* A4ui — starfield backdrop styles for <SpaceBackground/>.
2
+ *
3
+ * Extracted from the source project's space-theme.css. Mount SpaceBackground as
4
+ * a single fixed full-viewport layer BEHIND all content (z-index 0), with app
5
+ * content at z-index >= 10 (AppShell already does this). Bundled into
6
+ * `a4ui/styles.css` after the component surfaces. */
7
+
8
+ #space {
9
+ position: fixed;
10
+ inset: 0;
11
+ z-index: 0;
12
+ pointer-events: none;
13
+ overflow: hidden;
14
+ }
15
+
16
+ /* Nebula tinted from the active theme (--primary / --accent / --destructive), so
17
+ switching palettes recolors the whole backdrop. Space's tokens keep it blue. */
18
+ #space .sky {
19
+ position: absolute;
20
+ inset: 0;
21
+ background:
22
+ radial-gradient(1200px 700px at 82% -8%, hsl(var(--primary) / 0.34), transparent 60%),
23
+ radial-gradient(900px 600px at 8% 12%, hsl(var(--accent) / 0.28), transparent 60%),
24
+ radial-gradient(1000px 800px at 50% 120%, hsl(var(--accent) / 0.22), transparent 55%),
25
+ radial-gradient(700px 500px at 30% 55%, hsl(var(--destructive) / 0.12), transparent 60%),
26
+ hsl(var(--background));
27
+ }
28
+ [data-theme='light'] #space .sky {
29
+ background:
30
+ radial-gradient(1100px 680px at 84% -10%, hsl(var(--primary) / 0.22), transparent 60%),
31
+ radial-gradient(900px 600px at 6% 8%, hsl(var(--accent) / 0.2), transparent 60%),
32
+ radial-gradient(1000px 820px at 50% 120%, hsl(var(--accent) / 0.16), transparent 55%),
33
+ radial-gradient(700px 500px at 30% 55%, hsl(var(--destructive) / 0.1), transparent 60%),
34
+ hsl(var(--background));
35
+ }
36
+ @keyframes nebulaShift {
37
+ 0%,
38
+ 100% {
39
+ filter: hue-rotate(0deg);
40
+ }
41
+ 50% {
42
+ filter: hue-rotate(18deg);
43
+ }
44
+ }
45
+
46
+ /* Aurora — dark theme only */
47
+ #space .aurora {
48
+ position: absolute;
49
+ top: -10%;
50
+ left: -10%;
51
+ right: -10%;
52
+ height: 46%;
53
+ background: linear-gradient(
54
+ 180deg,
55
+ hsl(var(--accent) / 0.16),
56
+ hsl(var(--primary) / 0.1) 45%,
57
+ transparent 80%
58
+ );
59
+ filter: blur(40px);
60
+ opacity: 0.55;
61
+ mix-blend-mode: screen;
62
+ }
63
+ [data-theme='light'] #space .aurora {
64
+ display: none;
65
+ }
66
+ @keyframes auroraSway {
67
+ 0%,
68
+ 100% {
69
+ transform: translateX(-2%) scaleY(1);
70
+ opacity: 0.5;
71
+ }
72
+ 50% {
73
+ transform: translateX(3%) scaleY(1.08);
74
+ opacity: 0.68;
75
+ }
76
+ }
77
+
78
+ /* Randomized starfield — each star is generated in SpaceBackground.tsx with a
79
+ random position/size/brightness/bloom, so there's no visible tiling pattern.
80
+ Static (no drift): moving the field behind the frosted cards re-blurs every
81
+ frame and felt laggy; a subset twinkles for life instead. */
82
+ #space #starfield {
83
+ position: absolute;
84
+ inset: 0;
85
+ }
86
+ [data-theme='light'] #space #starfield {
87
+ opacity: 0.85;
88
+ }
89
+ /* A few stars drift smoothly like the satellite: each is a tiny element moving
90
+ via its own transform (GPU-composited, cheap), NOT the whole field. Uses
91
+ `alternate` so it eases out and back with no loop jump. Per-star --dx/--dy. */
92
+ @keyframes starDrift {
93
+ from {
94
+ transform: translate(0, 0);
95
+ }
96
+ to {
97
+ transform: translate(var(--dx, 0), var(--dy, 0));
98
+ }
99
+ }
100
+
101
+ /* Rocket exhaust flame (custom SVG rocket in SpaceBackground.tsx) — flickers. */
102
+ .rk-flame {
103
+ transform-box: fill-box;
104
+ transform-origin: 50% 0%;
105
+ animation: rocketFlame 0.12s ease-in-out infinite alternate;
106
+ }
107
+ @keyframes rocketFlame {
108
+ from {
109
+ transform: scaleY(0.8);
110
+ opacity: 0.85;
111
+ }
112
+ to {
113
+ transform: scaleY(1.06);
114
+ opacity: 1;
115
+ }
116
+ }
117
+
118
+ /* Legacy tiled star layers (kept for parity; the current SpaceBackground scatters
119
+ individual stars into #starfield instead). Sized generously: small dots nearly
120
+ vanish under backdrop-filter blur, so keep them >= 2.5px. */
121
+ #space .stars {
122
+ position: absolute;
123
+ inset: -50%;
124
+ background-repeat: repeat;
125
+ background-image:
126
+ radial-gradient(4px 4px at 20px 30px, hsl(var(--foreground)), transparent),
127
+ radial-gradient(3.6px 3.6px at 120px 90px, hsl(var(--foreground)), transparent),
128
+ radial-gradient(3.2px 3.2px at 210px 160px, hsl(217 91% 78%), transparent),
129
+ radial-gradient(4.8px 4.8px at 300px 60px, hsl(199 89% 82%), transparent),
130
+ radial-gradient(3.4px 3.4px at 380px 220px, hsl(330 80% 84%), transparent),
131
+ radial-gradient(3px 3px at 90px 190px, hsl(var(--foreground)), transparent),
132
+ radial-gradient(2.8px 2.8px at 260px 250px, hsl(48 90% 82%), transparent);
133
+ background-size: 420px 300px;
134
+ opacity: 1;
135
+ animation: drift 140s linear infinite;
136
+ }
137
+ [data-theme='light'] #space .stars {
138
+ background-image:
139
+ radial-gradient(4px 4px at 20px 30px, hsl(38 92% 50%), transparent),
140
+ radial-gradient(3.6px 3.6px at 120px 90px, hsl(42 90% 52%), transparent),
141
+ radial-gradient(3.2px 3.2px at 210px 160px, hsl(32 88% 48%), transparent),
142
+ radial-gradient(4.8px 4.8px at 300px 60px, hsl(45 95% 55%), transparent),
143
+ radial-gradient(3.4px 3.4px at 380px 220px, hsl(28 85% 50%), transparent),
144
+ radial-gradient(3px 3px at 90px 190px, hsl(40 90% 50%), transparent),
145
+ radial-gradient(2.8px 2.8px at 260px 250px, hsl(48 90% 52%), transparent);
146
+ }
147
+ #space .stars.s2 {
148
+ background-size: 640px 480px;
149
+ opacity: 0.85;
150
+ animation-duration: 200s;
151
+ }
152
+ #space .stars.s3 {
153
+ background-size: 260px 190px;
154
+ opacity: 1;
155
+ animation-duration: 95s;
156
+ background-image:
157
+ radial-gradient(6px 6px at 40px 50px, hsl(var(--foreground)), transparent),
158
+ radial-gradient(5.6px 5.6px at 160px 130px, hsl(199 89% 85%), transparent),
159
+ radial-gradient(5.8px 5.8px at 250px 40px, hsl(275 80% 86%), transparent),
160
+ radial-gradient(5px 5px at 90px 20px, hsl(330 85% 85%), transparent);
161
+ }
162
+ [data-theme='light'] #space .stars.s3 {
163
+ opacity: 0.8;
164
+ background-image:
165
+ radial-gradient(6px 6px at 40px 50px, hsl(38 92% 50%), transparent),
166
+ radial-gradient(5.6px 5.6px at 160px 130px, hsl(45 95% 54%), transparent),
167
+ radial-gradient(5.8px 5.8px at 250px 40px, hsl(30 88% 48%), transparent),
168
+ radial-gradient(5px 5px at 90px 20px, hsl(50 92% 55%), transparent);
169
+ }
170
+ @keyframes drift {
171
+ from {
172
+ transform: translate3d(0, 0, 0);
173
+ }
174
+ to {
175
+ transform: translate3d(-420px, -300px, 0);
176
+ }
177
+ }
178
+
179
+ /* Individually-placed twinkling stars */
180
+ .twinkle {
181
+ position: absolute;
182
+ width: 5px;
183
+ height: 5px;
184
+ border-radius: 50%;
185
+ background: hsl(var(--foreground));
186
+ box-shadow: 0 0 14px 3px hsl(var(--foreground) / 0.9);
187
+ animation: twinkle 3.2s ease-in-out infinite;
188
+ }
189
+ [data-theme='light'] .twinkle {
190
+ background: hsl(40 92% 50%);
191
+ box-shadow: 0 0 14px 3px hsl(40 92% 50% / 0.85);
192
+ }
193
+ @keyframes twinkle {
194
+ 0%,
195
+ 100% {
196
+ opacity: 0.25;
197
+ transform: scale(0.6);
198
+ }
199
+ 50% {
200
+ opacity: 1;
201
+ transform: scale(1.15);
202
+ }
203
+ }
204
+
205
+ /* Constellations — thin line art connecting a handful of stars */
206
+ #space .constellation {
207
+ position: absolute;
208
+ opacity: 1;
209
+ }
210
+ [data-theme='light'] #space .constellation {
211
+ opacity: 0.6;
212
+ }
213
+ [data-theme='light'] #space .constellation .cline {
214
+ stroke: hsl(38 92% 50%) !important;
215
+ }
216
+ [data-theme='light'] #space .constellation .cdot {
217
+ fill: hsl(222 47% 20%) !important;
218
+ }
219
+
220
+ /* Planets / moons — decorative, corner-anchored, gentle float */
221
+ .planet {
222
+ border-radius: 50%;
223
+ }
224
+ .planet::after {
225
+ content: '';
226
+ position: absolute;
227
+ inset: -34% -55%;
228
+ border-radius: 50%;
229
+ border: 3px solid hsl(var(--foreground) / 0.18);
230
+ transform: rotate(-24deg);
231
+ }
232
+ .planet-glow {
233
+ border-radius: 50%;
234
+ filter: blur(2px);
235
+ }
236
+ @keyframes floaty {
237
+ 0%,
238
+ 100% {
239
+ transform: translateY(0) rotate(0deg);
240
+ }
241
+ 50% {
242
+ transform: translateY(-14px) rotate(4deg);
243
+ }
244
+ }
245
+ .floaty {
246
+ animation: floaty 12s ease-in-out infinite;
247
+ }
248
+ .floaty.slow {
249
+ animation-duration: 18s;
250
+ }
251
+
252
+ /* Asteroid belt (small drifting rocks) */
253
+ .asteroid {
254
+ border-radius: 40% 55% 50% 45%;
255
+ background: hsl(var(--foreground) / 0.35);
256
+ }
257
+ @keyframes asteroidDrift {
258
+ 0%,
259
+ 100% {
260
+ transform: translate(0, 0) rotate(0deg);
261
+ }
262
+ 50% {
263
+ transform: translate(-8px, 6px) rotate(25deg);
264
+ }
265
+ }
266
+
267
+ /* Shooting star — bright core + tapered glowing trail */
268
+ .shooter {
269
+ position: absolute;
270
+ height: 2.5px;
271
+ border-radius: 999px;
272
+ transform-origin: left center;
273
+ background: linear-gradient(
274
+ 90deg,
275
+ hsla(0, 0%, 100%, 0) 0%,
276
+ hsla(199, 90%, 85%, 0.55) 55%,
277
+ hsla(0, 0%, 100%, 0.98) 100%
278
+ );
279
+ filter: blur(0.2px) drop-shadow(0 0 3px hsla(199, 90%, 80%, 0.5));
280
+ }
281
+ .shooter::before {
282
+ content: '';
283
+ position: absolute;
284
+ right: -2px;
285
+ top: 50%;
286
+ width: 6px;
287
+ height: 6px;
288
+ border-radius: 50%;
289
+ transform: translateY(-50%);
290
+ background: radial-gradient(circle, #fff 0%, hsla(199, 95%, 88%, 0.95) 50%, transparent 75%);
291
+ box-shadow:
292
+ 0 0 8px 2px #fff,
293
+ 0 0 20px 7px hsla(199, 90%, 75%, 0.85),
294
+ 0 0 38px 14px hsla(199, 90%, 60%, 0.4);
295
+ }
296
+ .shooter::after {
297
+ content: '';
298
+ position: absolute;
299
+ left: 0;
300
+ top: 50%;
301
+ width: 55%;
302
+ height: 1px;
303
+ transform: translateY(-50%);
304
+ background: hsla(199, 85%, 92%, 0.65);
305
+ filter: blur(2px);
306
+ }
307
+
308
+ /* Satellite (drifting glyph, e.g. a lucide "satellite" icon) */
309
+ @keyframes satelliteDrift {
310
+ from {
311
+ transform: translate(0, 0) rotate(-18deg);
312
+ }
313
+ to {
314
+ transform: translate(126vw, 34vh) rotate(-18deg);
315
+ }
316
+ }
317
+ #satellite {
318
+ animation: satelliteDrift 46s linear infinite;
319
+ }
320
+
321
+ /* Cursor-following ambient glow (nebula that trails the pointer) */
322
+ #cursorGlow {
323
+ border-radius: 50%;
324
+ transform: translate(-50%, -50%);
325
+ background: radial-gradient(circle, hsl(217 91% 60% / 0.1), transparent 70%);
326
+ opacity: 0;
327
+ transition: opacity 0.4s ease;
328
+ }
329
+
330
+ @media (prefers-reduced-motion: reduce) {
331
+ html:not(.force-motion) #space .stars,
332
+ html:not(.force-motion) .twinkle,
333
+ html:not(.force-motion) .floaty,
334
+ html:not(.force-motion) .shooter,
335
+ html:not(.force-motion) #satellite,
336
+ html:not(.force-motion) .asteroid {
337
+ animation: none !important;
338
+ }
339
+ html:not(.force-motion) #cursorGlow {
340
+ display: none !important;
341
+ }
342
+ html:not(.force-motion) #space .sky,
343
+ html:not(.force-motion) #space .aurora {
344
+ animation: none !important;
345
+ }
346
+ .card.glow-edge::before {
347
+ display: none;
348
+ }
349
+ }
350
+
351
+ /* ---- Generic themed scenery (non-space themes) --------------------------
352
+ A themed nebula (same token-tinted gradients as the starfield) plus a field
353
+ of slowly floating motif glyphs — <ThemedScenery motifs={[...]} />. Lighter
354
+ than the bespoke SpaceBackground so every extra theme costs almost nothing. */
355
+ #scenery {
356
+ position: fixed;
357
+ inset: 0;
358
+ z-index: 0;
359
+ pointer-events: none;
360
+ overflow: hidden;
361
+ }
362
+ #scenery .themed-sky {
363
+ position: absolute;
364
+ inset: 0;
365
+ animation: sceneryPulse 16s ease-in-out infinite;
366
+ background:
367
+ radial-gradient(1200px 700px at 82% -8%, hsl(var(--primary) / 0.34), transparent 60%),
368
+ radial-gradient(900px 600px at 8% 12%, hsl(var(--accent) / 0.28), transparent 60%),
369
+ radial-gradient(1000px 800px at 50% 120%, hsl(var(--accent) / 0.22), transparent 55%),
370
+ radial-gradient(700px 500px at 30% 55%, hsl(var(--destructive) / 0.12), transparent 60%),
371
+ hsl(var(--background));
372
+ }
373
+ [data-theme='light'] #scenery .themed-sky {
374
+ background:
375
+ radial-gradient(1100px 680px at 84% -10%, hsl(var(--primary) / 0.22), transparent 60%),
376
+ radial-gradient(900px 600px at 6% 8%, hsl(var(--accent) / 0.2), transparent 60%),
377
+ radial-gradient(1000px 820px at 50% 120%, hsl(var(--accent) / 0.16), transparent 55%),
378
+ radial-gradient(700px 500px at 30% 55%, hsl(var(--destructive) / 0.1), transparent 60%),
379
+ hsl(var(--background));
380
+ }
381
+ #scenery .motif {
382
+ position: absolute;
383
+ user-select: none;
384
+ will-change: transform;
385
+ animation: motifFloat var(--dur, 9s) ease-in-out var(--delay, 0s) infinite;
386
+ }
387
+ @keyframes motifFloat {
388
+ 0%,
389
+ 100% {
390
+ transform: translateY(0) rotate(var(--r, 0deg));
391
+ }
392
+ 50% {
393
+ transform: translateY(-16px) rotate(calc(var(--r, 0deg) + 5deg));
394
+ }
395
+ }
396
+ /* Subtle nebula "breathing" so the themed backdrop isn't static. */
397
+ @keyframes sceneryPulse {
398
+ 0%,
399
+ 100% {
400
+ filter: brightness(1);
401
+ }
402
+ 50% {
403
+ filter: brightness(1.06);
404
+ }
405
+ }
406
+ @media (prefers-reduced-motion: reduce) {
407
+ html:not(.force-motion) #scenery .motif,
408
+ html:not(.force-motion) #scenery .themed-sky {
409
+ animation: none !important;
410
+ }
411
+ }
412
+
413
+ /* ---- Snow scenery (the "snow" theme) ------------------------------------ */
414
+ #snow,
415
+ #xmas {
416
+ position: fixed;
417
+ inset: 0;
418
+ z-index: 0;
419
+ pointer-events: none;
420
+ overflow: hidden;
421
+ }
422
+ #snow .snow-sky {
423
+ position: absolute;
424
+ inset: 0;
425
+ background:
426
+ radial-gradient(1200px 800px at 50% -12%, hsl(var(--primary) / 0.18), transparent 60%),
427
+ radial-gradient(900px 700px at 82% 8%, hsl(var(--accent) / 0.14), transparent 60%), hsl(var(--background));
428
+ }
429
+ /* Snowflakes (shared by snow + christmas). Each drifts down with a sideways sway. */
430
+ #snow .flake,
431
+ #xmas .flake {
432
+ position: absolute;
433
+ top: -12px;
434
+ border-radius: 50%;
435
+ background: #fff;
436
+ box-shadow: 0 0 4px rgba(255, 255, 255, 0.6);
437
+ will-change: transform;
438
+ }
439
+ [data-theme='light'] #snow .flake,
440
+ [data-theme='light'] #xmas .flake {
441
+ background: hsl(205 45% 82%);
442
+ box-shadow: 0 0 3px hsl(205 45% 72% / 0.6);
443
+ }
444
+ @keyframes snowFall {
445
+ 0% {
446
+ transform: translate(0, -12px);
447
+ }
448
+ 100% {
449
+ transform: translate(var(--sway, 12px), 112vh);
450
+ }
451
+ }
452
+ /* Snow settling at the bottom + a frosted cap on cards (snow theme only). */
453
+ #snow .snow-bank {
454
+ position: absolute;
455
+ inset: auto 0 0 0;
456
+ height: 9vh;
457
+ background: linear-gradient(to top, hsl(0 0% 100% / 0.5), transparent);
458
+ filter: blur(2px);
459
+ }
460
+ [data-theme='light'] #snow .snow-bank {
461
+ background: linear-gradient(to top, hsl(0 0% 100% / 0.95), transparent);
462
+ }
463
+ [data-scene='snow'] .card::after {
464
+ content: '';
465
+ position: absolute;
466
+ top: -3px;
467
+ left: 10px;
468
+ right: 10px;
469
+ height: 5px;
470
+ border-radius: 9999px;
471
+ background: hsl(0 0% 100% / 0.8);
472
+ filter: blur(1.5px);
473
+ pointer-events: none;
474
+ }
475
+
476
+ /* ---- Christmas scenery (the "christmas" theme) -------------------------- */
477
+ #xmas .xmas-sky {
478
+ position: absolute;
479
+ inset: 0;
480
+ background:
481
+ radial-gradient(1000px 700px at 50% -12%, hsl(var(--primary) / 0.14), transparent 55%),
482
+ radial-gradient(900px 600px at 15% 16%, hsl(var(--accent) / 0.16), transparent 60%),
483
+ hsl(var(--background));
484
+ }
485
+ #xmas .light {
486
+ position: absolute;
487
+ top: 10px;
488
+ width: 9px;
489
+ height: 9px;
490
+ border-radius: 50%;
491
+ animation: twinkleLight 2.2s ease-in-out infinite;
492
+ }
493
+ @keyframes twinkleLight {
494
+ 0%,
495
+ 100% {
496
+ opacity: 1;
497
+ box-shadow: 0 0 9px currentColor;
498
+ }
499
+ 50% {
500
+ opacity: 0.35;
501
+ box-shadow: 0 0 3px currentColor;
502
+ }
503
+ }
504
+ @media (prefers-reduced-motion: reduce) {
505
+ html:not(.force-motion) #snow .flake,
506
+ html:not(.force-motion) #xmas .flake,
507
+ html:not(.force-motion) #xmas .light {
508
+ animation: none !important;
509
+ }
510
+ }
@@ -0,0 +1,213 @@
1
+ /* A4ui — Spatial Glass design tokens + motion.
2
+ *
3
+ * Pure CSS (no @tailwind / @apply) so it can be imported directly by any
4
+ * consumer: `import 'a4ui/styles.css'`. Dark "night" theme is the default;
5
+ * `data-theme="light"` on the root opts into the light palette.
6
+ *
7
+ * Extracted verbatim from the source project
8
+ * (sonora precision/alfredorust/solid/src/index.css) — keep in sync until that
9
+ * app consumes this package instead. */
10
+
11
+ :root {
12
+ --background: 222 47% 7%;
13
+ --foreground: 213 31% 91%;
14
+ --card: 222 40% 11%;
15
+ --card-foreground: 213 31% 91%;
16
+ --muted: 217 33% 17%;
17
+ /* 75% (not 65%) for WCAG AA contrast on glass/dark surfaces (pa11y/axe). */
18
+ --muted-foreground: 215 25% 75%;
19
+ --border: 217 30% 22%;
20
+ --input: 217 30% 22%;
21
+ --primary: 217 91% 52%;
22
+ --primary-foreground: 0 0% 100%;
23
+ --accent: 199 89% 55%;
24
+ --accent-foreground: 0 0% 100%;
25
+ --ring: 217 91% 52%;
26
+ --destructive: 0 72% 52%;
27
+ --destructive-foreground: 0 0% 100%;
28
+
29
+ /* Semantic data colors (financial direction) — constant across themes. */
30
+ --data-emit: 160 84% 39%;
31
+ --data-received: 347 89% 61%;
32
+ --data-net: 199 89% 48%;
33
+
34
+ --font-sans: 'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif;
35
+ --font-mono: 'IBM Plex Mono', ui-monospace, monospace;
36
+ --sidebar-w: 248px;
37
+ --sidebar-w-collapsed: 68px;
38
+
39
+ /* Shadow color for glass surfaces (consumed by the glass plugin in preset.js).
40
+ Undefined in the source project, so its .card box-shadow silently dropped —
41
+ defined here so the intended elevation renders. Low alpha in use, so one
42
+ dark value covers both themes. */
43
+ --shadow: 222 47% 4%;
44
+ --radius-xl: 1rem;
45
+ }
46
+
47
+ :root[data-theme='light'] {
48
+ --background: 214 32% 95%;
49
+ --foreground: 222 47% 11%;
50
+ --card: 0 0% 100%;
51
+ --card-foreground: 222 47% 11%;
52
+ --muted: 214 30% 92%;
53
+ /* 38% (not 47%) for WCAG AA contrast on light/glass surfaces (pa11y/axe). */
54
+ --muted-foreground: 215 22% 38%;
55
+ --border: 214 24% 84%;
56
+ --input: 214 32% 91%;
57
+ --primary: 217 91% 52%;
58
+ --primary-foreground: 0 0% 100%;
59
+ --accent: 199 89% 48%;
60
+ --accent-foreground: 0 0% 100%;
61
+ --ring: 217 91% 52%;
62
+ --destructive: 0 72% 50%;
63
+ --destructive-foreground: 0 0% 100%;
64
+ }
65
+
66
+ body {
67
+ background: hsl(var(--background));
68
+ color: hsl(var(--foreground));
69
+ font-family: var(--font-sans);
70
+ -webkit-font-smoothing: antialiased;
71
+ font-feature-settings: 'cv02', 'cv03', 'cv04';
72
+ }
73
+
74
+ /* ---- Motion (transform/opacity only, short & functional) --------------- */
75
+
76
+ /* Route/page cross-fade (AppShell wraps routed content in a <Transition>). */
77
+ .page-enter-active,
78
+ .page-exit-active {
79
+ transition: opacity 0.16s ease-out;
80
+ }
81
+ .page-enter,
82
+ .page-exit-to {
83
+ opacity: 0;
84
+ }
85
+
86
+ /* Modal — MUST be @keyframes (Kobalte Dialog presence listens for
87
+ animationend, not transitionend, or the exit never resolves). */
88
+ @keyframes modal-overlay-in {
89
+ from {
90
+ opacity: 0;
91
+ }
92
+ to {
93
+ opacity: 1;
94
+ }
95
+ }
96
+ @keyframes modal-overlay-out {
97
+ from {
98
+ opacity: 1;
99
+ }
100
+ to {
101
+ opacity: 0;
102
+ }
103
+ }
104
+ @keyframes modal-content-in {
105
+ from {
106
+ opacity: 0;
107
+ transform: scale(0.96) translateY(4px);
108
+ }
109
+ to {
110
+ opacity: 1;
111
+ transform: scale(1) translateY(0);
112
+ }
113
+ }
114
+ @keyframes modal-content-out {
115
+ from {
116
+ opacity: 1;
117
+ transform: scale(1) translateY(0);
118
+ }
119
+ to {
120
+ opacity: 0;
121
+ transform: scale(0.96) translateY(4px);
122
+ }
123
+ }
124
+ .modal-overlay[data-expanded] {
125
+ animation: modal-overlay-in 0.15s ease-out;
126
+ }
127
+ .modal-overlay[data-closed] {
128
+ animation: modal-overlay-out 0.15s ease-in;
129
+ }
130
+ .modal-content[data-expanded] {
131
+ animation: modal-content-in 0.18s ease-out;
132
+ }
133
+ .modal-content[data-closed] {
134
+ animation: modal-content-out 0.15s ease-in;
135
+ }
136
+
137
+ /* Toast — same animationend-only presence mechanism as Dialog. */
138
+ @keyframes toast-in {
139
+ from {
140
+ opacity: 0;
141
+ transform: translateX(16px);
142
+ }
143
+ to {
144
+ opacity: 1;
145
+ transform: translateX(0);
146
+ }
147
+ }
148
+ @keyframes toast-out {
149
+ from {
150
+ opacity: 1;
151
+ transform: translateX(0);
152
+ }
153
+ to {
154
+ opacity: 0;
155
+ transform: translateX(16px);
156
+ }
157
+ }
158
+ .toast-item[data-opened] {
159
+ animation: toast-in 0.2s ease-out;
160
+ }
161
+ .toast-item[data-closed] {
162
+ animation: toast-out 0.18s ease-in;
163
+ }
164
+
165
+ /* Table row enter/exit fade. */
166
+ .row-enter-active,
167
+ .row-exit-active {
168
+ transition: opacity 0.15s ease-out;
169
+ }
170
+ .row-enter,
171
+ .row-exit-to {
172
+ opacity: 0;
173
+ }
174
+
175
+ /* Drawer / Modal drawer-variant slide-over (Kobalte Dialog presence →
176
+ animationend keyframes, keyed off data-expanded/data-closed like Modal). */
177
+ @keyframes drawer-in {
178
+ from {
179
+ transform: translateX(100%);
180
+ }
181
+ to {
182
+ transform: translateX(0);
183
+ }
184
+ }
185
+ @keyframes drawer-out {
186
+ from {
187
+ transform: translateX(0);
188
+ }
189
+ to {
190
+ transform: translateX(100%);
191
+ }
192
+ }
193
+ .drawer-content[data-expanded] {
194
+ animation: drawer-in 0.28s cubic-bezier(0.16, 1, 0.3, 1);
195
+ }
196
+ .drawer-content[data-closed] {
197
+ animation: drawer-out 0.2s ease-in;
198
+ }
199
+
200
+ /* Reduced motion: resolve near-instantly rather than removing outright, so
201
+ libraries that gate DOM unmount on transitionend/animationend (Kobalte
202
+ Dialog/Toast, solid-transition-group) still fire the event. Add
203
+ `force-motion` to <html> to opt back in (the ⚡ / effects toggle). */
204
+ @media (prefers-reduced-motion: reduce) {
205
+ html:not(.force-motion) *,
206
+ html:not(.force-motion) *::before,
207
+ html:not(.force-motion) *::after {
208
+ animation-duration: 0.001ms !important;
209
+ animation-iteration-count: 1 !important;
210
+ transition-duration: 0.001ms !important;
211
+ scroll-behavior: auto !important;
212
+ }
213
+ }