@justin_evo/evo-ui 1.0.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 (110) hide show
  1. package/dist/Alert/Alert.d.ts +11 -0
  2. package/dist/AutoComplete/AutoComplete.d.ts +95 -0
  3. package/dist/Badge/Badge.d.ts +23 -0
  4. package/dist/Breadcrumb/Breadcrumb.d.ts +16 -0
  5. package/dist/Button/Button.d.ts +54 -0
  6. package/dist/Card/Card.d.ts +60 -0
  7. package/dist/Checkbox/Checkbox.d.ts +16 -0
  8. package/dist/CommandPalette/CommandPalette.d.ts +17 -0
  9. package/dist/Container/Container.d.ts +10 -0
  10. package/dist/Divider/Divider.d.ts +7 -0
  11. package/dist/Form/Form.d.ts +61 -0
  12. package/dist/Grid/Grid.d.ts +23 -0
  13. package/dist/ImageCropper/ImageCropper.d.ts +111 -0
  14. package/dist/Input/Input.d.ts +12 -0
  15. package/dist/Modal/Modal.d.ts +26 -0
  16. package/dist/Nav/Nav.d.ts +63 -0
  17. package/dist/Notification/Notification.d.ts +186 -0
  18. package/dist/Pagination/Pagination.d.ts +10 -0
  19. package/dist/Radio/Radio.d.ts +20 -0
  20. package/dist/RichTextArea/RichTextArea.d.ts +70 -0
  21. package/dist/Select/Select.d.ts +44 -0
  22. package/dist/Skeleton/Skeleton.d.ts +23 -0
  23. package/dist/Stack/Stack.d.ts +16 -0
  24. package/dist/Table/Table.d.ts +77 -0
  25. package/dist/Tabs/Tabs.d.ts +28 -0
  26. package/dist/Theme/ThemeProvider.d.ts +96 -0
  27. package/dist/Theme/ThemeToggle.d.ts +22 -0
  28. package/dist/Toggle/Toggle.d.ts +11 -0
  29. package/dist/Tooltip/Tooltip.d.ts +10 -0
  30. package/dist/TopNav/TopNav.d.ts +76 -0
  31. package/dist/TreeSelect/TreeSelect.d.ts +50 -0
  32. package/dist/declarations.d.ts +6 -0
  33. package/dist/evo-ui.css +1 -0
  34. package/dist/index.cjs.js +1 -0
  35. package/dist/index.d.ts +31 -0
  36. package/dist/index.es.js +5688 -0
  37. package/package.json +52 -0
  38. package/src/Alert/Alert.tsx +49 -0
  39. package/src/AutoComplete/AutoComplete.tsx +810 -0
  40. package/src/Badge/Badge.tsx +53 -0
  41. package/src/Breadcrumb/Breadcrumb.tsx +53 -0
  42. package/src/Button/Button.tsx +125 -0
  43. package/src/Card/Card.tsx +257 -0
  44. package/src/Checkbox/Checkbox.tsx +59 -0
  45. package/src/CommandPalette/CommandPalette.tsx +185 -0
  46. package/src/Container/Container.tsx +31 -0
  47. package/src/Divider/Divider.tsx +31 -0
  48. package/src/Form/Form.tsx +185 -0
  49. package/src/Grid/Grid.tsx +66 -0
  50. package/src/ImageCropper/ImageCropper.tsx +911 -0
  51. package/src/Input/Input.tsx +74 -0
  52. package/src/Modal/Modal.tsx +77 -0
  53. package/src/Nav/Nav.tsx +626 -0
  54. package/src/Notification/Notification.tsx +1503 -0
  55. package/src/Pagination/Pagination.tsx +76 -0
  56. package/src/Radio/Radio.tsx +69 -0
  57. package/src/RichTextArea/RichTextArea.tsx +869 -0
  58. package/src/Select/Select.tsx +515 -0
  59. package/src/Skeleton/Skeleton.tsx +70 -0
  60. package/src/Stack/Stack.tsx +52 -0
  61. package/src/Table/Table.tsx +335 -0
  62. package/src/Tabs/Tabs.tsx +90 -0
  63. package/src/Theme/ThemeProvider.tsx +253 -0
  64. package/src/Theme/ThemeToggle.tsx +79 -0
  65. package/src/Toggle/Toggle.tsx +48 -0
  66. package/src/Tooltip/Tooltip.tsx +38 -0
  67. package/src/TopNav/TopNav.tsx +994 -0
  68. package/src/TreeSelect/TreeSelect.tsx +825 -0
  69. package/src/css/alert.module.scss +93 -0
  70. package/src/css/autocomplete.module.scss +416 -0
  71. package/src/css/badge.module.scss +82 -0
  72. package/src/css/base/_color.scss +159 -0
  73. package/src/css/base/_theme.scss +237 -0
  74. package/src/css/base/_variables.scss +161 -0
  75. package/src/css/breadcrumb.module.scss +50 -0
  76. package/src/css/button.module.scss +385 -0
  77. package/src/css/card.module.scss +217 -0
  78. package/src/css/checkbox.module.scss +120 -0
  79. package/src/css/commandpalette.module.scss +211 -0
  80. package/src/css/container.module.scss +18 -0
  81. package/src/css/divider.module.scss +41 -0
  82. package/src/css/form.module.scss +245 -0
  83. package/src/css/imagecropper.module.scss +397 -0
  84. package/src/css/input.module.scss +89 -0
  85. package/src/css/modal.module.scss +105 -0
  86. package/src/css/nav.module.scss +339 -0
  87. package/src/css/notification.module.scss +691 -0
  88. package/src/css/pagination.module.scss +63 -0
  89. package/src/css/radio.module.scss +89 -0
  90. package/src/css/richtextarea.module.scss +307 -0
  91. package/src/css/select.module.scss +525 -0
  92. package/src/css/skeleton.module.scss +30 -0
  93. package/src/css/table.module.scss +386 -0
  94. package/src/css/tabs.module.scss +63 -0
  95. package/src/css/theme-toggle.module.scss +83 -0
  96. package/src/css/toggle.module.scss +54 -0
  97. package/src/css/tooltip.module.scss +97 -0
  98. package/src/css/topnav.module.scss +396 -0
  99. package/src/css/treeselect.module.scss +558 -0
  100. package/src/css/utilities/_borders.scss +111 -0
  101. package/src/css/utilities/_colors.scss +66 -0
  102. package/src/css/utilities/_effects.scss +216 -0
  103. package/src/css/utilities/_layout.scss +181 -0
  104. package/src/css/utilities/_position.scss +75 -0
  105. package/src/css/utilities/_sizing.scss +138 -0
  106. package/src/css/utilities/_spacing.scss +99 -0
  107. package/src/css/utilities/_typography.scss +121 -0
  108. package/src/css/utilities/index.scss +24 -0
  109. package/src/declarations.d.ts +6 -0
  110. package/src/index.ts +60 -0
@@ -0,0 +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
+ }