ga-toasts 1.0.0 → 2.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,608 @@
1
+ /**
2
+ * GA Toasts 2.x — stylesheet.
3
+ *
4
+ * Self-contained: all design tokens are namespaced (--gat-*) and scoped to the
5
+ * toast elements so they never collide with a host app's own variables.
6
+ * Vertical position + stacking is driven by inline transforms from the engine;
7
+ * this file only styles the cards and provides the transition timing.
8
+ */
9
+
10
+ /* ---------------- Design tokens ---------------- */
11
+ .ga-toast-container {
12
+ --gat-width: 380px;
13
+ --gat-radius: 14px;
14
+ --gat-gap: 14px;
15
+ --gat-edge: 22px;
16
+
17
+ /* Density (padding + type scale) and the optional leading accent bar — all
18
+ driven by the theme() token API; these are the comfortable defaults. */
19
+ --gat-pad-y: 12px;
20
+ --gat-pad-x: 16px;
21
+ --gat-font-size: 0.875rem;
22
+ --gat-accent-edge: 0px;
23
+
24
+ --gat-font: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
25
+ Roboto, "Helvetica Neue", Arial, sans-serif;
26
+
27
+ --gat-surface: rgba(255, 255, 255, 0.82);
28
+ --gat-surface-solid: #ffffff;
29
+ --gat-border: rgba(15, 23, 42, 0.07);
30
+ --gat-ring: rgba(15, 23, 42, 0.05); /* hairline definition ring */
31
+ --gat-highlight: rgba(255, 255, 255, 0.75); /* inset top edge highlight */
32
+ --gat-text: #0f172a;
33
+ --gat-text-soft: #475569;
34
+ --gat-text-muted: #64748b; /* meets WCAG AA on the surface */
35
+ --gat-shadow: 0 1px 2px rgba(15, 23, 42, 0.05),
36
+ 0 8px 20px -12px rgba(15, 23, 42, 0.16);
37
+ --gat-shadow-hover: 0 2px 4px -2px rgba(15, 23, 42, 0.12),
38
+ 0 14px 32px -12px rgba(15, 23, 42, 0.26);
39
+ --gat-chip: rgba(15, 23, 42, 0.05);
40
+
41
+ --gat-success: #16a34a;
42
+ --gat-error: #e11d48;
43
+ --gat-warning: #d97706;
44
+ --gat-info: #2563eb;
45
+ --gat-primary: #6366f1;
46
+ --gat-secondary: #64748b;
47
+ --gat-loading: #2563eb;
48
+
49
+ --gat-ease: cubic-bezier(0.22, 1, 0.36, 1);
50
+ }
51
+
52
+ /* Dark theme — via explicit attribute/class or the OS preference. */
53
+ [data-ga-theme="dark"] .ga-toast-container,
54
+ .ga-theme-dark .ga-toast-container,
55
+ .ga-toast-container.ga-theme-dark {
56
+ --gat-surface: rgba(30, 34, 44, 0.72);
57
+ --gat-surface-solid: #22262f;
58
+ --gat-border: rgba(255, 255, 255, 0.09);
59
+ --gat-ring: rgba(255, 255, 255, 0.06);
60
+ --gat-highlight: rgba(255, 255, 255, 0.07);
61
+ --gat-text: #f8fafc;
62
+ --gat-text-soft: #cbd5e1;
63
+ --gat-text-muted: #9aa7ba;
64
+ --gat-shadow: 0 1px 2px rgba(0, 0, 0, 0.35),
65
+ 0 10px 28px -14px rgba(0, 0, 0, 0.5);
66
+ --gat-shadow-hover: 0 2px 6px -2px rgba(0, 0, 0, 0.5),
67
+ 0 16px 38px -12px rgba(0, 0, 0, 0.64);
68
+ --gat-chip: rgba(255, 255, 255, 0.08);
69
+ }
70
+
71
+ /* Auto (OS) dark mode — applies ONLY when no explicit theme is set on the root,
72
+ so an app that pins data-ga-theme="light" (like the demo) is never overridden. */
73
+ @media (prefers-color-scheme: dark) {
74
+ :root:not([data-ga-theme]):not(.ga-theme-light):not(.ga-theme-dark) .ga-toast-container {
75
+ --gat-surface: rgba(24, 27, 34, 0.82);
76
+ --gat-surface-solid: #191c22;
77
+ --gat-border: rgba(255, 255, 255, 0.09);
78
+ --gat-ring: rgba(255, 255, 255, 0.06);
79
+ --gat-highlight: rgba(255, 255, 255, 0.07);
80
+ --gat-text: #f8fafc;
81
+ --gat-text-soft: #cbd5e1;
82
+ --gat-text-muted: #9aa7ba;
83
+ --gat-shadow: 0 1px 2px rgba(0, 0, 0, 0.35),
84
+ 0 10px 28px -14px rgba(0, 0, 0, 0.5);
85
+ --gat-chip: rgba(255, 255, 255, 0.08);
86
+ }
87
+ }
88
+
89
+ /* ---------------- Modal backdrop (confirm) ---------------- */
90
+ .ga-toast-backdrop {
91
+ position: fixed;
92
+ inset: 0;
93
+ z-index: 2147482990; /* just under the toast containers */
94
+ background: rgba(10, 12, 20, 0.42);
95
+ backdrop-filter: blur(2px);
96
+ -webkit-backdrop-filter: blur(2px);
97
+ opacity: 0;
98
+ transition: opacity 0.22s ease;
99
+ }
100
+ .ga-toast-backdrop-show { opacity: 1; }
101
+ @media (prefers-reduced-transparency: reduce) {
102
+ .ga-toast-backdrop { backdrop-filter: none; -webkit-backdrop-filter: none; }
103
+ }
104
+
105
+ /* ---------------- Container + positioning ---------------- */
106
+ .ga-toast-container {
107
+ position: fixed;
108
+ z-index: 2147483000;
109
+ width: var(--gat-width);
110
+ max-width: calc(100vw - 2 * var(--gat-edge));
111
+ pointer-events: none;
112
+ font-family: var(--gat-font);
113
+ }
114
+
115
+ /* Logical inline positioning so -start/-end mirror automatically under RTL. */
116
+ .ga-toast-container-top-start { inset-block-start: var(--gat-edge); inset-inline-start: var(--gat-edge); }
117
+ .ga-toast-container-top-end { inset-block-start: var(--gat-edge); inset-inline-end: var(--gat-edge); }
118
+ .ga-toast-container-top-center {
119
+ inset-block-start: var(--gat-edge); left: 50%; transform: translateX(-50%);
120
+ }
121
+ .ga-toast-container-bottom-start { inset-block-end: var(--gat-edge); inset-inline-start: var(--gat-edge); }
122
+ .ga-toast-container-bottom-end { inset-block-end: var(--gat-edge); inset-inline-end: var(--gat-edge); }
123
+ .ga-toast-container-bottom-center {
124
+ inset-block-end: var(--gat-edge); left: 50%; transform: translateX(-50%);
125
+ }
126
+ .ga-toast-container-middle-start { top: 50%; inset-inline-start: var(--gat-edge); }
127
+ .ga-toast-container-middle-end { top: 50%; inset-inline-end: var(--gat-edge); }
128
+ .ga-toast-container-middle-center { top: 50%; left: 50%; transform: translateX(-50%); }
129
+
130
+ /* ---------------- Toast card ---------------- */
131
+ .ga-toast {
132
+ position: absolute;
133
+ inset-inline: 0;
134
+ width: 100%;
135
+ box-sizing: border-box;
136
+ display: flex;
137
+ flex-direction: row;
138
+ align-items: flex-start;
139
+ gap: 11px;
140
+ padding: var(--gat-pad-y, 12px) var(--gat-pad-x, 16px);
141
+ color: var(--gat-text);
142
+ background: var(--gat-surface-solid);
143
+ border: none;
144
+ border-radius: var(--gat-radius);
145
+ box-shadow:
146
+ var(--gat-shadow),
147
+ inset 0 1px 0 var(--gat-highlight);
148
+ pointer-events: auto;
149
+ opacity: 0;
150
+ transform: translateY(-140%);
151
+ transition:
152
+ transform 0.5s var(--gat-ease),
153
+ opacity 0.35s ease,
154
+ box-shadow 0.2s ease;
155
+ will-change: transform, opacity;
156
+ overflow: hidden;
157
+ font-size: var(--gat-font-size, 0.875rem);
158
+ line-height: 1.5;
159
+ -webkit-font-smoothing: antialiased;
160
+ text-align: start;
161
+ touch-action: pan-y;
162
+ overscroll-behavior: contain;
163
+ }
164
+
165
+ /* Subtle lift on hover (shadow only — transforms are owned by the engine, so
166
+ changing them here would fight the stack layout). */
167
+ @media (hover: hover) {
168
+ .ga-toast:hover {
169
+ box-shadow:
170
+ var(--gat-shadow-hover),
171
+ inset 0 1px 0 var(--gat-highlight);
172
+ }
173
+ }
174
+
175
+ [data-ga-position^="top"] .ga-toast,
176
+ [data-ga-position^="middle"] .ga-toast { top: 0; transform-origin: top center; }
177
+ [data-ga-position^="bottom"] .ga-toast { bottom: 0; transform-origin: bottom center; }
178
+
179
+ .ga-toast-glass {
180
+ background: var(--gat-surface);
181
+ backdrop-filter: blur(16px) saturate(160%);
182
+ -webkit-backdrop-filter: blur(16px) saturate(160%);
183
+ }
184
+
185
+ /* Outline surface preset — transparent card with a defined hairline edge. */
186
+ .ga-toast-outline {
187
+ background: transparent;
188
+ border: 1px solid var(--gat-border);
189
+ box-shadow: var(--gat-shadow);
190
+ backdrop-filter: none;
191
+ -webkit-backdrop-filter: none;
192
+ }
193
+
194
+ /* Optional leading accent bar (theme token `accentEdge`; 0 = hidden). Painted
195
+ as an inset pseudo so it never shifts layout or gets clipped oddly. */
196
+ .ga-toast::before {
197
+ content: "";
198
+ position: absolute;
199
+ inset-block: 0;
200
+ inset-inline-start: 0;
201
+ width: var(--gat-accent-edge, 0px);
202
+ background: var(--gat-accent, var(--gat-info));
203
+ pointer-events: none;
204
+ }
205
+
206
+ .ga-toast-hide { pointer-events: none; }
207
+
208
+ /* While a horizontal swipe-to-dismiss is in progress, don't let the drag
209
+ paint a text selection. Text is fully selectable at rest. */
210
+ .ga-toast-swiping,
211
+ .ga-toast-swiping * {
212
+ user-select: none;
213
+ -webkit-user-select: none;
214
+ cursor: grabbing;
215
+ }
216
+
217
+ /* Per-type accent colors. */
218
+ .ga-toast-success { --gat-accent: var(--gat-success); }
219
+ .ga-toast-error { --gat-accent: var(--gat-error); }
220
+ .ga-toast-warning { --gat-accent: var(--gat-warning); }
221
+ .ga-toast-info { --gat-accent: var(--gat-info); }
222
+ .ga-toast-primary { --gat-accent: var(--gat-primary); }
223
+ .ga-toast-secondary { --gat-accent: var(--gat-secondary); }
224
+ .ga-toast-loading { --gat-accent: var(--gat-loading); }
225
+
226
+ /* ---------------- Content column ---------------- */
227
+ /* The content column sits between the leading icon and the trailing close. */
228
+ .ga-toast-content {
229
+ display: flex;
230
+ flex-direction: column;
231
+ gap: 4px;
232
+ flex: 1;
233
+ min-width: 0;
234
+ }
235
+
236
+ .ga-toast-header {
237
+ display: flex;
238
+ align-items: center;
239
+ gap: 8px;
240
+ min-width: 0;
241
+ }
242
+
243
+ /* Small inline colored glyph — no chunky chip (Sonner/Linear style). Leads the
244
+ row and aligns with the first line of text, so title-less toasts read well. */
245
+ .ga-toast-icon {
246
+ flex-shrink: 0;
247
+ display: inline-flex;
248
+ align-items: center;
249
+ justify-content: center;
250
+ margin-top: 1px;
251
+ color: var(--gat-accent, var(--gat-info));
252
+ }
253
+ .ga-toast-icon svg { width: 1.25rem; height: 1.25rem; display: block; overflow: visible; }
254
+
255
+ .ga-toast-spinner { animation: ga-toast-spin 0.8s linear infinite; }
256
+
257
+ /* ---- Circular countdown ring (theme progress: 'ring') ---- */
258
+ .ga-toast-icon-ring { position: relative; width: 2rem; height: 2rem; margin-top: 0; }
259
+ .ga-toast-icon-ring > svg:not(.ga-toast-ring) { width: 1.1rem; height: 1.1rem; }
260
+ .ga-toast-ring {
261
+ position: absolute;
262
+ inset: 0;
263
+ width: 100% !important;
264
+ height: 100% !important;
265
+ transform: rotate(-90deg); /* start the sweep at 12 o'clock */
266
+ overflow: visible;
267
+ }
268
+ .ga-toast-ring-track { fill: none; stroke: var(--gat-chip); stroke-width: 2.4; }
269
+ .ga-toast-ring-bar {
270
+ fill: none;
271
+ stroke: var(--gat-accent, var(--gat-info));
272
+ stroke-width: 2.4;
273
+ stroke-linecap: round;
274
+ /* stroke-dasharray / -dashoffset are driven inline by the engine's timer. */
275
+ }
276
+
277
+ /* Icons draw themselves in on mount (skipped under reduced motion). */
278
+ @media (prefers-reduced-motion: no-preference) {
279
+ .ga-toast-icon .ga-ic-ring {
280
+ stroke-dasharray: 64;
281
+ stroke-dashoffset: 64;
282
+ animation: ga-toast-ic-draw 0.5s var(--gat-ease) 0.02s forwards;
283
+ }
284
+ .ga-toast-icon .ga-ic-draw {
285
+ stroke-dasharray: 28;
286
+ stroke-dashoffset: 28;
287
+ animation: ga-toast-ic-draw 0.4s var(--gat-ease) 0.16s forwards;
288
+ }
289
+ }
290
+ @keyframes ga-toast-ic-draw {
291
+ to { stroke-dashoffset: 0; }
292
+ }
293
+
294
+ .ga-toast-avatar {
295
+ flex-shrink: 0;
296
+ width: 34px;
297
+ height: 34px;
298
+ border-radius: 50%;
299
+ object-fit: cover;
300
+ border: 1px solid var(--gat-border);
301
+ }
302
+
303
+ .ga-toast-unread-dot {
304
+ flex-shrink: 0;
305
+ width: 8px;
306
+ height: 8px;
307
+ border-radius: 50%;
308
+ background: var(--gat-accent, var(--gat-info));
309
+ }
310
+
311
+ .ga-toast-title {
312
+ font-weight: 600;
313
+ font-size: 0.9rem;
314
+ color: var(--gat-text);
315
+ flex: 1;
316
+ min-width: 0;
317
+ letter-spacing: -0.011em;
318
+ }
319
+ .ga-toast-title-truncate {
320
+ white-space: nowrap;
321
+ overflow: hidden;
322
+ text-overflow: ellipsis;
323
+ }
324
+
325
+ .ga-toast-status {
326
+ flex-shrink: 0;
327
+ padding: 2px 9px;
328
+ border-radius: 999px;
329
+ font-size: 0.6875rem;
330
+ font-weight: 600;
331
+ text-transform: uppercase;
332
+ letter-spacing: 0.04em;
333
+ /* Darkened accent ink on a stronger tint so the small label clears AA. */
334
+ color: var(--gat-accent, var(--gat-info));
335
+ color: color-mix(in srgb, var(--gat-accent, var(--gat-info)) 58%, var(--gat-text));
336
+ background: var(--gat-chip); /* fallback if color-mix is unsupported */
337
+ background: color-mix(in srgb, var(--gat-accent, var(--gat-info)) 18%, transparent);
338
+ }
339
+
340
+ /* ---------------- Close button ---------------- */
341
+ .ga-toast-close {
342
+ flex-shrink: 0;
343
+ align-self: flex-start;
344
+ margin-top: -1px;
345
+ margin-inline-end: -4px; /* pull toward the corner without losing hit area */
346
+ display: inline-flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ width: 26px;
350
+ height: 26px;
351
+ padding: 0;
352
+ border: none;
353
+ border-radius: 7px;
354
+ color: var(--gat-text-muted);
355
+ background: transparent;
356
+ cursor: pointer;
357
+ opacity: 0.9; /* stays >=3:1 at rest; full on hover/focus */
358
+ transition: background 0.15s ease, color 0.15s ease, opacity 0.15s ease;
359
+ }
360
+ .ga-toast:hover .ga-toast-close,
361
+ .ga-toast:focus-within .ga-toast-close { opacity: 1; }
362
+ .ga-toast-close svg { width: 15px; height: 15px; display: block; }
363
+ .ga-toast-close:hover {
364
+ background: var(--gat-chip);
365
+ color: var(--gat-text);
366
+ }
367
+ /* currentColor is guaranteed to contrast with its own surface (white on filled,
368
+ muted on default, etc.), so the focus ring is never accent-on-accent. */
369
+ .ga-toast-close:focus-visible {
370
+ outline: 2px solid currentColor;
371
+ outline-offset: 2px;
372
+ }
373
+
374
+ /* ---------------- Body ---------------- */
375
+ .ga-toast-body { min-width: 0; }
376
+ .ga-toast-meta {
377
+ font-size: 0.75rem;
378
+ color: var(--gat-text-muted);
379
+ margin-bottom: 2px;
380
+ }
381
+ .ga-toast-message {
382
+ color: var(--gat-text-soft);
383
+ font-size: 0.85rem;
384
+ word-break: break-word;
385
+ /* Clamp very long content so a toast can't fill the viewport. */
386
+ max-height: 40vh;
387
+ overflow-y: auto;
388
+ overscroll-behavior: contain;
389
+ }
390
+ .ga-toast-message a {
391
+ color: var(--gat-accent, var(--gat-info));
392
+ font-weight: 500;
393
+ text-decoration: underline; /* affordance beyond color (WCAG 1.4.1) */
394
+ }
395
+
396
+ /* When there's no header, let the message carry the toast on its own — but not
397
+ on filled toasts, whose message must stay light (specificity guard). */
398
+ .ga-toast:not([class*="-filled"]) .ga-toast-content > .ga-toast-body:only-child .ga-toast-message {
399
+ color: var(--gat-text);
400
+ }
401
+
402
+ /* ---------------- Actions ---------------- */
403
+ .ga-toast-actions {
404
+ display: flex;
405
+ gap: 8px;
406
+ margin-top: 2px;
407
+ }
408
+ .ga-toast-btn {
409
+ flex: 1;
410
+ padding: 8px 14px;
411
+ font: inherit;
412
+ font-size: 0.8125rem;
413
+ font-weight: 600;
414
+ border-radius: 9px;
415
+ border: 1px solid var(--gat-border);
416
+ background: var(--gat-chip);
417
+ color: var(--gat-text);
418
+ cursor: pointer;
419
+ transition: filter 0.15s ease, background 0.15s ease;
420
+ }
421
+ .ga-toast-btn:hover { filter: brightness(0.97); }
422
+ .ga-toast-btn:focus-visible {
423
+ outline: 2px solid currentColor;
424
+ outline-offset: 2px;
425
+ }
426
+ .ga-toast-btn-primary {
427
+ background: var(--gat-accent, var(--gat-primary));
428
+ border-color: transparent;
429
+ color: #fff;
430
+ }
431
+ .ga-toast-btn-primary:hover { filter: brightness(1.06); }
432
+
433
+ /* ---------------- Progress bar ---------------- */
434
+ .ga-toast-progress {
435
+ position: absolute;
436
+ left: 0;
437
+ bottom: 0;
438
+ height: 2px;
439
+ width: 100%;
440
+ transform: scaleX(1);
441
+ transform-origin: left center;
442
+ background: var(--gat-accent, var(--gat-info));
443
+ opacity: 0.4; /* a quiet hint, not a slab */
444
+ }
445
+ .ga-toast-progress-top { top: 0; bottom: auto; }
446
+
447
+ /* ---------------- Segmented steps ---------------- */
448
+ .ga-toast-steps { display: flex; gap: 4px; margin-top: 4px; }
449
+ .ga-toast-step {
450
+ flex: 1;
451
+ height: 3px;
452
+ border-radius: 999px;
453
+ background: var(--gat-chip);
454
+ }
455
+ .ga-toast-step-active { background: var(--gat-accent, var(--gat-info)); }
456
+
457
+ /* ---------------- Variants ---------------- */
458
+ /* Filled: solid accent, white ink, white stroke glyph directly on the fill.
459
+ Per-type fills are darkened where needed so white body text clears WCAG AA
460
+ (4.5:1); no top sheen (it dragged the top region under AA). */
461
+ .ga-toast[class*="-filled"] {
462
+ background: var(--gat-accent, var(--gat-info));
463
+ border-color: transparent;
464
+ color: #fff;
465
+ }
466
+ .ga-toast-success[class*="-filled"] { background: #15803d; } /* white ≈ 5.0:1 */
467
+ .ga-toast-primary[class*="-filled"] { background: #4f46e5; } /* white ≈ 6.3:1 */
468
+ .ga-toast[class*="-filled"] .ga-toast-title,
469
+ .ga-toast[class*="-filled"] .ga-toast-message,
470
+ .ga-toast[class*="-filled"] .ga-toast-meta,
471
+ .ga-toast[class*="-filled"] .ga-toast-icon,
472
+ .ga-toast[class*="-filled"] .ga-toast-close { color: #fff; }
473
+ .ga-toast[class*="-filled"] .ga-toast-close:hover { background: rgba(255, 255, 255, 0.2); }
474
+ /* Accent-colored bits would vanish on the accent fill — force them light. */
475
+ .ga-toast[class*="-filled"] .ga-toast-progress { background: #fff; opacity: 0.55; }
476
+ .ga-toast[class*="-filled"] .ga-toast-step { background: rgba(255, 255, 255, 0.28); }
477
+ .ga-toast[class*="-filled"] .ga-toast-step-active { background: #fff; }
478
+ .ga-toast[class*="-filled"] .ga-toast-status {
479
+ color: #fff;
480
+ background: rgba(255, 255, 255, 0.22);
481
+ }
482
+ .ga-toast[class*="-filled"] .ga-toast-message a { color: #fff; text-decoration: underline; }
483
+
484
+ /* Amber is too light for white text — filled warnings use dark ink throughout. */
485
+ .ga-toast-warning[class*="-filled"],
486
+ .ga-toast-warning[class*="-filled"] .ga-toast-title,
487
+ .ga-toast-warning[class*="-filled"] .ga-toast-message,
488
+ .ga-toast-warning[class*="-filled"] .ga-toast-meta,
489
+ .ga-toast-warning[class*="-filled"] .ga-toast-icon,
490
+ .ga-toast-warning[class*="-filled"] .ga-toast-close,
491
+ .ga-toast-warning[class*="-filled"] .ga-toast-message a {
492
+ color: #2e2100; /* ≈ 5.2:1 on the amber fill */
493
+ }
494
+ .ga-toast-warning[class*="-filled"] .ga-toast-progress { background: #2e2100; opacity: 0.6; }
495
+ .ga-toast-warning[class*="-filled"] .ga-toast-step { background: rgba(0, 0, 0, 0.18); }
496
+ .ga-toast-warning[class*="-filled"] .ga-toast-step-active { background: #2e2100; }
497
+ .ga-toast-warning[class*="-filled"] .ga-toast-status {
498
+ color: #2e2100;
499
+ background: rgba(0, 0, 0, 0.16);
500
+ }
501
+ .ga-toast-warning[class*="-filled"] .ga-toast-close:hover { background: rgba(0, 0, 0, 0.14); }
502
+
503
+ /* Light: a translucent accent wash over the (translucent) surface so glass still
504
+ shows through, plus an accent-tinted border. Secondary text is darkened to
505
+ keep AA over the tint. */
506
+ .ga-toast[class*="-light"] {
507
+ background: var(--gat-surface); /* stays translucent → glass blur still works */
508
+ background-image: linear-gradient(
509
+ 0deg,
510
+ color-mix(in srgb, var(--gat-accent, var(--gat-info)) 14%, transparent),
511
+ color-mix(in srgb, var(--gat-accent, var(--gat-info)) 14%, transparent)
512
+ );
513
+ border-color: color-mix(in srgb, var(--gat-accent, var(--gat-info)) 38%, var(--gat-border));
514
+ }
515
+ .ga-toast[class*="-light"] .ga-toast-meta { color: var(--gat-text-soft); }
516
+ .ga-toast[class*="-light"] .ga-toast-message a {
517
+ color: color-mix(in srgb, var(--gat-accent, var(--gat-info)) 55%, var(--gat-text));
518
+ text-decoration: underline;
519
+ }
520
+
521
+ /* ---------------- Sizes (density) ---------------- */
522
+ .ga-toast-xs { padding: 10px 13px; font-size: 0.78rem; }
523
+ .ga-toast-xs .ga-toast-icon svg { width: 1.05rem; height: 1.05rem; }
524
+ .ga-toast-xs .ga-toast-message { font-size: 0.78rem; }
525
+ .ga-toast-sm { padding: 11px 14px; gap: 9px; }
526
+ .ga-toast-lg { padding: 18px 20px; }
527
+ .ga-toast-lg .ga-toast-title { font-size: 1rem; }
528
+ .ga-toast-lg .ga-toast-message { font-size: 0.95rem; }
529
+ .ga-toast-lg .ga-toast-icon svg { width: 1.4rem; height: 1.4rem; }
530
+ .ga-toast-xl { padding: 22px 24px; }
531
+ .ga-toast-xl .ga-toast-title { font-size: 1.125rem; }
532
+ .ga-toast-xl .ga-toast-message { font-size: 1.05rem; }
533
+ .ga-toast-xl .ga-toast-icon svg { width: 1.6rem; height: 1.6rem; }
534
+
535
+ /* ---------------- Compact ---------------- */
536
+ /* The card is already an icon · content · close row, so compact just tightens
537
+ the padding and vertically centres the single line. */
538
+ .ga-toast-compact {
539
+ align-items: center;
540
+ gap: 10px;
541
+ padding: 10px 14px;
542
+ }
543
+ .ga-toast-compact .ga-toast-content { gap: 2px; }
544
+ .ga-toast-compact .ga-toast-icon { margin-top: 0; }
545
+ .ga-toast-compact .ga-toast-close { align-self: center; margin-top: 0; }
546
+ .ga-toast-compact .ga-toast-steps { display: none; }
547
+ .ga-toast-compact .ga-toast-icon svg { width: 1.05rem; height: 1.05rem; }
548
+
549
+ /* ---------------- Live region (screen-reader only) ---------------- */
550
+ .ga-toast-live-region {
551
+ position: fixed;
552
+ width: 1px;
553
+ height: 1px;
554
+ padding: 0;
555
+ margin: -1px;
556
+ overflow: hidden;
557
+ clip: rect(0 0 0 0);
558
+ clip-path: inset(50%);
559
+ white-space: nowrap;
560
+ border: 0;
561
+ }
562
+
563
+ /* ---------------- Responsive ---------------- */
564
+ @media (max-width: 520px) {
565
+ .ga-toast-container {
566
+ --gat-width: auto;
567
+ left: var(--gat-edge);
568
+ right: var(--gat-edge);
569
+ transform: none;
570
+ }
571
+ .ga-toast-container-top-center,
572
+ .ga-toast-container-bottom-center,
573
+ .ga-toast-container-middle-center { transform: none; }
574
+ }
575
+
576
+ /* ---------------- RTL ---------------- */
577
+ [dir="rtl"] .ga-toast-progress { transform-origin: right center; }
578
+
579
+ /* ---------------- Reduced motion ---------------- */
580
+ @media (prefers-reduced-motion: reduce) {
581
+ .ga-toast {
582
+ transition: opacity 0.15s ease;
583
+ }
584
+ .ga-toast-spinner { animation-duration: 1.4s; }
585
+ }
586
+
587
+ /* ---------------- Reduced transparency ---------------- */
588
+ @media (prefers-reduced-transparency: reduce) {
589
+ .ga-toast-glass {
590
+ background: var(--gat-surface-solid);
591
+ backdrop-filter: none;
592
+ -webkit-backdrop-filter: none;
593
+ }
594
+ }
595
+
596
+ /* ---------------- Custom content (toast.custom) ---------------- */
597
+ .ga-toast-custom { min-width: 0; }
598
+ .ga-toast-has-custom { padding: 0; }
599
+ .ga-toast-has-custom .ga-toast-close {
600
+ position: absolute;
601
+ inset-block-start: 8px;
602
+ inset-inline-end: 8px;
603
+ z-index: 1;
604
+ }
605
+
606
+ @keyframes ga-toast-spin {
607
+ to { transform: rotate(360deg); }
608
+ }