strata-css 1.0.4 → 1.1.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.
@@ -1,571 +1,573 @@
1
- /**
2
- * Strata Base Layer
3
- * CSS custom properties, normalize, reset and theme system
4
- * Light / Dark / Dim themes out of the box
5
- * System preference respected via prefers-color-scheme
6
- */
7
-
8
- 'use strict'
9
-
10
- const BASE_CSS = `
11
- /* ─── Strata Layer Declaration ─────────────────────────────────────── */
12
- /* Each breakpoint gets its own named layer declared in ascending order. */
13
- /* This means st-components-md always beats st-components regardless */
14
- /* of where classes appear in HTML — source order never matters. */
15
- @layer
16
- st-base,
17
-
18
- /* Component layers — xs first, xxl last so higher breakpoints always win */
19
- st-components,
20
- st-components-xs,
21
- st-components-sm,
22
- st-components-md,
23
- st-components-lg,
24
- st-components-xl,
25
- st-components-xxl,
26
-
27
- /* Utility layers — same pattern */
28
- st-utilities,
29
- st-utilities-xs,
30
- st-utilities-sm,
31
- st-utilities-md,
32
- st-utilities-lg,
33
- st-utilities-xl,
34
- st-utilities-xxl,
35
-
36
- /* Skeleton always on top */
37
- st-skeleton;
38
-
39
- /* ─── CSS Custom Properties — Light Theme (default) ────────────────── */
40
- @layer st-base {
41
- :root {
42
- /* Brand colors */
43
- --st-primary: #0d6efd;
44
- --st-primary-hover: #0b5ed7;
45
- --st-secondary: #6c757d;
46
- --st-secondary-hover: #5c636a;
47
- --st-success: #198754;
48
- --st-success-hover: #157347;
49
- --st-danger: #dc3545;
50
- --st-danger-hover: #bb2d3b;
51
- --st-warning: #ffc107;
52
- --st-warning-hover: #ffca2c;
53
- --st-info: #0dcaf0;
54
- --st-info-hover: #31d2f2;
55
- --st-light: #f8f9fa;
56
- --st-dark: #212529;
57
-
58
- /* Base */
59
- --st-bg: #ffffff;
60
- --st-bg-secondary: #f8f9fa;
61
- --st-text: #212529;
62
- --st-text-muted: #6c757d;
63
- --st-border: #dee2e6;
64
- --st-border-radius: 0.375rem;
65
- --st-border-width: 1px;
66
-
67
- /* Typography */
68
- --st-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
69
- --st-font-size-base: 1rem;
70
- --st-line-height-base: 1.5;
71
- --st-font-weight-base: 400;
72
- --st-heading-weight: 600;
73
-
74
- /* Spacing */
75
- --st-gutter-x: 1.5rem;
76
- --st-gutter-y: 0;
77
-
78
- /* Shadows */
79
- --st-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
80
- --st-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
81
- --st-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
82
-
83
- /* Transitions */
84
- --st-transition: all 0.2s ease-in-out;
85
- --st-transition-fast: all 0.1s ease-in-out;
86
-
87
- /* Z-index scale */
88
- --st-z-dropdown: 1000;
89
- --st-z-sticky: 1020;
90
- --st-z-fixed: 1030;
91
- --st-z-modal-backdrop: 1040;
92
- --st-z-modal: 1050;
93
- --st-z-tooltip: 1060;
94
- --st-z-toast: 1070;
95
-
96
- /* Focus ring */
97
- --st-focus-ring: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
98
-
99
- /* Skeleton loader */
100
- --st-skeleton-base: #e2e8f0;
101
- --st-skeleton-shine: #f8fafc;
102
- --st-skeleton-duration: 1.5s;
103
- --st-skeleton-radius: 4px;
104
- }
105
-
106
- /* ─── Dark Theme ──────────────────────────────────────────────────── */
107
- [data-st-theme="dark"] {
108
- --st-primary: #6ea8fe;
109
- --st-primary-hover: #8bb9fe;
110
- --st-secondary: #a0a7ae;
111
- --st-secondary-hover: #b4bac0;
112
- --st-success: #75b798;
113
- --st-success-hover: #8ecbaf;
114
- --st-danger: #ea868f;
115
- --st-danger-hover: #f1a1a8;
116
- --st-warning: #ffda6a;
117
- --st-warning-hover: #ffe083;
118
- --st-info: #6edff6;
119
- --st-info-hover: #88e5f7;
120
- --st-light: #f8f9fa;
121
- --st-dark: #adb5bd;
122
-
123
- --st-bg: #212529;
124
- --st-bg-secondary: #2b3035;
125
- --st-text: #dee2e6;
126
- --st-text-muted: #8c959e;
127
- --st-border: #495057;
128
-
129
- --st-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.3);
130
- --st-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5);
131
- --st-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.6);
132
-
133
- --st-focus-ring: 0 0 0 0.25rem rgba(110, 168, 254, 0.25);
134
-
135
- /* Skeleton loader */
136
- --st-skeleton-base: #2d3748;
137
- --st-skeleton-shine: #4a5568;
138
- }
139
- [data-st-theme="dim"] {
140
- --st-primary: #90cdf4;
141
- --st-primary-hover: #a8d8f6;
142
- --st-secondary: #9ca3af;
143
- --st-secondary-hover: #b0b7be;
144
- --st-success: #86efac;
145
- --st-success-hover: #9af2bc;
146
- --st-danger: #fca5a5;
147
- --st-danger-hover: #fdb7b7;
148
- --st-warning: #fcd34d;
149
- --st-warning-hover: #fdda65;
150
- --st-info: #67e8f9;
151
- --st-info-hover: #80ecfa;
152
- --st-light: #374151;
153
- --st-dark: #d1d5db;
154
-
155
- --st-bg: #2d3748;
156
- --st-bg-secondary: #374151;
157
- --st-text: #e2e8f0;
158
- --st-text-muted: #9ca3af;
159
- --st-border: #4a5568;
160
-
161
- --st-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.25);
162
- --st-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.4);
163
- --st-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.5);
164
-
165
- --st-focus-ring: 0 0 0 0.25rem rgba(144, 205, 244, 0.25);
166
-
167
- /* Skeleton loader */
168
- --st-skeleton-base: #374151;
169
- --st-skeleton-shine: #4b5563;
170
- }
171
-
172
- /* ─── System Preference — no data-st-theme set ────────────────────── */
173
- @media (prefers-color-scheme: dark) {
174
- :root:not([data-st-theme="light"]):not([data-st-theme="dim"]):not([data-st-theme="dark"]) {
175
- --st-primary: #6ea8fe;
176
- --st-primary-hover: #8bb9fe;
177
- --st-bg: #212529;
178
- --st-bg-secondary: #2b3035;
179
- --st-text: #dee2e6;
180
- --st-text-muted: #8c959e;
181
- --st-border: #495057;
182
- --st-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5);
183
- --st-focus-ring: 0 0 0 0.25rem rgba(110, 168, 254, 0.25);
184
- --st-skeleton-base: #2d3748;
185
- --st-skeleton-shine: #4a5568;
186
- }
187
- }
188
-
189
- /* ─── Normalize & Reset ───────────────────────────────────────────── */
190
- *,
191
- *::before,
192
- *::after {
193
- box-sizing: border-box;
194
- }
195
-
196
- html {
197
- font-size: 16px;
198
- -webkit-text-size-adjust: 100%;
199
- -webkit-tap-highlight-color: transparent;
200
- scroll-behavior: smooth;
201
- }
202
-
203
- body {
204
- margin: 0;
205
- font-family: var(--st-font-family);
206
- font-size: var(--st-font-size-base);
207
- font-weight: var(--st-font-weight-base);
208
- line-height: var(--st-line-height-base);
209
- color: var(--st-text);
210
- background-color: var(--st-bg);
211
- -webkit-font-smoothing: antialiased;
212
- -moz-osx-font-smoothing: grayscale;
213
- }
214
-
215
- h1, h2, h3, h4, h5, h6 {
216
- margin-top: 0;
217
- margin-bottom: 0.5rem;
218
- font-weight: var(--st-heading-weight);
219
- line-height: 1.2;
220
- color: var(--st-text);
221
- }
222
-
223
- h1 { font-size: 2.5rem; }
224
- h2 { font-size: 2rem; }
225
- h3 { font-size: 1.75rem; }
226
- h4 { font-size: 1.5rem; }
227
- h5 { font-size: 1.25rem; }
228
- h6 { font-size: 1rem; }
229
-
230
- p {
231
- margin-top: 0;
232
- margin-bottom: 1rem;
233
- }
234
-
235
- a {
236
- color: var(--st-primary);
237
- text-decoration: underline;
238
- }
239
-
240
- a:hover {
241
- color: var(--st-primary-hover);
242
- }
243
-
244
- img, svg {
245
- vertical-align: middle;
246
- max-width: 100%;
247
- }
248
-
249
- button {
250
- cursor: pointer;
251
- border: none;
252
- background: none;
253
- font-family: inherit;
254
- }
255
-
256
- input, button, select, textarea {
257
- font-family: inherit;
258
- font-size: inherit;
259
- line-height: inherit;
260
- }
261
-
262
- table {
263
- caption-side: bottom;
264
- border-collapse: collapse;
265
- }
266
-
267
- ul, ol {
268
- padding-left: 2rem;
269
- margin-top: 0;
270
- margin-bottom: 1rem;
271
- }
272
-
273
- hr {
274
- margin: 1rem 0;
275
- color: var(--st-border);
276
- border: 0;
277
- border-top: var(--st-border-width) solid var(--st-border);
278
- opacity: 1;
279
- }
280
-
281
- code {
282
- font-size: 0.875em;
283
- color: var(--st-danger);
284
- word-wrap: break-word;
285
- }
286
-
287
- pre {
288
- display: block;
289
- margin-top: 0;
290
- margin-bottom: 1rem;
291
- overflow: auto;
292
- font-size: 0.875em;
293
- color: var(--st-text);
294
- background-color: var(--st-bg-secondary);
295
- border-radius: var(--st-border-radius);
296
- padding: 1rem;
297
- }
298
-
299
- blockquote {
300
- margin: 0 0 1rem;
301
- padding: 0.5rem 1rem;
302
- border-left: 4px solid var(--st-border);
303
- color: var(--st-text-muted);
304
- }
305
-
306
- small {
307
- font-size: 0.875em;
308
- }
309
-
310
- mark {
311
- padding: 0.1875em;
312
- background-color: var(--st-warning);
313
- color: var(--st-dark);
314
- }
315
-
316
- /* ─── Focus visible ───────────────────────────────────────────────── */
317
- :focus-visible {
318
- outline: none;
319
- box-shadow: var(--st-focus-ring);
320
- }
321
-
322
- /* ─── Transition Variables ───────────────────────────────────────── */
323
- :root {
324
- --st-duration: 200ms;
325
- --st-duration-fast: 100ms;
326
- --st-duration-slow: 400ms;
327
- --st-easing: cubic-bezier(0.4, 0, 0.2, 1);
328
- --st-easing-in: cubic-bezier(0.4, 0, 1, 1);
329
- --st-easing-out: cubic-bezier(0, 0, 0.2, 1);
330
- }
331
-
332
- /* ─── Global Interactive Transitions ─────────────────────────────── */
333
- /* Applied to all interactive elements for buttery smooth state changes */
334
-
335
- a,
336
- button,
337
- input,
338
- select,
339
- textarea,
340
- label,
341
- summary {
342
- transition-property: color, background-color, border-color, box-shadow, opacity, transform;
343
- transition-duration: 200ms;
344
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
345
- }
346
-
347
- /* ─── Theme Transition ────────────────────────────────────────────── */
348
- *,
349
- *::before,
350
- *::after {
351
- transition-property: background-color, border-color, color, box-shadow;
352
- transition-duration: 150ms;
353
- transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
354
- }
355
-
356
- /* Prevent theme transition on page load — only on user-triggered changes */
357
- .st-no-transition,
358
- .st-no-transition * {
359
- transition: none !important;
360
- }
361
-
362
- /* ─── Data Attribute State Transitions ───────────────────────────── */
363
- /* Smooth transitions for JS-controlled states via data-st-* attributes */
364
-
365
- /* Visibility fade in/out instead of instant show/hide */
366
- [data-st-visible] {
367
- transition-property: opacity, visibility, transform;
368
- transition-duration: 200ms;
369
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
370
- }
371
-
372
- [data-st-visible="true"] {
373
- opacity: 1;
374
- visibility: visible;
375
- transform: translateY(0);
376
- pointer-events: auto;
377
- }
378
-
379
- [data-st-visible="false"] {
380
- opacity: 0;
381
- visibility: hidden;
382
- transform: translateY(-4px);
383
- pointer-events: none;
384
- }
385
-
386
- /* Active state — subtle scale for tactile feel */
387
- [data-st-active] {
388
- transition-property: color, background-color, border-color, box-shadow, transform;
389
- transition-duration: 150ms;
390
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
391
- }
392
-
393
- /* Collapsed state — for accordions, drawers etc */
394
- [data-st-collapsed] {
395
- transition-property: max-height, opacity, transform;
396
- transition-duration: 300ms;
397
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
398
- overflow: hidden;
399
- }
400
-
401
- [data-st-collapsed="false"] {
402
- max-height: 2000px;
403
- opacity: 1;
404
- }
405
-
406
- [data-st-collapsed="true"] {
407
- max-height: 0;
408
- opacity: 0;
409
- }
410
-
411
- /* Loading state */
412
- [data-st-loading="true"] {
413
- opacity: 0.7;
414
- pointer-events: none;
415
- cursor: wait;
416
- transition: opacity 200ms ease;
417
- }
418
-
419
- /* Disabled state */
420
- [data-st-disabled="true"] {
421
- opacity: 0.5;
422
- pointer-events: none;
423
- cursor: not-allowed;
424
- transition: opacity 200ms ease;
425
- }
426
-
427
- /* ─── Reduced Motion Accessibility ──────────────────────────────── */
428
- /* Covers every element including future ones — no manual list needed */
429
-
430
- @media (prefers-reduced-motion: reduce) {
431
- *,
432
- *::before,
433
- *::after {
434
- transition: none;
435
- animation: none;
436
- }
437
- }
438
- }
439
-
440
- /* ─── Skeleton Shimmer Keyframe ───────────────────────────────────────── */
441
-
442
- @keyframes st-shimmer {
443
- 0% { background-position: 200% 0; }
444
- 100% { background-position: -200% 0; }
445
- }
446
-
447
- /* ─── Skeleton Loader Layer ───────────────────────────────────────────── */
448
-
449
- @layer st-skeleton {
450
-
451
- /* ── Skeleton variables ───────────────────────────────────────────── */
452
- /* --st-skeleton-radius controls shimmer bar corner rounding */
453
- /* Separate from --st-border-radius so developer controls both */
454
- /* independently. Default 4px subtle like YouTube. */
455
- :root {
456
- --st-skeleton-radius: 4px;
457
- }
458
-
459
- /* ── Parent or individual skeleton element ────────────────────────── */
460
- [data-st-skeleton="true"] {
461
- position: relative;
462
- overflow: hidden;
463
- pointer-events: none;
464
- user-select: none;
465
- cursor: wait;
466
- border-radius: var(--st-skeleton-radius);
467
- }
468
-
469
- /* ── Inline elements — force block context ────────────────────────── */
470
- /* a, span, button, label etc. are inline or inline-flex by default. */
471
- /* overflow:hidden and the ::before overlay only work correctly on */
472
- /* block or inline-block elements. Force them here. */
473
- [data-st-skeleton="true"]:is(a, span, label, strong, em, small, abbr) {
474
- display: inline-block;
475
- }
476
-
477
- /* ── Buttons — force block context and reset padding ─────────────── */
478
- /* button is inline-flex or inline-block. Make it block so the */
479
- /* overlay covers it fully. Preserve its width with fit-content. */
480
- [data-st-skeleton="true"]:is(button, input[type="button"], input[type="submit"], input[type="reset"]) {
481
- display: block;
482
- width: fit-content;
483
- }
484
-
485
- /* ── The shimmer overlay ──────────────────────────────────────────── */
486
- /* ::before sits on top of all child content via z-index: 9999. */
487
- /* border-radius: inherit picks up --st-skeleton-radius from parent. */
488
- [data-st-skeleton="true"]::before {
489
- content: '';
490
- position: absolute;
491
- inset: 0;
492
- z-index: 9999;
493
- border-radius: inherit;
494
- background: linear-gradient(
495
- 90deg,
496
- var(--st-skeleton-base) 25%,
497
- var(--st-skeleton-shine) 50%,
498
- var(--st-skeleton-base) 75%
499
- );
500
- background-size: 200% 100%;
501
- animation: st-shimmer var(--st-skeleton-duration, 1.5s) infinite linear;
502
- }
503
-
504
- /* ── JS managed parent — null state ──────────────────────────────── */
505
- /* JS sets parent to "null" when smart detection takes over. */
506
- /* Parent becomes a neutral container no overlay, no shimmer. */
507
- /* Children are marked individually with "true" and shimmer on their */
508
- /* own. Parent stays completely out of the way visually. */
509
- [data-st-skeleton="null"] {
510
- position: relative;
511
- pointer-events: none;
512
- user-select: none;
513
- cursor: wait;
514
- /* No ::before — no shimmer on parent itself */
515
- /* No overflow:hidden — children manage their own clipping */
516
- }
517
-
518
- /* ── Opt-out works from both true and null parents ───────────────── */
519
- [data-st-skeleton="true"] [data-st-skeleton="false"],
520
- [data-st-skeleton="null"] [data-st-skeleton="false"] {
521
- position: relative;
522
- z-index: 10000;
523
- pointer-events: auto;
524
- cursor: auto;
525
- }
526
-
527
- /* ── Replaced elements fallback ──────────────────────────────────── */
528
- /* img, video, canvas cannot have ::before pseudo-elements. */
529
- /* JS smart detection marks their wrapper instead. */
530
- /* This CSS handles the edge case where developer manually sets */
531
- /* data-st-skeleton="true" directly on an img or video element. */
532
- /* Uses background shimmer + object-position to hide loaded content. */
533
- [data-st-skeleton="true"]:is(img, video, canvas) {
534
- background: linear-gradient(
535
- 90deg,
536
- var(--st-skeleton-base) 25%,
537
- var(--st-skeleton-shine) 50%,
538
- var(--st-skeleton-base) 75%
539
- );
540
- background-size: 200% 100%;
541
- animation: st-shimmer var(--st-skeleton-duration, 1.5s) infinite linear;
542
- /* Push loaded media content outside element bounds */
543
- object-fit: none;
544
- object-position: 9999px 9999px;
545
- /* Override any existing border-radius */
546
- border-radius: var(--st-skeleton-radius);
547
- }
548
-
549
- /* ── Circle variant — for avatars and round elements ─────────────── */
550
- /* border-radius: inherit on ::before picks up 50% automatically. */
551
- /* Nothing extra needed. */
552
-
553
- /* ── Opt-out — child pops above the overlay via z-index ─────────── */
554
- [data-st-skeleton="true"] [data-st-skeleton="false"] {
555
- position: relative;
556
- z-index: 10000;
557
- pointer-events: auto;
558
- cursor: auto;
559
- }
560
-
561
- /* ── Reduced motion — static placeholder, no animation ───────────── */
562
- @media (prefers-reduced-motion: reduce) {
563
- [data-st-skeleton="true"]::before {
564
- animation: none;
565
- background: var(--st-skeleton-base);
566
- }
567
- }
568
- }
569
- `
570
-
571
- module.exports = BASE_CSS
1
+ /**
2
+ * Strata Base Layer
3
+ * CSS custom properties, normalize, reset and theme system
4
+ * Light / Dark / Dim themes out of the box
5
+ * System preference respected via prefers-color-scheme
6
+ */
7
+
8
+ 'use strict'
9
+
10
+ const BASE_CSS = `
11
+ /* ─── Strata Layer Declaration ─────────────────────────────────────── */
12
+ /* Each breakpoint gets its own named layer declared in ascending order. */
13
+ /* This means st-components-md always beats st-components regardless */
14
+ /* of where classes appear in HTML — source order never matters. */
15
+ @layer
16
+ st-base,
17
+
18
+ /* Component layers — xs first, xxl last so higher breakpoints always win */
19
+ st-components,
20
+ st-components-xs,
21
+ st-components-sm,
22
+ st-components-md,
23
+ st-components-lg,
24
+ st-components-xl,
25
+ st-components-xxl,
26
+
27
+ /* Utility layers — same pattern */
28
+ st-utilities,
29
+ st-utilities-xs,
30
+ st-utilities-sm,
31
+ st-utilities-md,
32
+ st-utilities-lg,
33
+ st-utilities-xl,
34
+ st-utilities-xxl,
35
+
36
+ /* Skeleton always on top */
37
+ st-skeleton;
38
+
39
+ /* ─── CSS Custom Properties — Light Theme (default) ────────────────── */
40
+ @layer st-base {
41
+ :root {
42
+ /* Brand colors */
43
+ --st-primary: #0d6efd;
44
+ --st-primary-hover: #0b5ed7;
45
+ --st-secondary: #6c757d;
46
+ --st-secondary-hover: #5c636a;
47
+ --st-success: #198754;
48
+ --st-success-hover: #157347;
49
+ --st-danger: #dc3545;
50
+ --st-danger-hover: #bb2d3b;
51
+ --st-warning: #ffc107;
52
+ --st-warning-hover: #ffca2c;
53
+ --st-info: #0dcaf0;
54
+ --st-info-hover: #31d2f2;
55
+ --st-light: #f8f9fa;
56
+ --st-dark: #212529;
57
+
58
+ /* Base */
59
+ --st-bg: #ffffff;
60
+ --st-bg-secondary: #f8f9fa;
61
+ --st-text: #212529;
62
+ --st-text-muted: #6c757d;
63
+ --st-border: #dee2e6;
64
+ --st-border-radius: 0.375rem;
65
+ --st-border-width: 1px;
66
+
67
+ /* Typography */
68
+ --st-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
69
+ --st-font-size-base: 1rem;
70
+ --st-line-height-base: 1.5;
71
+ --st-font-weight-base: 400;
72
+ --st-heading-weight: 600;
73
+
74
+ /* Spacing */
75
+ --st-gutter-x: 1.5rem;
76
+ --st-gutter-y: 0;
77
+
78
+ /* Shadows */
79
+ --st-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
80
+ --st-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
81
+ --st-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
82
+
83
+ /* Transitions — shorthand helpers (use --st-duration / --st-easing for granular control) */
84
+ --st-transition: all var(--st-duration) var(--st-easing);
85
+ --st-transition-fast: all var(--st-duration-fast) var(--st-easing);
86
+
87
+ /* Z-index scale */
88
+ --st-z-dropdown: 1000;
89
+ --st-z-sticky: 1020;
90
+ --st-z-fixed: 1030;
91
+ --st-z-modal-backdrop: 1040;
92
+ --st-z-modal: 1050;
93
+ --st-z-tooltip: 1060;
94
+ --st-z-toast: 1070;
95
+
96
+ /* Focus ring */
97
+ --st-focus-ring: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
98
+
99
+ /* Skeleton loader */
100
+ --st-skeleton-base: #e2e8f0;
101
+ --st-skeleton-shine: #f8fafc;
102
+ --st-skeleton-duration: 1.5s;
103
+ --st-skeleton-radius: 4px;
104
+ }
105
+
106
+ /* ─── Dark Theme ──────────────────────────────────────────────────── */
107
+ [data-st-theme="dark"] {
108
+ --st-primary: #6ea8fe;
109
+ --st-primary-hover: #8bb9fe;
110
+ --st-secondary: #a0a7ae;
111
+ --st-secondary-hover: #b4bac0;
112
+ --st-success: #75b798;
113
+ --st-success-hover: #8ecbaf;
114
+ --st-danger: #ea868f;
115
+ --st-danger-hover: #f1a1a8;
116
+ --st-warning: #ffda6a;
117
+ --st-warning-hover: #ffe083;
118
+ --st-info: #6edff6;
119
+ --st-info-hover: #88e5f7;
120
+ --st-light: #f8f9fa;
121
+ --st-dark: #adb5bd;
122
+
123
+ --st-bg: #212529;
124
+ --st-bg-secondary: #2b3035;
125
+ --st-text: #dee2e6;
126
+ --st-text-muted: #8c959e;
127
+ --st-border: #495057;
128
+
129
+ --st-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.3);
130
+ --st-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5);
131
+ --st-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.6);
132
+
133
+ --st-focus-ring: 0 0 0 0.25rem rgba(110, 168, 254, 0.25);
134
+
135
+ /* Skeleton loader */
136
+ --st-skeleton-base: #2d3748;
137
+ --st-skeleton-shine: #4a5568;
138
+ }
139
+ [data-st-theme="dim"] {
140
+ --st-primary: #90cdf4;
141
+ --st-primary-hover: #a8d8f6;
142
+ --st-secondary: #9ca3af;
143
+ --st-secondary-hover: #b0b7be;
144
+ --st-success: #86efac;
145
+ --st-success-hover: #9af2bc;
146
+ --st-danger: #fca5a5;
147
+ --st-danger-hover: #fdb7b7;
148
+ --st-warning: #fcd34d;
149
+ --st-warning-hover: #fdda65;
150
+ --st-info: #67e8f9;
151
+ --st-info-hover: #80ecfa;
152
+ --st-light: #374151;
153
+ --st-dark: #d1d5db;
154
+
155
+ --st-bg: #2d3748;
156
+ --st-bg-secondary: #374151;
157
+ --st-text: #e2e8f0;
158
+ --st-text-muted: #9ca3af;
159
+ --st-border: #4a5568;
160
+
161
+ --st-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.25);
162
+ --st-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.4);
163
+ --st-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.5);
164
+
165
+ --st-focus-ring: 0 0 0 0.25rem rgba(144, 205, 244, 0.25);
166
+
167
+ /* Skeleton loader */
168
+ --st-skeleton-base: #374151;
169
+ --st-skeleton-shine: #4b5563;
170
+ }
171
+
172
+ /* ─── System Preference — no data-st-theme set ────────────────────── */
173
+ @media (prefers-color-scheme: dark) {
174
+ :root:not([data-st-theme="light"]):not([data-st-theme="dim"]):not([data-st-theme="dark"]) {
175
+ --st-primary: #6ea8fe;
176
+ --st-primary-hover: #8bb9fe;
177
+ --st-bg: #212529;
178
+ --st-bg-secondary: #2b3035;
179
+ --st-text: #dee2e6;
180
+ --st-text-muted: #8c959e;
181
+ --st-border: #495057;
182
+ --st-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5);
183
+ --st-focus-ring: 0 0 0 0.25rem rgba(110, 168, 254, 0.25);
184
+ --st-skeleton-base: #2d3748;
185
+ --st-skeleton-shine: #4a5568;
186
+ }
187
+ }
188
+
189
+ /* ─── Normalize & Reset ───────────────────────────────────────────── */
190
+ *,
191
+ *::before,
192
+ *::after {
193
+ box-sizing: border-box;
194
+ }
195
+
196
+ html {
197
+ font-size: 16px;
198
+ -webkit-text-size-adjust: 100%;
199
+ -webkit-tap-highlight-color: transparent;
200
+ scroll-behavior: smooth;
201
+ }
202
+
203
+ body {
204
+ margin: 0;
205
+ font-family: var(--st-font-family);
206
+ font-size: var(--st-font-size-base);
207
+ font-weight: var(--st-font-weight-base);
208
+ line-height: var(--st-line-height-base);
209
+ color: var(--st-text);
210
+ background-color: var(--st-bg);
211
+ -webkit-font-smoothing: antialiased;
212
+ -moz-osx-font-smoothing: grayscale;
213
+ }
214
+
215
+ h1, h2, h3, h4, h5, h6 {
216
+ margin-top: 0;
217
+ margin-bottom: 0.5rem;
218
+ font-weight: var(--st-heading-weight);
219
+ line-height: 1.2;
220
+ color: var(--st-text);
221
+ }
222
+
223
+ h1 { font-size: 2.5rem; }
224
+ h2 { font-size: 2rem; }
225
+ h3 { font-size: 1.75rem; }
226
+ h4 { font-size: 1.5rem; }
227
+ h5 { font-size: 1.25rem; }
228
+ h6 { font-size: 1rem; }
229
+
230
+ p {
231
+ margin-top: 0;
232
+ margin-bottom: 1rem;
233
+ }
234
+
235
+ a {
236
+ color: var(--st-primary);
237
+ text-decoration: underline;
238
+ }
239
+
240
+ a:hover {
241
+ color: var(--st-primary-hover);
242
+ }
243
+
244
+ img, svg {
245
+ vertical-align: middle;
246
+ max-width: 100%;
247
+ }
248
+
249
+ button {
250
+ cursor: pointer;
251
+ border: none;
252
+ background: none;
253
+ font-family: inherit;
254
+ }
255
+
256
+ input, button, select, textarea {
257
+ font-family: inherit;
258
+ font-size: inherit;
259
+ line-height: inherit;
260
+ }
261
+
262
+ table {
263
+ caption-side: bottom;
264
+ border-collapse: collapse;
265
+ }
266
+
267
+ ul, ol {
268
+ padding-left: 2rem;
269
+ margin-top: 0;
270
+ margin-bottom: 1rem;
271
+ }
272
+
273
+ hr {
274
+ margin: 1rem 0;
275
+ color: var(--st-border);
276
+ border: 0;
277
+ border-top: var(--st-border-width) solid var(--st-border);
278
+ opacity: 1;
279
+ }
280
+
281
+ code {
282
+ font-size: 0.875em;
283
+ color: var(--st-danger);
284
+ word-wrap: break-word;
285
+ }
286
+
287
+ pre {
288
+ display: block;
289
+ margin-top: 0;
290
+ margin-bottom: 1rem;
291
+ overflow: auto;
292
+ font-size: 0.875em;
293
+ color: var(--st-text);
294
+ background-color: var(--st-bg-secondary);
295
+ border-radius: var(--st-border-radius);
296
+ padding: 1rem;
297
+ }
298
+
299
+ blockquote {
300
+ margin: 0 0 1rem;
301
+ padding: 0.5rem 1rem;
302
+ border-left: 4px solid var(--st-border);
303
+ color: var(--st-text-muted);
304
+ }
305
+
306
+ small {
307
+ font-size: 0.875em;
308
+ }
309
+
310
+ mark {
311
+ padding: 0.1875em;
312
+ background-color: var(--st-warning);
313
+ color: var(--st-dark);
314
+ }
315
+
316
+ /* ─── Focus visible ───────────────────────────────────────────────── */
317
+ :focus-visible {
318
+ outline: none;
319
+ box-shadow: var(--st-focus-ring);
320
+ }
321
+
322
+ /* ─── Transition Variables ───────────────────────────────────────── */
323
+ :root {
324
+ --st-duration: 200ms;
325
+ --st-duration-fast: 100ms;
326
+ --st-duration-slow: 400ms;
327
+ --st-duration-theme: 150ms;
328
+ --st-easing: cubic-bezier(0.4, 0, 0.2, 1);
329
+ --st-easing-in: cubic-bezier(0.4, 0, 1, 1);
330
+ --st-easing-out: cubic-bezier(0, 0, 0.2, 1);
331
+ --st-easing-theme: cubic-bezier(0.22, 1, 0.36, 1);
332
+ }
333
+
334
+ /* ─── Global Interactive Transitions ─────────────────────────────── */
335
+ /* Applied to all interactive elements for buttery smooth state changes */
336
+
337
+ a,
338
+ button,
339
+ input,
340
+ select,
341
+ textarea,
342
+ label,
343
+ summary {
344
+ transition-property: color, background-color, border-color, box-shadow, opacity, transform;
345
+ transition-duration: var(--st-duration);
346
+ transition-timing-function: var(--st-easing);
347
+ }
348
+
349
+ /* ─── Theme Transition ────────────────────────────────────────────── */
350
+ *,
351
+ *::before,
352
+ *::after {
353
+ transition-property: background-color, border-color, color, box-shadow;
354
+ transition-duration: var(--st-duration-theme);
355
+ transition-timing-function: var(--st-easing-theme);
356
+ }
357
+
358
+ /* Prevent theme transition on page load — only on user-triggered changes */
359
+ .st-no-transition,
360
+ .st-no-transition * {
361
+ transition: none !important;
362
+ }
363
+
364
+ /* ─── Data Attribute State Transitions ───────────────────────────── */
365
+ /* Smooth transitions for JS-controlled states via data-st-* attributes */
366
+
367
+ /* Visibility fade in/out instead of instant show/hide */
368
+ [data-st-visible] {
369
+ transition-property: opacity, visibility, transform;
370
+ transition-duration: var(--st-duration);
371
+ transition-timing-function: var(--st-easing);
372
+ }
373
+
374
+ [data-st-visible="true"] {
375
+ opacity: 1;
376
+ visibility: visible;
377
+ transform: translateY(0);
378
+ pointer-events: auto;
379
+ }
380
+
381
+ [data-st-visible="false"] {
382
+ opacity: 0;
383
+ visibility: hidden;
384
+ transform: translateY(-4px);
385
+ pointer-events: none;
386
+ }
387
+
388
+ /* Active state subtle scale for tactile feel */
389
+ [data-st-active] {
390
+ transition-property: color, background-color, border-color, box-shadow, transform;
391
+ transition-duration: var(--st-duration-theme);
392
+ transition-timing-function: var(--st-easing);
393
+ }
394
+
395
+ /* Collapsed state — for accordions, drawers etc */
396
+ [data-st-collapsed] {
397
+ transition-property: max-height, opacity, transform;
398
+ transition-duration: var(--st-duration-slow);
399
+ transition-timing-function: var(--st-easing);
400
+ overflow: hidden;
401
+ }
402
+
403
+ [data-st-collapsed="false"] {
404
+ max-height: 2000px;
405
+ opacity: 1;
406
+ }
407
+
408
+ [data-st-collapsed="true"] {
409
+ max-height: 0;
410
+ opacity: 0;
411
+ }
412
+
413
+ /* Loading state */
414
+ [data-st-loading="true"] {
415
+ opacity: 0.7;
416
+ pointer-events: none;
417
+ cursor: wait;
418
+ transition: opacity var(--st-duration) var(--st-easing);
419
+ }
420
+
421
+ /* Disabled state */
422
+ [data-st-disabled="true"] {
423
+ opacity: 0.5;
424
+ pointer-events: none;
425
+ cursor: not-allowed;
426
+ transition: opacity var(--st-duration) var(--st-easing);
427
+ }
428
+
429
+ /* ─── Reduced Motion Accessibility ──────────────────────────────── */
430
+ /* Covers every element including future ones — no manual list needed */
431
+
432
+ @media (prefers-reduced-motion: reduce) {
433
+ *,
434
+ *::before,
435
+ *::after {
436
+ transition: none;
437
+ animation: none;
438
+ }
439
+ }
440
+ }
441
+
442
+ /* ─── Skeleton Shimmer Keyframe ───────────────────────────────────────── */
443
+
444
+ @keyframes st-shimmer {
445
+ 0% { background-position: 200% 0; }
446
+ 100% { background-position: -200% 0; }
447
+ }
448
+
449
+ /* ─── Skeleton Loader Layer ───────────────────────────────────────────── */
450
+
451
+ @layer st-skeleton {
452
+
453
+ /* ── Skeleton variables ───────────────────────────────────────────── */
454
+ /* --st-skeleton-radius controls shimmer bar corner rounding */
455
+ /* Separate from --st-border-radius so developer controls both */
456
+ /* independently. Default 4px — subtle like YouTube. */
457
+ :root {
458
+ --st-skeleton-radius: 4px;
459
+ }
460
+
461
+ /* ── Parent or individual skeleton element ────────────────────────── */
462
+ [data-st-skeleton="true"] {
463
+ position: relative;
464
+ overflow: hidden;
465
+ pointer-events: none;
466
+ user-select: none;
467
+ cursor: wait;
468
+ border-radius: var(--st-skeleton-radius);
469
+ }
470
+
471
+ /* ── Inline elements force block context ────────────────────────── */
472
+ /* a, span, button, label etc. are inline or inline-flex by default. */
473
+ /* overflow:hidden and the ::before overlay only work correctly on */
474
+ /* block or inline-block elements. Force them here. */
475
+ [data-st-skeleton="true"]:is(a, span, label, strong, em, small, abbr) {
476
+ display: inline-block;
477
+ }
478
+
479
+ /* ── Buttons force block context and reset padding ─────────────── */
480
+ /* button is inline-flex or inline-block. Make it block so the */
481
+ /* overlay covers it fully. Preserve its width with fit-content. */
482
+ [data-st-skeleton="true"]:is(button, input[type="button"], input[type="submit"], input[type="reset"]) {
483
+ display: block;
484
+ width: fit-content;
485
+ }
486
+
487
+ /* ── The shimmer overlay ──────────────────────────────────────────── */
488
+ /* ::before sits on top of all child content via z-index: 9999. */
489
+ /* border-radius: inherit picks up --st-skeleton-radius from parent. */
490
+ [data-st-skeleton="true"]::before {
491
+ content: '';
492
+ position: absolute;
493
+ inset: 0;
494
+ z-index: 9999;
495
+ border-radius: inherit;
496
+ background: linear-gradient(
497
+ 90deg,
498
+ var(--st-skeleton-base) 25%,
499
+ var(--st-skeleton-shine) 50%,
500
+ var(--st-skeleton-base) 75%
501
+ );
502
+ background-size: 200% 100%;
503
+ animation: st-shimmer var(--st-skeleton-duration, 1.5s) infinite linear;
504
+ }
505
+
506
+ /* ── JS managed parentnull state ──────────────────────────────── */
507
+ /* JS sets parent to "null" when smart detection takes over. */
508
+ /* Parent becomes a neutral container no overlay, no shimmer. */
509
+ /* Children are marked individually with "true" and shimmer on their */
510
+ /* own. Parent stays completely out of the way visually. */
511
+ [data-st-skeleton="null"] {
512
+ position: relative;
513
+ pointer-events: none;
514
+ user-select: none;
515
+ cursor: wait;
516
+ /* No ::before — no shimmer on parent itself */
517
+ /* No overflow:hidden — children manage their own clipping */
518
+ }
519
+
520
+ /* ── Opt-out works from both true and null parents ───────────────── */
521
+ [data-st-skeleton="true"] [data-st-skeleton="false"],
522
+ [data-st-skeleton="null"] [data-st-skeleton="false"] {
523
+ position: relative;
524
+ z-index: 10000;
525
+ pointer-events: auto;
526
+ cursor: auto;
527
+ }
528
+
529
+ /* ── Replaced elements fallback ──────────────────────────────────── */
530
+ /* img, video, canvas cannot have ::before pseudo-elements. */
531
+ /* JS smart detection marks their wrapper instead. */
532
+ /* This CSS handles the edge case where developer manually sets */
533
+ /* data-st-skeleton="true" directly on an img or video element. */
534
+ /* Uses background shimmer + object-position to hide loaded content. */
535
+ [data-st-skeleton="true"]:is(img, video, canvas) {
536
+ background: linear-gradient(
537
+ 90deg,
538
+ var(--st-skeleton-base) 25%,
539
+ var(--st-skeleton-shine) 50%,
540
+ var(--st-skeleton-base) 75%
541
+ );
542
+ background-size: 200% 100%;
543
+ animation: st-shimmer var(--st-skeleton-duration, 1.5s) infinite linear;
544
+ /* Push loaded media content outside element bounds */
545
+ object-fit: none;
546
+ object-position: 9999px 9999px;
547
+ /* Override any existing border-radius */
548
+ border-radius: var(--st-skeleton-radius);
549
+ }
550
+
551
+ /* ── Circle variant — for avatars and round elements ─────────────── */
552
+ /* border-radius: inherit on ::before picks up 50% automatically. */
553
+ /* Nothing extra needed. */
554
+
555
+ /* ── Opt-out — child pops above the overlay via z-index ─────────── */
556
+ [data-st-skeleton="true"] [data-st-skeleton="false"] {
557
+ position: relative;
558
+ z-index: 10000;
559
+ pointer-events: auto;
560
+ cursor: auto;
561
+ }
562
+
563
+ /* ── Reduced motion — static placeholder, no animation ───────────── */
564
+ @media (prefers-reduced-motion: reduce) {
565
+ [data-st-skeleton="true"]::before {
566
+ animation: none;
567
+ background: var(--st-skeleton-base);
568
+ }
569
+ }
570
+ }
571
+ `
572
+
573
+ module.exports = BASE_CSS