sileo-sveltekit 0.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.
@@ -0,0 +1,719 @@
1
+ <script lang="ts" module>
2
+ export type SileoPosition =
3
+ | 'top-left'
4
+ | 'top-center'
5
+ | 'top-right'
6
+ | 'bottom-left'
7
+ | 'bottom-center'
8
+ | 'bottom-right';
9
+
10
+ export interface SileoOptions {
11
+ title?: string;
12
+ description?: string;
13
+ position?: SileoPosition;
14
+ duration?: number | null;
15
+ fill?: string;
16
+ roundness?: number;
17
+ autopilot?: boolean | { expand?: number; collapse?: number };
18
+ }
19
+
20
+ export interface SileoToastAPI {
21
+ show: (opts: SileoOptions) => string;
22
+ success: (title: string, desc?: string, dur?: number) => string;
23
+ error: (title: string, desc?: string, dur?: number) => string;
24
+ warning: (title: string, desc?: string, dur?: number) => string;
25
+ info: (title: string, desc?: string, dur?: number) => string;
26
+ action: (title: string, desc?: string, dur?: number) => string;
27
+ dismiss: (id: string) => void;
28
+ clear: (position?: SileoPosition) => void;
29
+ }
30
+ </script>
31
+
32
+ <script lang="ts">
33
+ import { onMount } from 'svelte';
34
+ import SileoToast, { type SileoState } from './SileoToast.svelte';
35
+
36
+ /* --------------------------------- Types --------------------------------- */
37
+
38
+ interface SileoItem extends SileoOptions {
39
+ id: string;
40
+ instanceId: string;
41
+ state: SileoState;
42
+ exiting?: boolean;
43
+ autoExpandDelayMs?: number;
44
+ autoCollapseDelayMs?: number;
45
+ }
46
+
47
+ interface Props {
48
+ position?: SileoPosition;
49
+ }
50
+
51
+ /* -------------------------------- Constants ------------------------------- */
52
+
53
+ const DEFAULT_TOAST_DURATION = 6000;
54
+ const EXIT_DURATION = DEFAULT_TOAST_DURATION * 0.1; // 600ms
55
+ const AUTO_EXPAND_DELAY = DEFAULT_TOAST_DURATION * 0.025; // 150ms
56
+ const AUTO_COLLAPSE_DELAY = DEFAULT_TOAST_DURATION - 2000; // 4000ms
57
+
58
+ /* --------------------------------- Props --------------------------------- */
59
+
60
+ let { position: defaultPosition = 'bottom-right' }: Props = $props();
61
+
62
+ /* --------------------------------- State --------------------------------- */
63
+
64
+ let toasts = $state<SileoItem[]>([]);
65
+ let activeId = $state<string | undefined>(undefined);
66
+ let isHovering = false;
67
+ let timers = new Map<string, number>();
68
+ let idCounter = 0;
69
+
70
+ /* -------------------------------- Helpers --------------------------------- */
71
+
72
+ function generateId() {
73
+ return `${++idCounter}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
74
+ }
75
+
76
+ function timeoutKey(t: SileoItem) {
77
+ return `${t.id}:${t.instanceId}`;
78
+ }
79
+
80
+ function pillAlign(pos: SileoPosition): 'left' | 'center' | 'right' {
81
+ if (pos.includes('right')) return 'right';
82
+ if (pos.includes('center')) return 'center';
83
+ return 'left';
84
+ }
85
+
86
+ function expandDir(pos: SileoPosition): 'top' | 'bottom' {
87
+ return pos.startsWith('top') ? 'bottom' : 'top';
88
+ }
89
+
90
+ /* ------------------------------ Dismiss ---------------------------------- */
91
+
92
+ function dismissToast(id: string) {
93
+ const item = toasts.find((t) => t.id === id);
94
+ if (!item || item.exiting) return;
95
+
96
+ toasts = toasts.map((t) => (t.id === id ? { ...t, exiting: true } : t));
97
+
98
+ setTimeout(() => {
99
+ toasts = toasts.filter((t) => t.id !== id);
100
+ }, EXIT_DURATION);
101
+ }
102
+
103
+ /* ------------------------------ Scheduling ------------------------------- */
104
+
105
+ function clearAllTimers() {
106
+ for (const t of timers.values()) clearTimeout(t);
107
+ timers.clear();
108
+ }
109
+
110
+ function schedule(items: SileoItem[]) {
111
+ if (isHovering) return;
112
+
113
+ for (const item of items) {
114
+ if (item.exiting) continue;
115
+ const key = timeoutKey(item);
116
+ if (timers.has(key)) continue;
117
+
118
+ const dur = item.duration ?? DEFAULT_TOAST_DURATION;
119
+ if (dur === null || dur <= 0) continue;
120
+
121
+ timers.set(key, window.setTimeout(() => dismissToast(item.id), dur));
122
+ }
123
+ }
124
+
125
+ /* ------------------------------ Autopilot -------------------------------- */
126
+
127
+ function resolveAutopilot(
128
+ opts: SileoOptions & { state?: SileoState },
129
+ duration: number | null
130
+ ): { expandDelayMs?: number; collapseDelayMs?: number } {
131
+ if (opts.autopilot === false || !duration || duration <= 0) return {};
132
+ const cfg = typeof opts.autopilot === 'object' ? opts.autopilot : undefined;
133
+ const clamp = (v: number) => Math.min(duration, Math.max(0, v));
134
+ return {
135
+ expandDelayMs: clamp(cfg?.expand ?? AUTO_EXPAND_DELAY),
136
+ collapseDelayMs: clamp(cfg?.collapse ?? AUTO_COLLAPSE_DELAY)
137
+ };
138
+ }
139
+
140
+ /* ------------------------------ Toast API -------------------------------- */
141
+
142
+ function createToast(options: SileoOptions & { state?: SileoState }): string {
143
+ const live = toasts.filter((t) => !t.exiting);
144
+ const id = 'sileo-' + generateId();
145
+ const duration = options.duration ?? DEFAULT_TOAST_DURATION;
146
+ const auto = resolveAutopilot(options, duration);
147
+
148
+ const resolvedState = options.state ?? 'success';
149
+ const item: SileoItem = {
150
+ ...options,
151
+ id,
152
+ instanceId: generateId(),
153
+ state: resolvedState,
154
+ position: options.position ?? defaultPosition,
155
+ autoExpandDelayMs: auto.expandDelayMs,
156
+ autoCollapseDelayMs: auto.collapseDelayMs
157
+ };
158
+
159
+ // Check for duplicate ID
160
+ const prev = live.find((t) => t.id === id);
161
+ if (prev) {
162
+ toasts = toasts.map((t) => (t.id === id ? item : t));
163
+ } else {
164
+ toasts = [...toasts.filter((t) => t.id !== id), item];
165
+ }
166
+
167
+ // Schedule auto-dismiss
168
+ const key = timeoutKey(item);
169
+ if (!isHovering && duration && duration > 0 && !timers.has(key)) {
170
+ timers.set(key, window.setTimeout(() => dismissToast(id), duration));
171
+ }
172
+
173
+ return id;
174
+ }
175
+
176
+ /* ----------------------------- Active tracking --------------------------- */
177
+
178
+ const latest = $derived.by(() => {
179
+ for (let i = toasts.length - 1; i >= 0; i--) {
180
+ if (!toasts[i].exiting) return toasts[i].id;
181
+ }
182
+ return undefined;
183
+ });
184
+
185
+ $effect(() => {
186
+ activeId = latest;
187
+ });
188
+
189
+ // Clean up stale timers and schedule new ones
190
+ $effect(() => {
191
+ const toastKeys = new Set(toasts.map(timeoutKey));
192
+ for (const [key, timer] of timers) {
193
+ if (!toastKeys.has(key)) {
194
+ clearTimeout(timer);
195
+ timers.delete(key);
196
+ }
197
+ }
198
+ schedule(toasts);
199
+ });
200
+
201
+ /* ----------------------------- Group by position ------------------------- */
202
+
203
+ const activePositions = $derived.by(() => {
204
+ const map = new Map<SileoPosition, SileoItem[]>();
205
+ for (const t of toasts) {
206
+ const pos = t.position ?? defaultPosition;
207
+ const arr = map.get(pos);
208
+ if (arr) {
209
+ arr.push(t);
210
+ } else {
211
+ map.set(pos, [t]);
212
+ }
213
+ }
214
+ return map;
215
+ });
216
+
217
+ /* -------------------------------- Handlers ------------------------------- */
218
+
219
+ function handleToastEnter(toastId: string, e: MouseEvent) {
220
+ activeId = toastId;
221
+ if (!isHovering) {
222
+ isHovering = true;
223
+ clearAllTimers();
224
+ }
225
+ }
226
+
227
+ function handleToastLeave(toastId: string, e: MouseEvent) {
228
+ activeId = latest;
229
+ if (isHovering) {
230
+ isHovering = false;
231
+ schedule(toasts);
232
+ }
233
+ }
234
+
235
+ /* -------------------------------- Mount ---------------------------------- */
236
+
237
+ onMount(() => {
238
+ const api: SileoToastAPI = {
239
+ show: (opts: SileoOptions) => createToast(opts),
240
+ success: (title: string, desc?: string, dur?: number) =>
241
+ createToast({ title, description: desc, duration: dur, state: 'success' }),
242
+ error: (title: string, desc?: string, dur?: number) =>
243
+ createToast({ title, description: desc, duration: dur, state: 'error' }),
244
+ warning: (title: string, desc?: string, dur?: number) =>
245
+ createToast({ title, description: desc, duration: dur, state: 'warning' }),
246
+ info: (title: string, desc?: string, dur?: number) =>
247
+ createToast({ title, description: desc, duration: dur, state: 'info' }),
248
+ action: (title: string, desc?: string, dur?: number) =>
249
+ createToast({ title, description: desc, duration: dur, state: 'action' }),
250
+ dismiss: dismissToast,
251
+ clear: (pos?: SileoPosition) => {
252
+ toasts = pos ? toasts.filter((t) => t.position !== pos) : [];
253
+ }
254
+ };
255
+
256
+ (window as any).toast = api;
257
+
258
+ return () => {
259
+ clearAllTimers();
260
+ };
261
+ });
262
+ </script>
263
+
264
+ <!-- Viewports -->
265
+ {#each [...activePositions] as [pos, items] (pos)}
266
+ {@const pill = pillAlign(pos)}
267
+ {@const exp = expandDir(pos)}
268
+
269
+ <section data-sileo-viewport data-position={pos} aria-live="polite">
270
+ {#each items as item (item.id)}
271
+ <SileoToast
272
+ id={item.id}
273
+ toastState={item.state}
274
+ title={item.title}
275
+ description={item.description}
276
+ position={pill}
277
+ expand={exp}
278
+ fill={item.fill}
279
+ roundness={item.roundness}
280
+ exiting={item.exiting}
281
+ autoExpandDelayMs={item.autoExpandDelayMs}
282
+ autoCollapseDelayMs={item.autoCollapseDelayMs}
283
+ refreshKey={item.instanceId}
284
+ canExpand={activeId === undefined || activeId === item.id}
285
+ onmouseenter={(e) => handleToastEnter(item.id, e)}
286
+ onmouseleave={(e) => handleToastLeave(item.id, e)}
287
+ ondismiss={() => dismissToast(item.id)}
288
+ />
289
+ {/each}
290
+ </section>
291
+ {/each}
292
+
293
+ <style>
294
+ /* -------------------------------- Variables ------------------------------- */
295
+
296
+ :root {
297
+ --sileo-spring-easing: linear(
298
+ 0,
299
+ 0.002 0.6%,
300
+ 0.007 1.2%,
301
+ 0.015 1.8%,
302
+ 0.026 2.4%,
303
+ 0.041 3.1%,
304
+ 0.06 3.8%,
305
+ 0.108 5.3%,
306
+ 0.157 6.6%,
307
+ 0.214 8%,
308
+ 0.467 13.7%,
309
+ 0.577 16.3%,
310
+ 0.631 17.7%,
311
+ 0.682 19.1%,
312
+ 0.73 20.5%,
313
+ 0.771 21.8%,
314
+ 0.808 23.1%,
315
+ 0.844 24.5%,
316
+ 0.874 25.8%,
317
+ 0.903 27.2%,
318
+ 0.928 28.6%,
319
+ 0.952 30.1%,
320
+ 0.972 31.6%,
321
+ 0.988 33.1%,
322
+ 1.01 35.7%,
323
+ 1.025 38.5%,
324
+ 1.034 41.6%,
325
+ 1.038 45%,
326
+ 1.035 50.1%,
327
+ 1.012 64.2%,
328
+ 1.003 73%,
329
+ 0.999 83.7%,
330
+ 1
331
+ );
332
+
333
+ --sileo-duration: 600ms;
334
+ --sileo-height: 40px;
335
+ --sileo-width: 350px;
336
+ --sileo-fill: #fff;
337
+
338
+ --sileo-state-success: oklch(0.723 0.219 142.136);
339
+ --sileo-state-loading: oklch(0.556 0 0);
340
+ --sileo-state-error: oklch(0.637 0.237 25.331);
341
+ --sileo-state-warning: oklch(0.795 0.184 86.047);
342
+ --sileo-state-info: oklch(0.685 0.169 237.323);
343
+ --sileo-state-action: oklch(0.623 0.214 259.815);
344
+ }
345
+
346
+ /* -------------------------------- Viewports ------------------------------- */
347
+
348
+ :global([data-sileo-viewport]) {
349
+ position: fixed;
350
+ z-index: 99999;
351
+ display: flex;
352
+ gap: 0.75rem;
353
+ padding: 0.75rem;
354
+ pointer-events: none;
355
+ max-width: calc(100vw - 1.5rem);
356
+ contain: layout style;
357
+ }
358
+
359
+ /* Vertical edge */
360
+ :global([data-sileo-viewport][data-position^='top']) {
361
+ top: 0;
362
+ flex-direction: column-reverse;
363
+ }
364
+
365
+ :global([data-sileo-viewport][data-position^='bottom']) {
366
+ bottom: 0;
367
+ flex-direction: column;
368
+ }
369
+
370
+ /* Horizontal alignment */
371
+ :global([data-sileo-viewport][data-position$='left']) {
372
+ left: 0;
373
+ align-items: flex-start;
374
+ }
375
+
376
+ :global([data-sileo-viewport][data-position$='right']) {
377
+ right: 0;
378
+ align-items: flex-end;
379
+ }
380
+
381
+ :global([data-sileo-viewport][data-position$='center']) {
382
+ left: 50%;
383
+ transform: translateX(-50%);
384
+ align-items: center;
385
+ }
386
+
387
+ /* Negative margin entry for stacking */
388
+ :global(
389
+ [data-sileo-viewport][data-position^='top'] [data-sileo-toast]:not([data-ready='true'])
390
+ ) {
391
+ margin-bottom: calc(-1 * (var(--sileo-height) + 0.75rem));
392
+ }
393
+
394
+ :global(
395
+ [data-sileo-viewport][data-position^='bottom'] [data-sileo-toast]:not([data-ready='true'])
396
+ ) {
397
+ margin-top: calc(-1 * (var(--sileo-height) + 0.75rem));
398
+ }
399
+
400
+ /* ---------------------------------- Toast --------------------------------- */
401
+
402
+ :global([data-sileo-toast]) {
403
+ position: relative;
404
+ cursor: pointer;
405
+ pointer-events: auto;
406
+ touch-action: none;
407
+ border: 0;
408
+ background: transparent;
409
+ padding: 0;
410
+ width: var(--sileo-width);
411
+ height: var(--_h, var(--sileo-height));
412
+ opacity: 0;
413
+ transform: translateZ(0) scale(0.95);
414
+ transform-origin: center;
415
+ contain: layout style;
416
+ overflow: visible;
417
+ }
418
+
419
+ :global([data-sileo-toast][data-state='loading']) {
420
+ cursor: default;
421
+ }
422
+
423
+ :global([data-sileo-toast][data-ready='true']) {
424
+ opacity: 1;
425
+ transform: translateZ(0) scale(1);
426
+ transition:
427
+ transform calc(var(--sileo-duration) * 0.66) var(--sileo-spring-easing),
428
+ opacity calc(var(--sileo-duration) * 0.66) var(--sileo-spring-easing),
429
+ margin-bottom calc(var(--sileo-duration) * 0.66) var(--sileo-spring-easing),
430
+ margin-top calc(var(--sileo-duration) * 0.66) var(--sileo-spring-easing),
431
+ height var(--sileo-duration) var(--sileo-spring-easing);
432
+ }
433
+
434
+ /* Entry animation direction */
435
+ :global(
436
+ [data-sileo-viewport][data-position^='top'] [data-sileo-toast]:not([data-ready='true'])
437
+ ) {
438
+ transform: translateY(-6px) scale(0.95);
439
+ }
440
+
441
+ :global(
442
+ [data-sileo-viewport][data-position^='bottom'] [data-sileo-toast]:not([data-ready='true'])
443
+ ) {
444
+ transform: translateY(6px) scale(0.95);
445
+ }
446
+
447
+ /* Exit */
448
+ :global([data-sileo-toast][data-ready='true'][data-exiting='true']) {
449
+ opacity: 0;
450
+ pointer-events: none;
451
+ }
452
+
453
+ :global(
454
+ [data-sileo-viewport][data-position^='top']
455
+ [data-sileo-toast][data-ready='true'][data-exiting='true']
456
+ ) {
457
+ transform: translateY(-6px) scale(0.95);
458
+ }
459
+
460
+ :global(
461
+ [data-sileo-viewport][data-position^='bottom']
462
+ [data-sileo-toast][data-ready='true'][data-exiting='true']
463
+ ) {
464
+ transform: translateY(6px) scale(0.95);
465
+ }
466
+
467
+ /* ------------------------------- SVG Canvas ------------------------------- */
468
+
469
+ :global([data-sileo-canvas]) {
470
+ position: absolute;
471
+ left: 0;
472
+ right: 0;
473
+ pointer-events: none;
474
+ transform: translateZ(0);
475
+ contain: layout style;
476
+ overflow: visible;
477
+ }
478
+
479
+ :global([data-sileo-canvas][data-edge='top']) {
480
+ bottom: 0;
481
+ transform: scaleY(-1) translateZ(0);
482
+ }
483
+
484
+ :global([data-sileo-canvas][data-edge='bottom']) {
485
+ top: 0;
486
+ }
487
+
488
+ :global([data-sileo-svg]) {
489
+ overflow: visible;
490
+ }
491
+
492
+ /* --------------------------------- Header --------------------------------- */
493
+
494
+ :global([data-sileo-header]) {
495
+ position: absolute;
496
+ z-index: 20;
497
+ display: flex;
498
+ align-items: center;
499
+ padding: 0.5rem;
500
+ height: var(--sileo-height);
501
+ overflow: hidden;
502
+ left: var(--_px, 0px);
503
+ transform: var(--_ht);
504
+ max-width: var(--_pw);
505
+ }
506
+
507
+ :global([data-sileo-toast][data-ready='true'] [data-sileo-header]) {
508
+ transition:
509
+ transform var(--sileo-duration) var(--sileo-spring-easing),
510
+ left var(--sileo-duration) var(--sileo-spring-easing),
511
+ max-width var(--sileo-duration) var(--sileo-spring-easing);
512
+ }
513
+
514
+ :global([data-sileo-header][data-edge='top']) {
515
+ bottom: 0;
516
+ }
517
+
518
+ :global([data-sileo-header][data-edge='bottom']) {
519
+ top: 0;
520
+ }
521
+
522
+ /* Header inner morphing */
523
+ :global([data-sileo-header-stack]) {
524
+ position: relative;
525
+ display: inline-flex;
526
+ align-items: center;
527
+ height: 100%;
528
+ }
529
+
530
+ :global([data-sileo-header-inner]) {
531
+ display: flex;
532
+ align-items: center;
533
+ gap: 0.5rem;
534
+ white-space: nowrap;
535
+ opacity: 1;
536
+ filter: blur(0px);
537
+ transform: translateZ(0);
538
+ }
539
+
540
+ :global([data-sileo-header-inner][data-layer='current']) {
541
+ position: relative;
542
+ z-index: 1;
543
+ animation: sileo-header-enter var(--sileo-duration) var(--sileo-spring-easing) both;
544
+ }
545
+
546
+ :global([data-sileo-header-inner][data-layer='current']:not(:only-child)),
547
+ :global([data-sileo-header-inner][data-exiting='true']) {
548
+ will-change: opacity, filter;
549
+ }
550
+
551
+ :global([data-sileo-header-inner][data-layer='prev']) {
552
+ position: absolute;
553
+ left: 0;
554
+ top: 0;
555
+ z-index: 0;
556
+ pointer-events: none;
557
+ }
558
+
559
+ :global([data-sileo-header-inner][data-exiting='true']) {
560
+ animation: sileo-header-exit calc(var(--sileo-duration) * 0.7) ease forwards;
561
+ }
562
+
563
+ /* ---------------------------------- Badge --------------------------------- */
564
+
565
+ :global([data-sileo-badge]) {
566
+ display: flex;
567
+ height: 24px;
568
+ width: 24px;
569
+ flex-shrink: 0;
570
+ align-items: center;
571
+ justify-content: center;
572
+ padding: 2px;
573
+ box-sizing: border-box;
574
+ border-radius: 9999px;
575
+ color: var(--sileo-tone, currentColor);
576
+ background-color: var(--sileo-tone-bg, transparent);
577
+ }
578
+
579
+ /* ---------------------------------- Title --------------------------------- */
580
+
581
+ :global([data-sileo-title]) {
582
+ font-size: 0.825rem;
583
+ line-height: 1rem;
584
+ font-weight: 500;
585
+ text-transform: capitalize;
586
+ color: var(--sileo-tone, currentColor);
587
+ }
588
+
589
+ /* ------------------------------ State Colors ------------------------------ */
590
+
591
+ :global(:is([data-sileo-badge], [data-sileo-title], [data-sileo-button])[data-state]) {
592
+ --_c: var(--sileo-state-success);
593
+ }
594
+
595
+ :global(:is([data-sileo-badge], [data-sileo-title], [data-sileo-button])[data-state='loading']) {
596
+ --_c: var(--sileo-state-loading);
597
+ }
598
+
599
+ :global(:is([data-sileo-badge], [data-sileo-title], [data-sileo-button])[data-state='error']) {
600
+ --_c: var(--sileo-state-error);
601
+ }
602
+
603
+ :global(:is([data-sileo-badge], [data-sileo-title], [data-sileo-button])[data-state='warning']) {
604
+ --_c: var(--sileo-state-warning);
605
+ }
606
+
607
+ :global(:is([data-sileo-badge], [data-sileo-title], [data-sileo-button])[data-state='info']) {
608
+ --_c: var(--sileo-state-info);
609
+ }
610
+
611
+ :global(:is([data-sileo-badge], [data-sileo-title], [data-sileo-button])[data-state='action']) {
612
+ --_c: var(--sileo-state-action);
613
+ }
614
+
615
+ :global(:is([data-sileo-badge], [data-sileo-title])[data-state]) {
616
+ --sileo-tone: var(--_c);
617
+ --sileo-tone-bg: color-mix(in oklch, var(--_c) 20%, transparent);
618
+ }
619
+
620
+ /* --------------------------------- Content -------------------------------- */
621
+
622
+ :global([data-sileo-content]) {
623
+ position: absolute;
624
+ left: 0;
625
+ z-index: 10;
626
+ width: 100%;
627
+ pointer-events: none;
628
+ opacity: var(--_co, 0);
629
+ }
630
+
631
+ :global([data-sileo-content]:not([data-visible='true'])) {
632
+ content-visibility: hidden;
633
+ }
634
+
635
+ :global([data-sileo-toast][data-ready='true'] [data-sileo-content]) {
636
+ transition: opacity calc(var(--sileo-duration) * 0.08) ease calc(var(--sileo-duration) * 0.04);
637
+ }
638
+
639
+ :global([data-sileo-content][data-edge='top']) {
640
+ top: 0;
641
+ }
642
+
643
+ :global([data-sileo-content][data-edge='bottom']) {
644
+ top: var(--sileo-height);
645
+ }
646
+
647
+ :global([data-sileo-content][data-visible='true']) {
648
+ pointer-events: auto;
649
+ }
650
+
651
+ :global([data-sileo-toast][data-ready='true'] [data-sileo-content][data-visible='true']) {
652
+ transition: opacity calc(var(--sileo-duration) * 0.6) ease calc(var(--sileo-duration) * 0.3);
653
+ }
654
+
655
+ :global([data-sileo-description]) {
656
+ width: 100%;
657
+ text-align: left;
658
+ padding: 1rem;
659
+ font-size: 0.875rem;
660
+ line-height: 1.25rem;
661
+ color: var(--sileo-text, currentColor);
662
+ font-family: system-ui, -apple-system, sans-serif;
663
+ contain: layout style paint;
664
+ content-visibility: auto;
665
+ }
666
+
667
+ /* -------------------------------- Animations ------------------------------ */
668
+
669
+ :global([data-sileo-icon='spin']) {
670
+ animation: sileo-spin 1s linear infinite;
671
+ }
672
+
673
+ @keyframes sileo-spin {
674
+ to {
675
+ transform: rotate(360deg);
676
+ }
677
+ }
678
+
679
+ @keyframes sileo-header-enter {
680
+ from {
681
+ opacity: 0;
682
+ filter: blur(6px);
683
+ }
684
+ to {
685
+ opacity: 1;
686
+ filter: blur(0px);
687
+ }
688
+ }
689
+
690
+ @keyframes sileo-header-exit {
691
+ from {
692
+ opacity: 1;
693
+ filter: blur(0px);
694
+ }
695
+ to {
696
+ opacity: 0;
697
+ filter: blur(6px);
698
+ }
699
+ }
700
+
701
+ /* -------------------------------- Reduced motion -------------------------- */
702
+
703
+ @media (prefers-reduced-motion: no-preference) {
704
+ :global([data-sileo-toast][data-ready='true']:hover),
705
+ :global([data-sileo-toast][data-ready='true'][data-exiting='true']) {
706
+ will-change: transform, opacity, height;
707
+ }
708
+ }
709
+
710
+ @media (prefers-reduced-motion: reduce) {
711
+ :global(*),
712
+ :global(*::before),
713
+ :global(*::after) {
714
+ animation-duration: 0.01ms !important;
715
+ animation-iteration-count: 1 !important;
716
+ transition-duration: 0.01ms !important;
717
+ }
718
+ }
719
+ </style>