@justin_evo/evo-ui 1.2.0 → 1.2.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.
Files changed (77) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +70 -70
  3. package/dist/declarations.d.ts +6 -6
  4. package/package.json +52 -52
  5. package/src/Alert/Alert.tsx +49 -49
  6. package/src/AutoComplete/AutoComplete.tsx +810 -810
  7. package/src/Badge/Badge.tsx +53 -53
  8. package/src/Breadcrumb/Breadcrumb.tsx +53 -53
  9. package/src/Button/Button.tsx +125 -125
  10. package/src/Card/Card.tsx +257 -257
  11. package/src/Checkbox/Checkbox.tsx +59 -59
  12. package/src/CommandPalette/CommandPalette.tsx +185 -185
  13. package/src/Container/Container.tsx +31 -31
  14. package/src/Divider/Divider.tsx +31 -31
  15. package/src/Form/Form.tsx +185 -185
  16. package/src/Grid/Grid.tsx +66 -66
  17. package/src/ImageCropper/ImageCropper.tsx +911 -911
  18. package/src/Input/Input.tsx +74 -74
  19. package/src/Modal/Modal.tsx +77 -77
  20. package/src/Nav/Nav.tsx +708 -708
  21. package/src/Notification/Notification.tsx +1503 -1503
  22. package/src/Pagination/Pagination.tsx +76 -76
  23. package/src/Radio/Radio.tsx +69 -69
  24. package/src/RichTextArea/RichTextArea.tsx +886 -886
  25. package/src/Select/Select.tsx +515 -515
  26. package/src/Skeleton/Skeleton.tsx +70 -70
  27. package/src/Stack/Stack.tsx +52 -52
  28. package/src/Table/Table.tsx +335 -335
  29. package/src/Tabs/Tabs.tsx +90 -90
  30. package/src/Theme/ThemeProvider.tsx +253 -253
  31. package/src/Theme/ThemeToggle.tsx +79 -79
  32. package/src/Toggle/Toggle.tsx +48 -48
  33. package/src/Tooltip/Tooltip.tsx +38 -38
  34. package/src/TopNav/TopNav.tsx +1163 -1163
  35. package/src/TreeSelect/TreeSelect.tsx +825 -825
  36. package/src/css/alert.module.scss +93 -93
  37. package/src/css/autocomplete.module.scss +416 -416
  38. package/src/css/badge.module.scss +82 -82
  39. package/src/css/base/_color.scss +159 -159
  40. package/src/css/base/_theme.scss +237 -237
  41. package/src/css/base/_variables.scss +161 -161
  42. package/src/css/breadcrumb.module.scss +50 -50
  43. package/src/css/button.module.scss +385 -385
  44. package/src/css/card.module.scss +217 -217
  45. package/src/css/checkbox.module.scss +123 -123
  46. package/src/css/commandpalette.module.scss +211 -211
  47. package/src/css/container.module.scss +18 -18
  48. package/src/css/divider.module.scss +41 -41
  49. package/src/css/form.module.scss +245 -245
  50. package/src/css/imagecropper.module.scss +397 -397
  51. package/src/css/input.module.scss +89 -89
  52. package/src/css/modal.module.scss +105 -105
  53. package/src/css/nav.module.scss +494 -494
  54. package/src/css/notification.module.scss +691 -691
  55. package/src/css/pagination.module.scss +63 -63
  56. package/src/css/radio.module.scss +89 -89
  57. package/src/css/richtextarea.module.scss +307 -307
  58. package/src/css/select.module.scss +525 -525
  59. package/src/css/skeleton.module.scss +30 -30
  60. package/src/css/table.module.scss +386 -386
  61. package/src/css/tabs.module.scss +63 -63
  62. package/src/css/theme-toggle.module.scss +83 -83
  63. package/src/css/toggle.module.scss +54 -54
  64. package/src/css/tooltip.module.scss +97 -97
  65. package/src/css/topnav.module.scss +568 -568
  66. package/src/css/treeselect.module.scss +558 -558
  67. package/src/css/utilities/_borders.scss +111 -111
  68. package/src/css/utilities/_colors.scss +66 -66
  69. package/src/css/utilities/_effects.scss +216 -216
  70. package/src/css/utilities/_layout.scss +181 -181
  71. package/src/css/utilities/_position.scss +75 -75
  72. package/src/css/utilities/_sizing.scss +138 -138
  73. package/src/css/utilities/_spacing.scss +99 -99
  74. package/src/css/utilities/_typography.scss +121 -121
  75. package/src/css/utilities/index.scss +24 -24
  76. package/src/declarations.d.ts +6 -6
  77. package/src/index.ts +60 -60
