@24vlh/vds 0.1.0 → 0.1.1

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,421 +1 @@
1
- /************************************************************
2
- * VLAH DESIGN SYSTEM (VDS) - Toast System
3
- *
4
- * Responsibilities:
5
- * - Provide floating and inline toast components with full stacking logic
6
- * - Define neutral + semantic variants (info, success, warning, danger, error)
7
- * - Implement icon slot, title/message layout, close button, and timed bar
8
- * - Support in/out animation states, compact mode, and mobile reflow
9
- * - Ensure accessibility: aria-live roles, focus-visible, reduced-motion
10
- *
11
- * System Notes:
12
- * - Token-driven: spacing, radii, borders, surfaces, shadows, motion, text
13
- * - Pure CSS; JS only toggles lifecycle states (in, out, exiting)
14
- * - Motion system honours prefers-reduced-motion
15
- * - Semantic colours derive from feedback.css and theme tokens
16
- ************************************************************/
17
-
18
- /* ---------------------------------------------------------
19
- 1. TOAST TOKEN DEFINITIONS (component-level)
20
- --------------------------------------------------------- */
21
-
22
- [data-vds-toast],
23
- .vds-toast {
24
- --toast-duration: 5000ms;
25
- --toast-feedback-padding-md: var(--space-4);
26
- }
27
-
28
- /* ---------------------------------------------------------
29
- 2. STACK CONTAINERS (FLOATING)
30
- Usage:
31
- <div class="toast-stack toast-stack--top-right">
32
- <div class="toast toast--success">...</div>
33
- </div>
34
- --------------------------------------------------------- */
35
-
36
- .toast-stack {
37
- position: fixed;
38
- z-index: var(--z-toast);
39
- display: flex;
40
- flex-direction: column;
41
- gap: var(--space-2);
42
- pointer-events: none;
43
- align-items: stretch;
44
- width: 20rem;
45
- max-width: 100%;
46
- }
47
-
48
- .toast-stack .toast {
49
- width: 100%;
50
- }
51
-
52
- .toast-stack--top-right {
53
- top: var(--space-4);
54
- right: var(--space-4);
55
- align-items: flex-end;
56
- }
57
-
58
- .toast-stack--top-left {
59
- top: var(--space-4);
60
- left: var(--space-4);
61
- align-items: flex-start;
62
- }
63
-
64
- .toast-stack--bottom-right {
65
- bottom: var(--space-4);
66
- right: var(--space-4);
67
- align-items: flex-end;
68
- }
69
-
70
- .toast-stack--bottom-left {
71
- bottom: var(--space-4);
72
- left: var(--space-4);
73
- align-items: flex-start;
74
- }
75
-
76
- .toast-stack--top-center {
77
- top: var(--space-4);
78
- left: 50%;
79
- transform: translateX(-50%);
80
- align-items: center;
81
- }
82
-
83
- .toast-stack--bottom-center {
84
- bottom: var(--space-4);
85
- left: 50%;
86
- transform: translateX(-50%);
87
- align-items: center;
88
- }
89
-
90
- @media (max-width: 640px) {
91
- .toast-stack {
92
- left: var(--space-2);
93
- right: var(--space-2);
94
- max-width: none;
95
- }
96
-
97
- .toast-stack--top-right,
98
- .toast-stack--top-left,
99
- .toast-stack--top-center {
100
- top: auto;
101
- bottom: var(--space-2);
102
- transform: none;
103
- align-items: stretch;
104
- }
105
-
106
- .toast-stack--bottom-right,
107
- .toast-stack--bottom-left,
108
- .toast-stack--bottom-center {
109
- bottom: var(--space-2);
110
- transform: none;
111
- align-items: stretch;
112
- }
113
- }
114
-
115
- /* ---------------------------------------------------------
116
- 3. BASE TOAST
117
- Pattern:
118
- <div class="toast toast--info" role="status" aria-live="polite">
119
- <div class="toast__icon">…</div>
120
- <div class="toast__body">
121
- <div class="toast__title">Saved</div>
122
- <div class="toast__message">Your changes were stored.</div>
123
- </div>
124
- <button class="toast__close" type="button" aria-label="Dismiss">×</button>
125
- <div class="toast__progress"></div>
126
- </div>
127
- --------------------------------------------------------- */
128
-
129
- .toast {
130
- pointer-events: auto;
131
- display: grid;
132
- grid-template-columns: auto 1fr auto;
133
- grid-template-rows: auto auto;
134
- grid-template-areas:
135
- "icon body close"
136
- "progress progress progress";
137
- grid-column-gap: var(--space-3);
138
- -moz-column-gap: var(--space-3);
139
- column-gap: var(--space-3);
140
- grid-row-gap: var(--space-1);
141
- row-gap: var(--space-1);
142
-
143
- padding: var(--toast-feedback-padding-md);
144
- border-radius: var(--radius-md);
145
- border: var(--border-width) solid var(--color-border-subtle);
146
- background-color: var(--color-surface);
147
- color: var(--color-text);
148
- box-shadow: var(--shadow-md);
149
-
150
- font-size: var(--text-sm);
151
- line-height: var(--line-height-normal);
152
-
153
- opacity: 0;
154
- transform: translateY(8px);
155
- position: relative;
156
- }
157
-
158
- .toast__icon {
159
- grid-area: icon;
160
- display: flex;
161
- align-items: center;
162
- justify-content: center;
163
- margin-top: 0;
164
- width: var(--icon-sm);
165
- height: var(--icon-sm);
166
- flex-shrink: 0;
167
- }
168
-
169
- .toast__body {
170
- grid-area: body;
171
- display: flex;
172
- flex-direction: column;
173
- gap: var(--space-1);
174
- min-width: 0;
175
- }
176
-
177
- .toast__title {
178
- font-weight: var(--font-weight-semibold);
179
- }
180
-
181
- .toast__message {
182
- font-size: var(--text-xs);
183
- color: var(--color-text-muted);
184
- }
185
-
186
- .toast__close {
187
- position: absolute;
188
- top: var(--space-2);
189
- right: var(--space-2);
190
-
191
- -webkit-appearance: none;
192
-
193
- -moz-appearance: none;
194
-
195
- appearance: none;
196
- border: none;
197
- padding: var(--space-1);
198
- background: transparent;
199
- color: var(--color-text-soft);
200
- cursor: pointer;
201
- line-height: 0;
202
-
203
- display: inline-flex;
204
- align-items: center;
205
- justify-content: center;
206
- }
207
-
208
- .toast__close:hover {
209
- color: var(--color-text);
210
- }
211
-
212
- .toast__close:focus-visible {
213
- outline: none;
214
- box-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring-color);
215
- }
216
-
217
- .toast--in {
218
- animation: toast-slide-in 180ms ease-out forwards;
219
- animation: toast-slide-in 180ms var(--ease-out, ease-out) forwards;
220
- }
221
-
222
- .toast--out {
223
- animation: toast-slide-out 150ms ease-in forwards;
224
- animation: toast-slide-out 150ms var(--ease-in, ease-in) forwards;
225
- }
226
-
227
- .toast__progress {
228
- grid-area: progress;
229
- position: relative;
230
- margin-top: var(--space-1);
231
- height: 2px;
232
- border-radius: var(--radius-full);
233
- background-color: rgba(0, 0, 0, 0.08);
234
- overflow: hidden;
235
- }
236
-
237
- .toast__progress::before {
238
- content: "";
239
- position: absolute;
240
- top: 0;
241
- right: 0;
242
- bottom: 0;
243
- left: 0;
244
- transform-origin: left center;
245
- transform: scaleX(1);
246
- background-color: currentColor;
247
- animation: toast-progress var(--toast-duration) linear forwards;
248
- }
249
-
250
- .toast--compact {
251
- grid-template-rows: auto;
252
- grid-template-areas: "icon body close";
253
- }
254
-
255
- .toast--compact .toast__message {
256
- font-size: var(--text-sm);
257
- color: inherit;
258
- }
259
-
260
- .toast--compact .toast__title {
261
- display: none;
262
- }
263
-
264
- .toast--compact .toast__progress {
265
- display: none;
266
- }
267
-
268
- /* ---------------------------------------------------------
269
- 4. INLINE VARIANT (NON-FLOATING)
270
- Usage:
271
- <div class="toast toast--inline toast--warning">...</div>
272
- --------------------------------------------------------- */
273
-
274
- .toast--inline {
275
- position: static;
276
- box-shadow: none;
277
- width: 100%;
278
- margin-top: var(--space-2);
279
- margin-bottom: var(--space-2);
280
- }
281
-
282
- .toast--inline .toast__progress {
283
- display: none;
284
- }
285
-
286
- /* ---------------------------------------------------------
287
- 5. SEMANTIC VARIANTS
288
- --------------------------------------------------------- */
289
-
290
- .toast--neutral {
291
- border-color: var(--color-muted-border);
292
- background-color: var(--color-muted-bg);
293
- color: var(--color-text);
294
- }
295
-
296
- .toast--info {
297
- --toast-bg: var(--semantic-info-bg-strong, var(--color-info-soft));
298
- --toast-border: var(--semantic-info-border-strong, var(--color-info));
299
- --toast-text: var(--semantic-info-text-strong, var(--color-info-strong));
300
- }
301
-
302
- .toast--success {
303
- --toast-bg: var(--semantic-success-bg-strong, var(--color-success-soft));
304
- --toast-border: var(--semantic-success-border-strong, var(--color-success));
305
- --toast-text: var(--semantic-success-text-strong, var(--color-success-strong));
306
- }
307
-
308
- .toast--warning {
309
- --toast-bg: var(--semantic-warning-bg-strong, var(--color-warning-soft));
310
- --toast-border: var(--semantic-warning-border-strong, var(--color-warning));
311
- --toast-text: var(--semantic-warning-text-strong, var(--color-warning-strong));
312
- }
313
-
314
- .toast--danger,
315
- .toast--error {
316
- --toast-bg: var(--semantic-error-bg-strong, var(--color-danger-soft));
317
- --toast-border: var(--semantic-error-border-strong, var(--color-danger));
318
- --toast-text: var(--semantic-error-text-strong, var(--color-danger-strong));
319
- }
320
-
321
- .toast--info,
322
- .toast--success,
323
- .toast--warning,
324
- .toast--danger,
325
- .toast--error {
326
- background-color: var(--toast-bg);
327
- color: var(--toast-text);
328
- border-left: var(--border-width-strong) solid var(--toast-border);
329
- }
330
-
331
- .toast--info .toast__message,
332
- .toast--success .toast__message,
333
- .toast--warning .toast__message,
334
- .toast--danger .toast__message,
335
- .toast--error .toast__message,
336
- .toast--neutral .toast__message {
337
- color: inherit;
338
- }
339
-
340
- /* ---------------------------------------------------------
341
- 6. ANIMATION STATES (ENTER / EXIT)
342
- --------------------------------------------------------- */
343
-
344
- .toast--exiting {
345
- animation: toast-slide-out var(--transition-fast) var(--ease-in) forwards;
346
- }
347
-
348
- @keyframes toast-slide-in {
349
- from {
350
- opacity: 0;
351
- transform: translateY(8px);
352
- }
353
- to {
354
- opacity: 1;
355
- transform: translateY(0);
356
- }
357
- }
358
-
359
- @keyframes toast-slide-out {
360
- from {
361
- opacity: 1;
362
- transform: translateY(0);
363
- }
364
- to {
365
- opacity: 0;
366
- transform: translateY(6px);
367
- }
368
- }
369
-
370
- @keyframes toast-progress {
371
- from {
372
- transform: scaleX(1);
373
- }
374
- to {
375
- transform: scaleX(0);
376
- }
377
- }
378
-
379
- @keyframes toast-slide-in {
380
- from {
381
- opacity: 0;
382
- transform: translateY(8px);
383
- }
384
- to {
385
- opacity: 1;
386
- transform: translateY(0);
387
- }
388
- }
389
-
390
- @keyframes toast-slide-out {
391
- from {
392
- opacity: 1;
393
- transform: translateY(0);
394
- }
395
- to {
396
- opacity: 0;
397
- transform: translateY(6px);
398
- }
399
- }
400
-
401
- /* ---------------------------------------------------------
402
- 7. REDUCED MOTION FALLBACKS
403
- --------------------------------------------------------- */
404
-
405
- @media (prefers-reduced-motion: reduce) {
406
- .toast {
407
- animation: none;
408
- opacity: 1;
409
- transform: none;
410
- transition: none;
411
- }
412
-
413
- .toast--exiting {
414
- animation: none;
415
- opacity: 0;
416
- }
417
-
418
- .toast__progress::before {
419
- animation-duration: var(--motion-none);
420
- }
421
- }
1
+ .vds-toast,[data-vds-toast]{--toast-duration:5000ms;--toast-feedback-padding-md:var(--space-4)}.toast-stack{align-items:stretch;display:flex;flex-direction:column;gap:var(--space-2);max-width:100%;pointer-events:none;position:fixed;width:20rem;z-index:var(--z-toast)}.toast-stack .toast{width:100%}.toast-stack--top-right{align-items:flex-end;right:var(--space-4);top:var(--space-4)}.toast-stack--top-left{align-items:flex-start;left:var(--space-4);top:var(--space-4)}.toast-stack--bottom-right{align-items:flex-end;bottom:var(--space-4);right:var(--space-4)}.toast-stack--bottom-left{align-items:flex-start;bottom:var(--space-4);left:var(--space-4)}.toast-stack--top-center{top:var(--space-4)}.toast-stack--bottom-center,.toast-stack--top-center{align-items:center;left:50%;transform:translateX(-50%)}.toast-stack--bottom-center{bottom:var(--space-4)}@media (max-width:640px){.toast-stack{left:var(--space-2);max-width:none;right:var(--space-2)}.toast-stack--top-center,.toast-stack--top-left,.toast-stack--top-right{align-items:stretch;bottom:var(--space-2);top:auto;transform:none}.toast-stack--bottom-center,.toast-stack--bottom-left,.toast-stack--bottom-right{align-items:stretch;bottom:var(--space-2);transform:none}}.toast{grid-column-gap:var(--space-3);grid-row-gap:var(--space-1);background-color:var(--color-surface);border:var(--border-width) solid var(--color-border-subtle);border-radius:var(--radius-md);box-shadow:var(--shadow-md);color:var(--color-text);-moz-column-gap:var(--space-3);column-gap:var(--space-3);display:grid;font-size:var(--text-sm);grid-template-areas:"icon body close" "progress progress progress";grid-template-columns:auto 1fr auto;grid-template-rows:auto auto;line-height:var(--line-height-normal);opacity:0;padding:var(--toast-feedback-padding-md);pointer-events:auto;position:relative;row-gap:var(--space-1);transform:translateY(8px)}.toast__icon{align-items:center;display:flex;flex-shrink:0;grid-area:icon;height:var(--icon-sm);justify-content:center;margin-top:0;width:var(--icon-sm)}.toast__body{display:flex;flex-direction:column;gap:var(--space-1);grid-area:body;min-width:0}.toast__title{font-weight:var(--font-weight-semibold)}.toast__message{color:var(--color-text-muted);font-size:var(--text-xs)}.toast__close{align-items:center;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:none;color:var(--color-text-soft);cursor:pointer;display:inline-flex;justify-content:center;line-height:0;padding:var(--space-1);position:absolute;right:var(--space-2);top:var(--space-2)}.toast__close:hover{color:var(--color-text)}.toast__close:focus-visible{box-shadow:0 0 0 var(--focus-ring-width) var(--focus-ring-color);outline:none}.toast--in{animation:toast-slide-in .18s ease-out forwards;animation:toast-slide-in .18s var(--ease-out,ease-out) forwards}.toast--out{animation:toast-slide-out .15s ease-in forwards;animation:toast-slide-out .15s var(--ease-in,ease-in) forwards}.toast__progress{background-color:rgba(0,0,0,.08);border-radius:var(--radius-full);grid-area:progress;height:2px;margin-top:var(--space-1);overflow:hidden;position:relative}.toast__progress:before{animation:toast-progress var(--toast-duration) linear forwards;background-color:currentColor;bottom:0;content:"";left:0;position:absolute;right:0;top:0;transform:scaleX(1);transform-origin:left center}.toast--compact{grid-template-areas:"icon body close";grid-template-rows:auto}.toast--compact .toast__message{color:inherit;font-size:var(--text-sm)}.toast--compact .toast__progress,.toast--compact .toast__title{display:none}.toast--inline{box-shadow:none;margin-bottom:var(--space-2);margin-top:var(--space-2);position:static;width:100%}.toast--inline .toast__progress{display:none}.toast--neutral{background-color:var(--color-muted-bg);border-color:var(--color-muted-border);color:var(--color-text)}.toast--info{--toast-bg:var(--semantic-info-bg-strong,var(--color-info-soft));--toast-border:var(--semantic-info-border-strong,var(--color-info));--toast-text:var(--semantic-info-text-strong,var(--color-info-strong))}.toast--success{--toast-bg:var(--semantic-success-bg-strong,var(--color-success-soft));--toast-border:var(--semantic-success-border-strong,var(--color-success));--toast-text:var(--semantic-success-text-strong,var(--color-success-strong))}.toast--warning{--toast-bg:var(--semantic-warning-bg-strong,var(--color-warning-soft));--toast-border:var(--semantic-warning-border-strong,var(--color-warning));--toast-text:var(--semantic-warning-text-strong,var(--color-warning-strong))}.toast--danger,.toast--error{--toast-bg:var(--semantic-error-bg-strong,var(--color-danger-soft));--toast-border:var(--semantic-error-border-strong,var(--color-danger));--toast-text:var(--semantic-error-text-strong,var(--color-danger-strong))}.toast--danger,.toast--error,.toast--info,.toast--success,.toast--warning{background-color:var(--toast-bg);border-left:var(--border-width-strong) solid var(--toast-border);color:var(--toast-text)}.toast--danger .toast__message,.toast--error .toast__message,.toast--info .toast__message,.toast--neutral .toast__message,.toast--success .toast__message,.toast--warning .toast__message{color:inherit}.toast--exiting{animation:toast-slide-out var(--transition-fast) var(--ease-in) forwards}@keyframes toast-progress{0%{transform:scaleX(1)}to{transform:scaleX(0)}}@keyframes toast-slide-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes toast-slide-out{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(6px)}}@media (prefers-reduced-motion:reduce){.toast{animation:none;opacity:1;transform:none;transition:none}.toast--exiting{animation:none;opacity:0}.toast__progress:before{animation-duration:var(--motion-none)}}