@@ -1,691 +1,691 @@
1
- @use 'base/variables' as *;
2
- @use 'base/color' as *;
3
-
4
- // ============================================================
5
- // EvoNotification — toast + inbox stylesheet
6
- // ------------------------------------------------------------
7
- // Two visual subsystems share this file:
8
- // 1. Toast (transient, portaled to body)
9
- // 2. Bell + Panel + Item (inbox center)
10
- // All colour comes from --evo-color-* tokens so dark mode is
11
- // automatic. Six anchors and a strict mobile breakpoint below.
12
- // ============================================================
13
-
14
- // ---------- Animations ----------
15
-
16
- @keyframes evoToastEnterRight {
17
- from { transform: translateX(110%); opacity: 0; }
18
- to { transform: translateX(0); opacity: 1; }
19
- }
20
-
21
- @keyframes evoToastEnterLeft {
22
- from { transform: translateX(-110%); opacity: 0; }
23
- to { transform: translateX(0); opacity: 1; }
24
- }
25
-
26
- @keyframes evoToastEnterTop {
27
- from { transform: translateY(-110%); opacity: 0; }
28
- to { transform: translateY(0); opacity: 1; }
29
- }
30
-
31
- @keyframes evoToastEnterBottom {
32
- from { transform: translateY(110%); opacity: 0; }
33
- to { transform: translateY(0); opacity: 1; }
34
- }
35
-
36
- @keyframes evoToastExit {
37
- from { opacity: 1; transform: scale(1); }
38
- to { opacity: 0; transform: scale(0.92); }
39
- }
40
-
41
- @keyframes evoPanelEnter {
42
- from { opacity: 0; transform: translateY(-6px) scale(0.98); }
43
- to { opacity: 1; transform: translateY(0) scale(1); }
44
- }
45
-
46
- // ---------- Toaster (portal root) ----------
47
-
48
- .toasterRoot {
49
- position: fixed;
50
- inset: 0;
51
- pointer-events: none;
52
- z-index: 10000;
53
- }
54
-
55
- // Visually-hidden screen-reader live region. Always present in the DOM so
56
- // announcements fire reliably when its text content changes.
57
- .srOnly {
58
- position: absolute;
59
- width: 1px;
60
- height: 1px;
61
- margin: -1px;
62
- padding: 0;
63
- border: 0;
64
- overflow: hidden;
65
- clip: rect(0, 0, 0, 0);
66
- white-space: nowrap;
67
- }
68
-
69
- .anchor {
70
- position: absolute;
71
- display: flex;
72
- flex-direction: column;
73
- gap: 0.5rem;
74
- padding: 1rem;
75
- max-width: min(420px, calc(100vw - 2rem));
76
- pointer-events: none;
77
- }
78
-
79
- .anchor > * {
80
- pointer-events: auto;
81
- }
82
-
83
- // Stacking direction. The newest toast is always rendered last in the DOM and
84
- // must sit flush against the anchored screen edge — top anchors grow downward
85
- // from the top, bottom anchors grow upward from the bottom. So top anchors
86
- // reverse the column (DOM-last renders visually first/topmost) while bottom
87
- // anchors keep the natural column (DOM-last renders visually last/bottom-most).
88
- // The depth offset in ToastRow then pushes older cards *away* from that edge.
89
- .anchor-top-left { top: 0; left: 0; align-items: flex-start; flex-direction: column-reverse; }
90
- .anchor-top-center { top: 0; left: 50%; transform: translateX(-50%); align-items: center; flex-direction: column-reverse; }
91
- .anchor-top-right { top: 0; right: 0; align-items: flex-end; flex-direction: column-reverse; }
92
- .anchor-bottom-left { bottom: 0; left: 0; align-items: flex-start; }
93
- .anchor-bottom-center {
94
- bottom: 0; left: 50%; transform: translateX(-50%);
95
- align-items: center;
96
- }
97
- .anchor-bottom-right {
98
- bottom: 0; right: 0; align-items: flex-end;
99
- }
100
-
101
- // Mobile: pin to top or bottom edge, full-width.
102
- @media (max-width: 23.4375rem) { // 375px
103
- .anchor {
104
- left: 0 !important;
105
- right: 0 !important;
106
- transform: none !important;
107
- max-width: 100%;
108
- padding: 0.5rem;
109
- align-items: stretch;
110
- }
111
- }
112
-
113
- .overflowPill {
114
- font-size: $text-xs;
115
- font-weight: 600;
116
- color: $color-text-secondary;
117
- background-color: $color-surface-elevated;
118
- border: 1px solid $color-border;
119
- border-radius: $radius-full;
120
- padding: 0.25rem 0.625rem;
121
- box-shadow: $shadow-sm;
122
- }
123
-
124
- // ---------- Single toast card ----------
125
-
126
- .toast {
127
- position: relative;
128
- display: flex;
129
- align-items: flex-start;
130
- gap: 0.625rem;
131
- padding: 0.75rem 0.875rem;
132
- min-width: 280px;
133
- max-width: 100%;
134
- background-color: $color-surface-elevated;
135
- color: $color-text-primary;
136
- border: 1px solid $color-border;
137
- border-radius: $radius-md;
138
- box-shadow: $shadow-xl;
139
- font-family: $font-sans;
140
- font-size: $text-sm;
141
- line-height: 1.45;
142
- // Left colour bar (severity)
143
- border-left-width: 3px;
144
- transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1),
145
- opacity 220ms ease;
146
- will-change: transform, opacity;
147
-
148
- // Width parity on mobile.
149
- @media (max-width: 23.4375rem) {
150
- min-width: 0;
151
- width: 100%;
152
- }
153
- }
154
-
155
- // Anchor-aware enter animations applied via parent.
156
- .anchor-top-right > .toast { animation: evoToastEnterRight 240ms cubic-bezier(0.16, 1, 0.3, 1); }
157
- .anchor-bottom-right > .toast { animation: evoToastEnterRight 240ms cubic-bezier(0.16, 1, 0.3, 1); }
158
- .anchor-top-left > .toast { animation: evoToastEnterLeft 240ms cubic-bezier(0.16, 1, 0.3, 1); }
159
- .anchor-bottom-left > .toast { animation: evoToastEnterLeft 240ms cubic-bezier(0.16, 1, 0.3, 1); }
160
- .anchor-top-center > .toast { animation: evoToastEnterTop 240ms cubic-bezier(0.16, 1, 0.3, 1); }
161
- .anchor-bottom-center > .toast { animation: evoToastEnterBottom 240ms cubic-bezier(0.16, 1, 0.3, 1); }
162
-
163
- .toast.exiting {
164
- animation: evoToastExit 180ms ease forwards;
165
- }
166
-
167
- .toast.noMotion,
168
- .toast.noMotion.exiting {
169
- animation: none !important;
170
- transition: opacity 1ms linear !important;
171
- }
172
-
173
- @media (prefers-reduced-motion: reduce) {
174
- .toast,
175
- .toast.exiting {
176
- animation: none !important;
177
- transition: opacity 1ms linear !important;
178
- }
179
- }
180
-
181
- .sev-success {
182
- border-left-color: $evo-success-color;
183
- .toastIcon { background-color: $evo-success-color; color: $evo-success-fg; }
184
- }
185
- .sev-error {
186
- border-left-color: $evo-danger-color;
187
- .toastIcon { background-color: $evo-danger-color; color: $evo-danger-fg; }
188
- }
189
- .sev-warning {
190
- border-left-color: $evo-warning-color;
191
- .toastIcon { background-color: $evo-warning-color; color: $evo-warning-text; }
192
- }
193
- .sev-info {
194
- border-left-color: $evo-info-color;
195
- .toastIcon { background-color: $evo-info-color; color: $evo-info-fg; }
196
- }
197
-
198
- .toastIcon {
199
- display: inline-flex;
200
- align-items: center;
201
- justify-content: center;
202
- width: 1.25rem;
203
- height: 1.25rem;
204
- border-radius: 50%;
205
- font-size: $text-xs;
206
- font-weight: 700;
207
- flex-shrink: 0;
208
- margin-top: 0.0625rem;
209
- }
210
-
211
- .toastBody {
212
- flex: 1;
213
- min-width: 0;
214
- }
215
-
216
- .toastTitle {
217
- font-weight: 600;
218
- color: $color-text-primary;
219
- }
220
-
221
- .toastDescription {
222
- margin-top: 0.1875rem;
223
- color: $color-text-secondary;
224
- font-size: $text-sm;
225
- }
226
-
227
- // Count badge for coalesced toasts (groupKey) — "×3" beside the title.
228
- .toastCount {
229
- display: inline-flex;
230
- align-items: center;
231
- justify-content: center;
232
- min-width: 1.25rem;
233
- height: 1.125rem;
234
- margin-left: 0.375rem;
235
- padding: 0 0.3125rem;
236
- font-size: $text-xs;
237
- font-weight: 700;
238
- font-variant-numeric: tabular-nums;
239
- color: $color-text-secondary;
240
- background-color: $color-surface-sunken;
241
- border: 1px solid $color-border;
242
- border-radius: $radius-full;
243
- vertical-align: middle;
244
- }
245
-
246
- // Determinate progress bar (toast.progress / EvoToastOptions.progress).
247
- // Pinned along the bottom edge of the card.
248
- .toastProgressTrack {
249
- position: absolute;
250
- left: 0;
251
- right: 0;
252
- bottom: 0;
253
- height: 3px;
254
- background-color: $color-border-subtle;
255
- border-bottom-left-radius: $radius-md;
256
- border-bottom-right-radius: $radius-md;
257
- overflow: hidden;
258
- }
259
-
260
- .toastProgressFill {
261
- height: 100%;
262
- width: 100%;
263
- transform-origin: left center;
264
- background-color: $evo-primary-color;
265
- transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1);
266
- }
267
-
268
- // Fill tracks the toast severity so a resolved progress toast reads green.
269
- .sev-success .toastProgressFill { background-color: $evo-success-color; }
270
- .sev-error .toastProgressFill { background-color: $evo-danger-color; }
271
- .sev-warning .toastProgressFill { background-color: $evo-warning-color; }
272
- .sev-info .toastProgressFill { background-color: $evo-info-color; }
273
-
274
- .toast.noMotion .toastProgressFill { transition: none; }
275
-
276
- @media (prefers-reduced-motion: reduce) {
277
- .toastProgressFill { transition: none; }
278
- }
279
-
280
- .toastAction {
281
- align-self: center;
282
- font: inherit;
283
- font-weight: 600;
284
- font-size: $text-xs;
285
- color: $evo-primary-color;
286
- background: transparent;
287
- border: 1px solid $color-border;
288
- border-radius: $radius-sm;
289
- padding: 0.25rem 0.5rem;
290
- cursor: pointer;
291
- flex-shrink: 0;
292
- min-height: 1.75rem;
293
- transition: background-color $transition-fast, border-color $transition-fast;
294
-
295
- &:hover { background-color: $color-surface-hover; }
296
- &:focus-visible {
297
- outline: 2px solid $evo-primary-focus;
298
- outline-offset: 1px;
299
- }
300
- }
301
-
302
- .toastClose {
303
- align-self: flex-start;
304
- background: transparent;
305
- border: none;
306
- color: $color-text-muted;
307
- font-size: $text-xs;
308
- cursor: pointer;
309
- padding: 0.25rem;
310
- border-radius: $radius-sm;
311
- flex-shrink: 0;
312
- transition: color $transition-fast, background-color $transition-fast;
313
-
314
- &:hover { color: $color-text-primary; background-color: $color-surface-hover; }
315
- &:focus-visible {
316
- outline: 2px solid $evo-primary-focus;
317
- outline-offset: 1px;
318
- }
319
- }
320
-
321
- // ---------- Bell ----------
322
-
323
- .bellWrapper {
324
- position: relative;
325
- display: inline-block;
326
- }
327
-
328
- .bell {
329
- position: relative;
330
- display: inline-flex;
331
- align-items: center;
332
- justify-content: center;
333
- background: transparent;
334
- border: 1px solid transparent;
335
- border-radius: $radius-full;
336
- color: $color-text-primary;
337
- cursor: pointer;
338
- font: inherit;
339
- transition: background-color $transition-fast, color $transition-fast, border-color $transition-fast;
340
-
341
- &:hover { background-color: $color-surface-hover; }
342
- &:focus-visible {
343
- outline: 2px solid $evo-primary-focus;
344
- outline-offset: 2px;
345
- }
346
- }
347
-
348
- .bell-ghost { background-color: transparent; }
349
- .bell-solid {
350
- background-color: $color-surface-elevated;
351
- border-color: $color-border;
352
- }
353
-
354
- .bell-sm { width: 2rem; height: 2rem; }
355
- .bell-md { width: 2.75rem; height: 2.75rem; } // 44px — WCAG touch target
356
- .bell-lg { width: 3rem; height: 3rem; }
357
-
358
- .bellOpen {
359
- background-color: $color-surface-hover;
360
- }
361
-
362
- .bellBadge {
363
- position: absolute;
364
- top: 0.125rem;
365
- right: 0.125rem;
366
- min-width: 1rem;
367
- height: 1rem;
368
- padding: 0 0.25rem;
369
- background-color: $evo-danger-color;
370
- color: $evo-danger-fg;
371
- font-size: 0.625rem;
372
- font-weight: 700;
373
- border-radius: $radius-full;
374
- display: inline-flex;
375
- align-items: center;
376
- justify-content: center;
377
- border: 2px solid $color-surface;
378
- line-height: 1;
379
- }
380
-
381
- .bellBadgeZero {
382
- background-color: $color-border-strong;
383
- color: $color-text-inverted;
384
- }
385
-
386
- // ---------- Panel placement (anchored to Bell) ----------
387
-
388
- .bellPanelHost {
389
- position: absolute;
390
- z-index: 9001;
391
- min-width: 22rem;
392
- max-width: 24rem;
393
-
394
- @media (max-width: 30rem) {
395
- position: fixed;
396
- top: auto !important;
397
- bottom: auto !important;
398
- left: 0.5rem !important;
399
- right: 0.5rem !important;
400
- min-width: 0;
401
- max-width: none;
402
- }
403
- }
404
-
405
- .place-bottom-end { top: calc(100% + 0.5rem); right: 0; }
406
- .place-bottom-start { top: calc(100% + 0.5rem); left: 0; }
407
- .place-bottom { top: calc(100% + 0.5rem); left: 50%; transform: translateX(-50%); }
408
- .place-top-end { bottom: calc(100% + 0.5rem); right: 0; }
409
- .place-top-start { bottom: calc(100% + 0.5rem); left: 0; }
410
-
411
- // ---------- Panel ----------
412
-
413
- .panel {
414
- background-color: $color-surface-elevated;
415
- border: 1px solid $color-border;
416
- border-radius: $radius-lg;
417
- box-shadow: $shadow-xl;
418
- display: flex;
419
- flex-direction: column;
420
- overflow: hidden;
421
- font-family: $font-sans;
422
- color: $color-text-primary;
423
- animation: evoPanelEnter 160ms ease;
424
-
425
- @media (prefers-reduced-motion: reduce) {
426
- animation: none;
427
- }
428
- }
429
-
430
- .panelHeader {
431
- display: flex;
432
- align-items: center;
433
- justify-content: space-between;
434
- padding: 0.75rem 1rem;
435
- border-bottom: 1px solid $color-border;
436
- gap: 0.5rem;
437
- }
438
-
439
- .panelTitle {
440
- font-weight: 600;
441
- font-size: $text-sm;
442
- }
443
-
444
- .panelHeaderActions {
445
- display: inline-flex;
446
- align-items: center;
447
- gap: 0.25rem;
448
- }
449
-
450
- .panelMarkAll {
451
- background: transparent;
452
- border: none;
453
- cursor: pointer;
454
- font: inherit;
455
- font-size: $text-xs;
456
- font-weight: 600;
457
- color: $evo-primary-color;
458
- padding: 0.25rem 0.5rem;
459
- border-radius: $radius-sm;
460
- min-height: 1.75rem;
461
-
462
- &:hover { background-color: $color-surface-hover; }
463
- &:focus-visible {
464
- outline: 2px solid $evo-primary-focus;
465
- outline-offset: 1px;
466
- }
467
- }
468
-
469
- .panelClose {
470
- background: transparent;
471
- border: none;
472
- cursor: pointer;
473
- color: $color-text-muted;
474
- padding: 0.25rem;
475
- font-size: $text-xs;
476
- border-radius: $radius-sm;
477
- min-height: 1.75rem;
478
- min-width: 1.75rem;
479
- display: inline-flex;
480
- align-items: center;
481
- justify-content: center;
482
-
483
- &:hover { color: $color-text-primary; background-color: $color-surface-hover; }
484
- }
485
-
486
- .panelBody {
487
- overflow-y: auto;
488
- }
489
-
490
- .panelState {
491
- display: flex;
492
- align-items: center;
493
- justify-content: center;
494
- padding: 2rem 1.5rem;
495
- color: $color-text-secondary;
496
- font-size: $text-sm;
497
- text-align: center;
498
- }
499
-
500
- .panelStateError {
501
- color: $evo-danger-color;
502
- }
503
-
504
- .itemList {
505
- list-style: none;
506
- margin: 0;
507
- padding: 0;
508
-
509
- li {
510
- border-bottom: 1px solid $color-border-subtle;
511
-
512
- &:last-child { border-bottom: none; }
513
- }
514
- }
515
-
516
- // ---------- Empty state ----------
517
-
518
- .emptyState {
519
- display: flex;
520
- flex-direction: column;
521
- align-items: center;
522
- gap: 0.5rem;
523
- padding: 2rem 1rem;
524
- text-align: center;
525
- }
526
-
527
- .emptyIcon {
528
- display: inline-flex;
529
- align-items: center;
530
- justify-content: center;
531
- width: 2.5rem;
532
- height: 2.5rem;
533
- border-radius: 50%;
534
- background-color: $color-surface-sunken;
535
- color: $color-text-muted;
536
- }
537
-
538
- .emptyTitle {
539
- font-weight: 600;
540
- font-size: $text-sm;
541
- color: $color-text-primary;
542
- }
543
-
544
- .emptyHint {
545
- font-size: $text-xs;
546
- color: $color-text-secondary;
547
- }
548
-
549
- // ---------- Item ----------
550
-
551
- .item {
552
- position: relative;
553
- display: flex;
554
- align-items: flex-start;
555
- gap: 0.625rem;
556
- padding: 0.75rem 1rem 0.75rem 1.25rem;
557
- background-color: transparent;
558
- transition: background-color $transition-fast;
559
-
560
- &.itemUnread { background-color: color-mix(in srgb, $evo-primary-color 6%, transparent); }
561
- }
562
-
563
- .itemInteractive {
564
- cursor: pointer;
565
-
566
- &:hover { background-color: $color-surface-hover; }
567
- &:focus-visible {
568
- outline: 2px solid $evo-primary-focus;
569
- outline-offset: -2px;
570
- }
571
- }
572
-
573
- .itemUnreadDot {
574
- position: absolute;
575
- left: 0.5rem;
576
- top: 1rem;
577
- width: 0.4375rem;
578
- height: 0.4375rem;
579
- border-radius: 50%;
580
- background-color: $evo-primary-color;
581
- opacity: 1;
582
-
583
- // Hidden when read (parent doesn't get .itemUnread).
584
- .item:not(.itemUnread) & { opacity: 0; }
585
- }
586
-
587
- .itemMedia {
588
- width: 2rem;
589
- height: 2rem;
590
- border-radius: 50%;
591
- display: inline-flex;
592
- align-items: center;
593
- justify-content: center;
594
- color: $color-text-inverted;
595
- font-size: $text-xs;
596
- font-weight: 700;
597
- flex-shrink: 0;
598
- overflow: hidden;
599
-
600
- &.sev-success { background-color: $evo-success-color; }
601
- &.sev-error { background-color: $evo-danger-color; }
602
- &.sev-warning { background-color: $evo-warning-color; }
603
- &.sev-info { background-color: $evo-info-color; }
604
- }
605
-
606
- .itemAvatar {
607
- width: 100%;
608
- height: 100%;
609
- object-fit: cover;
610
- }
611
-
612
- .itemMediaGlyph {
613
- line-height: 1;
614
- }
615
-
616
- .itemBody {
617
- flex: 1;
618
- min-width: 0;
619
- }
620
-
621
- .itemTitle {
622
- font-size: $text-sm;
623
- font-weight: 600;
624
- color: $color-text-primary;
625
- line-height: 1.35;
626
- }
627
-
628
- .itemDescription {
629
- margin-top: 0.125rem;
630
- font-size: $text-sm;
631
- color: $color-text-secondary;
632
- line-height: 1.4;
633
- display: -webkit-box;
634
- -webkit-line-clamp: 2;
635
- -webkit-box-orient: vertical;
636
- overflow: hidden;
637
- }
638
-
639
- .itemMeta {
640
- margin-top: 0.375rem;
641
- display: flex;
642
- align-items: center;
643
- gap: 0.5rem;
644
- font-size: $text-xs;
645
- color: $color-text-muted;
646
- }
647
-
648
- .itemTimestamp {
649
- font-variant-numeric: tabular-nums;
650
- }
651
-
652
- .itemAction {
653
- background: transparent;
654
- border: 1px solid $color-border;
655
- color: $evo-primary-color;
656
- border-radius: $radius-sm;
657
- cursor: pointer;
658
- font: inherit;
659
- font-size: $text-xs;
660
- font-weight: 600;
661
- padding: 0.1875rem 0.5rem;
662
- min-height: 1.5rem;
663
-
664
- &:hover { background-color: $color-surface-hover; }
665
- &:focus-visible {
666
- outline: 2px solid $evo-primary-focus;
667
- outline-offset: 1px;
668
- }
669
- }
670
-
671
- .itemDismiss {
672
- align-self: flex-start;
673
- background: transparent;
674
- border: none;
675
- color: $color-text-muted;
676
- font-size: $text-xs;
677
- cursor: pointer;
678
- padding: 0.25rem;
679
- border-radius: $radius-sm;
680
- opacity: 0;
681
- transition: opacity $transition-fast, color $transition-fast;
682
- flex-shrink: 0;
683
-
684
- .item:hover &,
685
- &:focus-visible { opacity: 1; }
686
- &:hover { color: $color-text-primary; }
687
- &:focus-visible {
688
- outline: 2px solid $evo-primary-focus;
689
- outline-offset: 1px;
690
- }
691
- }
1
+ @use 'base/variables' as *;
2
+ @use 'base/color' as *;
3
+
4
+ // ============================================================
5
+ // EvoNotification — toast + inbox stylesheet
6
+ // ------------------------------------------------------------
7
+ // Two visual subsystems share this file:
8
+ // 1. Toast (transient, portaled to body)
9
+ // 2. Bell + Panel + Item (inbox center)
10
+ // All colour comes from --evo-color-* tokens so dark mode is
11
+ // automatic. Six anchors and a strict mobile breakpoint below.
12
+ // ============================================================
13
+
14
+ // ---------- Animations ----------
15
+
16
+ @keyframes evoToastEnterRight {
17
+ from { transform: translateX(110%); opacity: 0; }
18
+ to { transform: translateX(0); opacity: 1; }
19
+ }
20
+
21
+ @keyframes evoToastEnterLeft {
22
+ from { transform: translateX(-110%); opacity: 0; }
23
+ to { transform: translateX(0); opacity: 1; }
24
+ }
25
+
26
+ @keyframes evoToastEnterTop {
27
+ from { transform: translateY(-110%); opacity: 0; }
28
+ to { transform: translateY(0); opacity: 1; }
29
+ }
30
+
31
+ @keyframes evoToastEnterBottom {
32
+ from { transform: translateY(110%); opacity: 0; }
33
+ to { transform: translateY(0); opacity: 1; }
34
+ }
35
+
36
+ @keyframes evoToastExit {
37
+ from { opacity: 1; transform: scale(1); }
38
+ to { opacity: 0; transform: scale(0.92); }
39
+ }
40
+
41
+ @keyframes evoPanelEnter {
42
+ from { opacity: 0; transform: translateY(-6px) scale(0.98); }
43
+ to { opacity: 1; transform: translateY(0) scale(1); }
44
+ }
45
+
46
+ // ---------- Toaster (portal root) ----------
47
+
48
+ .toasterRoot {
49
+ position: fixed;
50
+ inset: 0;
51
+ pointer-events: none;
52
+ z-index: 10000;
53
+ }
54
+
55
+ // Visually-hidden screen-reader live region. Always present in the DOM so
56
+ // announcements fire reliably when its text content changes.
57
+ .srOnly {
58
+ position: absolute;
59
+ width: 1px;
60
+ height: 1px;
61
+ margin: -1px;
62
+ padding: 0;
63
+ border: 0;
64
+ overflow: hidden;
65
+ clip: rect(0, 0, 0, 0);
66
+ white-space: nowrap;
67
+ }
68
+
69
+ .anchor {
70
+ position: absolute;
71
+ display: flex;
72
+ flex-direction: column;
73
+ gap: 0.5rem;
74
+ padding: 1rem;
75
+ max-width: min(420px, calc(100vw - 2rem));
76
+ pointer-events: none;
77
+ }
78
+
79
+ .anchor > * {
80
+ pointer-events: auto;
81
+ }
82
+
83
+ // Stacking direction. The newest toast is always rendered last in the DOM and
84
+ // must sit flush against the anchored screen edge — top anchors grow downward
85
+ // from the top, bottom anchors grow upward from the bottom. So top anchors
86
+ // reverse the column (DOM-last renders visually first/topmost) while bottom
87
+ // anchors keep the natural column (DOM-last renders visually last/bottom-most).
88
+ // The depth offset in ToastRow then pushes older cards *away* from that edge.
89
+ .anchor-top-left { top: 0; left: 0; align-items: flex-start; flex-direction: column-reverse; }
90
+ .anchor-top-center { top: 0; left: 50%; transform: translateX(-50%); align-items: center; flex-direction: column-reverse; }
91
+ .anchor-top-right { top: 0; right: 0; align-items: flex-end; flex-direction: column-reverse; }
92
+ .anchor-bottom-left { bottom: 0; left: 0; align-items: flex-start; }
93
+ .anchor-bottom-center {
94
+ bottom: 0; left: 50%; transform: translateX(-50%);
95
+ align-items: center;
96
+ }
97
+ .anchor-bottom-right {
98
+ bottom: 0; right: 0; align-items: flex-end;
99
+ }
100
+
101
+ // Mobile: pin to top or bottom edge, full-width.
102
+ @media (max-width: 23.4375rem) { // 375px
103
+ .anchor {
104
+ left: 0 !important;
105
+ right: 0 !important;
106
+ transform: none !important;
107
+ max-width: 100%;
108
+ padding: 0.5rem;
109
+ align-items: stretch;
110
+ }
111
+ }
112
+
113
+ .overflowPill {
114
+ font-size: $text-xs;
115
+ font-weight: 600;
116
+ color: $color-text-secondary;
117
+ background-color: $color-surface-elevated;
118
+ border: 1px solid $color-border;
119
+ border-radius: $radius-full;
120
+ padding: 0.25rem 0.625rem;
121
+ box-shadow: $shadow-sm;
122
+ }
123
+
124
+ // ---------- Single toast card ----------
125
+
126
+ .toast {
127
+ position: relative;
128
+ display: flex;
129
+ align-items: flex-start;
130
+ gap: 0.625rem;
131
+ padding: 0.75rem 0.875rem;
132
+ min-width: 280px;
133
+ max-width: 100%;
134
+ background-color: $color-surface-elevated;
135
+ color: $color-text-primary;
136
+ border: 1px solid $color-border;
137
+ border-radius: $radius-md;
138
+ box-shadow: $shadow-xl;
139
+ font-family: $font-sans;
140
+ font-size: $text-sm;
141
+ line-height: 1.45;
142
+ // Left colour bar (severity)
143
+ border-left-width: 3px;
144
+ transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1),
145
+ opacity 220ms ease;
146
+ will-change: transform, opacity;
147
+
148
+ // Width parity on mobile.
149
+ @media (max-width: 23.4375rem) {
150
+ min-width: 0;
151
+ width: 100%;
152
+ }
153
+ }
154
+
155
+ // Anchor-aware enter animations applied via parent.
156
+ .anchor-top-right > .toast { animation: evoToastEnterRight 240ms cubic-bezier(0.16, 1, 0.3, 1); }
157
+ .anchor-bottom-right > .toast { animation: evoToastEnterRight 240ms cubic-bezier(0.16, 1, 0.3, 1); }
158
+ .anchor-top-left > .toast { animation: evoToastEnterLeft 240ms cubic-bezier(0.16, 1, 0.3, 1); }
159
+ .anchor-bottom-left > .toast { animation: evoToastEnterLeft 240ms cubic-bezier(0.16, 1, 0.3, 1); }
160
+ .anchor-top-center > .toast { animation: evoToastEnterTop 240ms cubic-bezier(0.16, 1, 0.3, 1); }
161
+ .anchor-bottom-center > .toast { animation: evoToastEnterBottom 240ms cubic-bezier(0.16, 1, 0.3, 1); }
162
+
163
+ .toast.exiting {
164
+ animation: evoToastExit 180ms ease forwards;
165
+ }
166
+
167
+ .toast.noMotion,
168
+ .toast.noMotion.exiting {
169
+ animation: none !important;
170
+ transition: opacity 1ms linear !important;
171
+ }
172
+
173
+ @media (prefers-reduced-motion: reduce) {
174
+ .toast,
175
+ .toast.exiting {
176
+ animation: none !important;
177
+ transition: opacity 1ms linear !important;
178
+ }
179
+ }
180
+
181
+ .sev-success {
182
+ border-left-color: $evo-success-color;
183
+ .toastIcon { background-color: $evo-success-color; color: $evo-success-fg; }
184
+ }
185
+ .sev-error {
186
+ border-left-color: $evo-danger-color;
187
+ .toastIcon { background-color: $evo-danger-color; color: $evo-danger-fg; }
188
+ }
189
+ .sev-warning {
190
+ border-left-color: $evo-warning-color;
191
+ .toastIcon { background-color: $evo-warning-color; color: $evo-warning-text; }
192
+ }
193
+ .sev-info {
194
+ border-left-color: $evo-info-color;
195
+ .toastIcon { background-color: $evo-info-color; color: $evo-info-fg; }
196
+ }
197
+
198
+ .toastIcon {
199
+ display: inline-flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+ width: 1.25rem;
203
+ height: 1.25rem;
204
+ border-radius: 50%;
205
+ font-size: $text-xs;
206
+ font-weight: 700;
207
+ flex-shrink: 0;
208
+ margin-top: 0.0625rem;
209
+ }
210
+
211
+ .toastBody {
212
+ flex: 1;
213
+ min-width: 0;
214
+ }
215
+
216
+ .toastTitle {
217
+ font-weight: 600;
218
+ color: $color-text-primary;
219
+ }
220
+
221
+ .toastDescription {
222
+ margin-top: 0.1875rem;
223
+ color: $color-text-secondary;
224
+ font-size: $text-sm;
225
+ }
226
+
227
+ // Count badge for coalesced toasts (groupKey) — "×3" beside the title.
228
+ .toastCount {
229
+ display: inline-flex;
230
+ align-items: center;
231
+ justify-content: center;
232
+ min-width: 1.25rem;
233
+ height: 1.125rem;
234
+ margin-left: 0.375rem;
235
+ padding: 0 0.3125rem;
236
+ font-size: $text-xs;
237
+ font-weight: 700;
238
+ font-variant-numeric: tabular-nums;
239
+ color: $color-text-secondary;
240
+ background-color: $color-surface-sunken;
241
+ border: 1px solid $color-border;
242
+ border-radius: $radius-full;
243
+ vertical-align: middle;
244
+ }
245
+
246
+ // Determinate progress bar (toast.progress / EvoToastOptions.progress).
247
+ // Pinned along the bottom edge of the card.
248
+ .toastProgressTrack {
249
+ position: absolute;
250
+ left: 0;
251
+ right: 0;
252
+ bottom: 0;
253
+ height: 3px;
254
+ background-color: $color-border-subtle;
255
+ border-bottom-left-radius: $radius-md;
256
+ border-bottom-right-radius: $radius-md;
257
+ overflow: hidden;
258
+ }
259
+
260
+ .toastProgressFill {
261
+ height: 100%;
262
+ width: 100%;
263
+ transform-origin: left center;
264
+ background-color: $evo-primary-color;
265
+ transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1);
266
+ }
267
+
268
+ // Fill tracks the toast severity so a resolved progress toast reads green.
269
+ .sev-success .toastProgressFill { background-color: $evo-success-color; }
270
+ .sev-error .toastProgressFill { background-color: $evo-danger-color; }
271
+ .sev-warning .toastProgressFill { background-color: $evo-warning-color; }
272
+ .sev-info .toastProgressFill { background-color: $evo-info-color; }
273
+
274
+ .toast.noMotion .toastProgressFill { transition: none; }
275
+
276
+ @media (prefers-reduced-motion: reduce) {
277
+ .toastProgressFill { transition: none; }
278
+ }
279
+
280
+ .toastAction {
281
+ align-self: center;
282
+ font: inherit;
283
+ font-weight: 600;
284
+ font-size: $text-xs;
285
+ color: $evo-primary-color;
286
+ background: transparent;
287
+ border: 1px solid $color-border;
288
+ border-radius: $radius-sm;
289
+ padding: 0.25rem 0.5rem;
290
+ cursor: pointer;
291
+ flex-shrink: 0;
292
+ min-height: 1.75rem;
293
+ transition: background-color $transition-fast, border-color $transition-fast;
294
+
295
+ &:hover { background-color: $color-surface-hover; }
296
+ &:focus-visible {
297
+ outline: 2px solid $evo-primary-focus;
298
+ outline-offset: 1px;
299
+ }
300
+ }
301
+
302
+ .toastClose {
303
+ align-self: flex-start;
304
+ background: transparent;
305
+ border: none;
306
+ color: $color-text-muted;
307
+ font-size: $text-xs;
308
+ cursor: pointer;
309
+ padding: 0.25rem;
310
+ border-radius: $radius-sm;
311
+ flex-shrink: 0;
312
+ transition: color $transition-fast, background-color $transition-fast;
313
+
314
+ &:hover { color: $color-text-primary; background-color: $color-surface-hover; }
315
+ &:focus-visible {
316
+ outline: 2px solid $evo-primary-focus;
317
+ outline-offset: 1px;
318
+ }
319
+ }
320
+
321
+ // ---------- Bell ----------
322
+
323
+ .bellWrapper {
324
+ position: relative;
325
+ display: inline-block;
326
+ }
327
+
328
+ .bell {
329
+ position: relative;
330
+ display: inline-flex;
331
+ align-items: center;
332
+ justify-content: center;
333
+ background: transparent;
334
+ border: 1px solid transparent;
335
+ border-radius: $radius-full;
336
+ color: $color-text-primary;
337
+ cursor: pointer;
338
+ font: inherit;
339
+ transition: background-color $transition-fast, color $transition-fast, border-color $transition-fast;
340
+
341
+ &:hover { background-color: $color-surface-hover; }
342
+ &:focus-visible {
343
+ outline: 2px solid $evo-primary-focus;
344
+ outline-offset: 2px;
345
+ }
346
+ }
347
+
348
+ .bell-ghost { background-color: transparent; }
349
+ .bell-solid {
350
+ background-color: $color-surface-elevated;
351
+ border-color: $color-border;
352
+ }
353
+
354
+ .bell-sm { width: 2rem; height: 2rem; }
355
+ .bell-md { width: 2.75rem; height: 2.75rem; } // 44px — WCAG touch target
356
+ .bell-lg { width: 3rem; height: 3rem; }
357
+
358
+ .bellOpen {
359
+ background-color: $color-surface-hover;
360
+ }
361
+
362
+ .bellBadge {
363
+ position: absolute;
364
+ top: 0.125rem;
365
+ right: 0.125rem;
366
+ min-width: 1rem;
367
+ height: 1rem;
368
+ padding: 0 0.25rem;
369
+ background-color: $evo-danger-color;
370
+ color: $evo-danger-fg;
371
+ font-size: 0.625rem;
372
+ font-weight: 700;
373
+ border-radius: $radius-full;
374
+ display: inline-flex;
375
+ align-items: center;
376
+ justify-content: center;
377
+ border: 2px solid $color-surface;
378
+ line-height: 1;
379
+ }
380
+
381
+ .bellBadgeZero {
382
+ background-color: $color-border-strong;
383
+ color: $color-text-inverted;
384
+ }
385
+
386
+ // ---------- Panel placement (anchored to Bell) ----------
387
+
388
+ .bellPanelHost {
389
+ position: absolute;
390
+ z-index: 9001;
391
+ min-width: 22rem;
392
+ max-width: 24rem;
393
+
394
+ @media (max-width: 30rem) {
395
+ position: fixed;
396
+ top: auto !important;
397
+ bottom: auto !important;
398
+ left: 0.5rem !important;
399
+ right: 0.5rem !important;
400
+ min-width: 0;
401
+ max-width: none;
402
+ }
403
+ }
404
+
405
+ .place-bottom-end { top: calc(100% + 0.5rem); right: 0; }
406
+ .place-bottom-start { top: calc(100% + 0.5rem); left: 0; }
407
+ .place-bottom { top: calc(100% + 0.5rem); left: 50%; transform: translateX(-50%); }
408
+ .place-top-end { bottom: calc(100% + 0.5rem); right: 0; }
409
+ .place-top-start { bottom: calc(100% + 0.5rem); left: 0; }
410
+
411
+ // ---------- Panel ----------
412
+
413
+ .panel {
414
+ background-color: $color-surface-elevated;
415
+ border: 1px solid $color-border;
416
+ border-radius: $radius-lg;
417
+ box-shadow: $shadow-xl;
418
+ display: flex;
419
+ flex-direction: column;
420
+ overflow: hidden;
421
+ font-family: $font-sans;
422
+ color: $color-text-primary;
423
+ animation: evoPanelEnter 160ms ease;
424
+
425
+ @media (prefers-reduced-motion: reduce) {
426
+ animation: none;
427
+ }
428
+ }
429
+
430
+ .panelHeader {
431
+ display: flex;
432
+ align-items: center;
433
+ justify-content: space-between;
434
+ padding: 0.75rem 1rem;
435
+ border-bottom: 1px solid $color-border;
436
+ gap: 0.5rem;
437
+ }
438
+
439
+ .panelTitle {
440
+ font-weight: 600;
441
+ font-size: $text-sm;
442
+ }
443
+
444
+ .panelHeaderActions {
445
+ display: inline-flex;
446
+ align-items: center;
447
+ gap: 0.25rem;
448
+ }
449
+
450
+ .panelMarkAll {
451
+ background: transparent;
452
+ border: none;
453
+ cursor: pointer;
454
+ font: inherit;
455
+ font-size: $text-xs;
456
+ font-weight: 600;
457
+ color: $evo-primary-color;
458
+ padding: 0.25rem 0.5rem;
459
+ border-radius: $radius-sm;
460
+ min-height: 1.75rem;
461
+
462
+ &:hover { background-color: $color-surface-hover; }
463
+ &:focus-visible {
464
+ outline: 2px solid $evo-primary-focus;
465
+ outline-offset: 1px;
466
+ }
467
+ }
468
+
469
+ .panelClose {
470
+ background: transparent;
471
+ border: none;
472
+ cursor: pointer;
473
+ color: $color-text-muted;
474
+ padding: 0.25rem;
475
+ font-size: $text-xs;
476
+ border-radius: $radius-sm;
477
+ min-height: 1.75rem;
478
+ min-width: 1.75rem;
479
+ display: inline-flex;
480
+ align-items: center;
481
+ justify-content: center;
482
+
483
+ &:hover { color: $color-text-primary; background-color: $color-surface-hover; }
484
+ }
485
+
486
+ .panelBody {
487
+ overflow-y: auto;
488
+ }
489
+
490
+ .panelState {
491
+ display: flex;
492
+ align-items: center;
493
+ justify-content: center;
494
+ padding: 2rem 1.5rem;
495
+ color: $color-text-secondary;
496
+ font-size: $text-sm;
497
+ text-align: center;
498
+ }
499
+
500
+ .panelStateError {
501
+ color: $evo-danger-color;
502
+ }
503
+
504
+ .itemList {
505
+ list-style: none;
506
+ margin: 0;
507
+ padding: 0;
508
+
509
+ li {
510
+ border-bottom: 1px solid $color-border-subtle;
511
+
512
+ &:last-child { border-bottom: none; }
513
+ }
514
+ }
515
+
516
+ // ---------- Empty state ----------
517
+
518
+ .emptyState {
519
+ display: flex;
520
+ flex-direction: column;
521
+ align-items: center;
522
+ gap: 0.5rem;
523
+ padding: 2rem 1rem;
524
+ text-align: center;
525
+ }
526
+
527
+ .emptyIcon {
528
+ display: inline-flex;
529
+ align-items: center;
530
+ justify-content: center;
531
+ width: 2.5rem;
532
+ height: 2.5rem;
533
+ border-radius: 50%;
534
+ background-color: $color-surface-sunken;
535
+ color: $color-text-muted;
536
+ }
537
+
538
+ .emptyTitle {
539
+ font-weight: 600;
540
+ font-size: $text-sm;
541
+ color: $color-text-primary;
542
+ }
543
+
544
+ .emptyHint {
545
+ font-size: $text-xs;
546
+ color: $color-text-secondary;
547
+ }
548
+
549
+ // ---------- Item ----------
550
+
551
+ .item {
552
+ position: relative;
553
+ display: flex;
554
+ align-items: flex-start;
555
+ gap: 0.625rem;
556
+ padding: 0.75rem 1rem 0.75rem 1.25rem;
557
+ background-color: transparent;
558
+ transition: background-color $transition-fast;
559
+
560
+ &.itemUnread { background-color: color-mix(in srgb, $evo-primary-color 6%, transparent); }
561
+ }
562
+
563
+ .itemInteractive {
564
+ cursor: pointer;
565
+
566
+ &:hover { background-color: $color-surface-hover; }
567
+ &:focus-visible {
568
+ outline: 2px solid $evo-primary-focus;
569
+ outline-offset: -2px;
570
+ }
571
+ }
572
+
573
+ .itemUnreadDot {
574
+ position: absolute;
575
+ left: 0.5rem;
576
+ top: 1rem;
577
+ width: 0.4375rem;
578
+ height: 0.4375rem;
579
+ border-radius: 50%;
580
+ background-color: $evo-primary-color;
581
+ opacity: 1;
582
+
583
+ // Hidden when read (parent doesn't get .itemUnread).
584
+ .item:not(.itemUnread) & { opacity: 0; }
585
+ }
586
+
587
+ .itemMedia {
588
+ width: 2rem;
589
+ height: 2rem;
590
+ border-radius: 50%;
591
+ display: inline-flex;
592
+ align-items: center;
593
+ justify-content: center;
594
+ color: $color-text-inverted;
595
+ font-size: $text-xs;
596
+ font-weight: 700;
597
+ flex-shrink: 0;
598
+ overflow: hidden;
599
+
600
+ &.sev-success { background-color: $evo-success-color; }
601
+ &.sev-error { background-color: $evo-danger-color; }
602
+ &.sev-warning { background-color: $evo-warning-color; }
603
+ &.sev-info { background-color: $evo-info-color; }
604
+ }
605
+
606
+ .itemAvatar {
607
+ width: 100%;
608
+ height: 100%;
609
+ object-fit: cover;
610
+ }
611
+
612
+ .itemMediaGlyph {
613
+ line-height: 1;
614
+ }
615
+
616
+ .itemBody {
617
+ flex: 1;
618
+ min-width: 0;
619
+ }
620
+
621
+ .itemTitle {
622
+ font-size: $text-sm;
623
+ font-weight: 600;
624
+ color: $color-text-primary;
625
+ line-height: 1.35;
626
+ }
627
+
628
+ .itemDescription {
629
+ margin-top: 0.125rem;
630
+ font-size: $text-sm;
631
+ color: $color-text-secondary;
632
+ line-height: 1.4;
633
+ display: -webkit-box;
634
+ -webkit-line-clamp: 2;
635
+ -webkit-box-orient: vertical;
636
+ overflow: hidden;
637
+ }
638
+
639
+ .itemMeta {
640
+ margin-top: 0.375rem;
641
+ display: flex;
642
+ align-items: center;
643
+ gap: 0.5rem;
644
+ font-size: $text-xs;
645
+ color: $color-text-muted;
646
+ }
647
+
648
+ .itemTimestamp {
649
+ font-variant-numeric: tabular-nums;
650
+ }
651
+
652
+ .itemAction {
653
+ background: transparent;
654
+ border: 1px solid $color-border;
655
+ color: $evo-primary-color;
656
+ border-radius: $radius-sm;
657
+ cursor: pointer;
658
+ font: inherit;
659
+ font-size: $text-xs;
660
+ font-weight: 600;
661
+ padding: 0.1875rem 0.5rem;
662
+ min-height: 1.5rem;
663
+
664
+ &:hover { background-color: $color-surface-hover; }
665
+ &:focus-visible {
666
+ outline: 2px solid $evo-primary-focus;
667
+ outline-offset: 1px;
668
+ }
669
+ }
670
+
671
+ .itemDismiss {
672
+ align-self: flex-start;
673
+ background: transparent;
674
+ border: none;
675
+ color: $color-text-muted;
676
+ font-size: $text-xs;
677
+ cursor: pointer;
678
+ padding: 0.25rem;
679
+ border-radius: $radius-sm;
680
+ opacity: 0;
681
+ transition: opacity $transition-fast, color $transition-fast;
682
+ flex-shrink: 0;
683
+
684
+ .item:hover &,
685
+ &:focus-visible { opacity: 1; }
686
+ &:hover { color: $color-text-primary; }
687
+ &:focus-visible {
688
+ outline: 2px solid $evo-primary-focus;
689
+ outline-offset: 1px;
690
+ }
691
+ }