@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,385 @@
1
+ @use 'base/variables' as *;
2
+ @use 'base/color' as *;
3
+
4
+ // ==========================================================
5
+ // EvoButton
6
+ // ----------------------------------------------------------
7
+ // Layout is built around three orthogonal axes:
8
+ // 1. variant — solid / outline / ghost / soft
9
+ // 2. severity — primary / secondary / danger / warning / success / info
10
+ // 3. shape — default / rounded / square
11
+ // Plus modifiers: size (sm/md/lg), fullWidth, iconOnly, loading.
12
+ //
13
+ // All sizing uses `rem` + `min-height` so the component stays
14
+ // usable on touch screens (44px target for `lg`) without needing
15
+ // explicit mobile media queries.
16
+ // ==========================================================
17
+
18
+ // --- Base button ---
19
+ .button {
20
+ display: inline-flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ gap: 0.4rem;
24
+ padding: $evo-btn-padding-y $evo-btn-padding-x;
25
+ min-height: 2.25rem;
26
+ max-width: 100%;
27
+ font-family: inherit;
28
+ font-size: $evo-btn-font-size;
29
+ font-weight: $evo-btn-font-weight;
30
+ line-height: $evo-btn-line-height;
31
+ white-space: nowrap;
32
+ vertical-align: middle;
33
+ border: 2px solid transparent;
34
+ border-radius: $evo-border-radius;
35
+ background-color: transparent;
36
+ color: inherit;
37
+ cursor: pointer;
38
+ user-select: none;
39
+ text-decoration: none;
40
+ appearance: none;
41
+ -webkit-tap-highlight-color: transparent;
42
+ touch-action: manipulation;
43
+ transition: background-color $evo-btn-transition,
44
+ border-color $evo-btn-transition,
45
+ box-shadow $evo-btn-transition,
46
+ color $evo-btn-transition;
47
+
48
+ &:focus-visible {
49
+ outline-width: $evo-btn-outline-width;
50
+ outline-style: solid;
51
+ outline-offset: $evo-btn-outline-offset;
52
+ }
53
+
54
+ &:disabled {
55
+ opacity: $evo-btn-disabled-opacity;
56
+ cursor: not-allowed;
57
+ pointer-events: none;
58
+ }
59
+ }
60
+
61
+ // --- Slots ---
62
+ .label {
63
+ min-width: 0;
64
+ overflow: hidden;
65
+ text-overflow: ellipsis;
66
+ }
67
+
68
+ .icon {
69
+ display: inline-flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ flex-shrink: 0;
73
+ line-height: 1;
74
+
75
+ // Normalize SVGs so consumers don't have to size them
76
+ > svg {
77
+ width: 1em;
78
+ height: 1em;
79
+ }
80
+ }
81
+
82
+ // --- Loading spinner (CSS-only, inherits currentColor) ---
83
+ .spinner {
84
+ display: inline-block;
85
+ width: 1em;
86
+ height: 1em;
87
+ flex-shrink: 0;
88
+ border: 2px solid currentColor;
89
+ border-right-color: transparent;
90
+ border-radius: 50%;
91
+ animation: evo-btn-spin 0.6s linear infinite;
92
+ }
93
+
94
+ @keyframes evo-btn-spin {
95
+ to { transform: rotate(360deg); }
96
+ }
97
+
98
+ // --- Size modifiers ---
99
+ .sm {
100
+ gap: 0.3rem;
101
+ padding: $evo-btn-padding-y-sm $evo-btn-padding-x-sm;
102
+ min-height: 1.75rem;
103
+ font-size: $evo-btn-font-size-sm;
104
+ border-radius: $evo-border-radius-sm;
105
+ }
106
+
107
+ .md { /* default */ }
108
+
109
+ .lg {
110
+ gap: 0.5rem;
111
+ padding: $evo-btn-padding-y-lg $evo-btn-padding-x-lg;
112
+ min-height: 2.75rem;
113
+ font-size: $evo-btn-font-size-lg;
114
+ border-radius: $evo-border-radius-lg;
115
+ }
116
+
117
+ // --- Shape modifiers (orthogonal to variant) ---
118
+ .rounded {
119
+ border-radius: $evo-border-radius-pill;
120
+ }
121
+
122
+ .square {
123
+ padding: 0;
124
+ aspect-ratio: 1 / 1;
125
+ min-width: 2.25rem;
126
+
127
+ &.sm { min-width: 1.75rem; }
128
+ &.lg { min-width: 2.75rem; }
129
+ }
130
+
131
+ // --- Icon-only (auto-applied when there's no label/children) ---
132
+ .iconOnly {
133
+ padding-left: 0.5rem;
134
+ padding-right: 0.5rem;
135
+
136
+ &.sm { padding-left: 0.35rem; padding-right: 0.35rem; }
137
+ &.lg { padding-left: 0.65rem; padding-right: 0.65rem; }
138
+ }
139
+
140
+ // --- Layout modifiers ---
141
+ .fullWidth {
142
+ display: flex;
143
+ width: 100%;
144
+ }
145
+
146
+ // --- Variant base looks ---
147
+ .solid { /* color set per-severity */ }
148
+ .outline { background-color: transparent; }
149
+ .ghost { background-color: transparent; border-color: transparent; }
150
+ .soft { border-color: transparent; }
151
+
152
+ // --- Severities ---
153
+ .primary {
154
+ &.solid {
155
+ color: $evo-primary-fg;
156
+ background-color: $evo-primary-color;
157
+ border-color: $evo-primary-color;
158
+
159
+ &:hover:not(:disabled) { background-color: $evo-primary-hover; border-color: $evo-primary-hover; }
160
+ &:active:not(:disabled) { background-color: $evo-primary-active; border-color: $evo-primary-active; }
161
+ &:focus-visible { outline-color: $evo-primary-focus; }
162
+ }
163
+
164
+ &.outline {
165
+ color: $evo-primary-color;
166
+ border-color: $evo-primary-color;
167
+
168
+ &:hover:not(:disabled) { color: $evo-primary-fg; background-color: $evo-primary-color; }
169
+ &:active:not(:disabled) { color: $evo-primary-fg; background-color: $evo-primary-hover; border-color: $evo-primary-hover; }
170
+ &:focus-visible { outline-color: $evo-primary-focus; }
171
+ }
172
+
173
+ &.ghost {
174
+ color: $evo-primary-color;
175
+
176
+ &:hover:not(:disabled) { background-color: $evo-primary-soft; }
177
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-primary-color 22%, transparent); }
178
+ &:focus-visible { outline-color: $evo-primary-focus; }
179
+ }
180
+
181
+ &.soft {
182
+ color: $evo-primary-color;
183
+ background-color: $evo-primary-soft;
184
+
185
+ &:hover:not(:disabled) { background-color: color-mix(in srgb, $evo-primary-color 22%, transparent); }
186
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-primary-color 30%, transparent); }
187
+ &:focus-visible { outline-color: $evo-primary-focus; }
188
+ }
189
+ }
190
+
191
+ .secondary {
192
+ &.solid {
193
+ color: $evo-secondary-text;
194
+ background-color: $evo-secondary-bg;
195
+ border-color: $evo-secondary-border;
196
+
197
+ &:hover:not(:disabled) { background-color: $evo-secondary-hover; border-color: $color-border-strong; color: $color-text-primary; }
198
+ &:active:not(:disabled) { background-color: $evo-secondary-active; border-color: $color-border-strong; }
199
+ &:focus-visible { outline-color: $evo-secondary-focus; }
200
+ }
201
+
202
+ &.outline {
203
+ color: $evo-secondary-text;
204
+ border-color: $evo-secondary-border;
205
+
206
+ &:hover:not(:disabled) { background-color: $evo-secondary-bg; border-color: $color-border-strong; color: $color-text-primary; }
207
+ &:active:not(:disabled) { background-color: $evo-secondary-hover; border-color: $color-border-strong; }
208
+ &:focus-visible { outline-color: $evo-secondary-focus; }
209
+ }
210
+
211
+ &.ghost {
212
+ color: $evo-secondary-text;
213
+
214
+ &:hover:not(:disabled) { background-color: $evo-secondary-bg; color: $color-text-primary; }
215
+ &:active:not(:disabled) { background-color: $evo-secondary-hover; }
216
+ &:focus-visible { outline-color: $evo-secondary-focus; }
217
+ }
218
+
219
+ &.soft {
220
+ color: $color-text-primary;
221
+ background-color: $evo-secondary-bg;
222
+
223
+ &:hover:not(:disabled) { background-color: $evo-secondary-hover; }
224
+ &:active:not(:disabled) { background-color: $evo-secondary-active; }
225
+ &:focus-visible { outline-color: $evo-secondary-focus; }
226
+ }
227
+ }
228
+
229
+ .danger {
230
+ &.solid {
231
+ color: $evo-danger-fg;
232
+ background-color: $evo-danger-color;
233
+ border-color: $evo-danger-color;
234
+
235
+ &:hover:not(:disabled) { background-color: $evo-danger-hover; border-color: $evo-danger-hover; }
236
+ &:active:not(:disabled) { background-color: $evo-danger-active; border-color: $evo-danger-active; }
237
+ &:focus-visible { outline-color: $evo-danger-focus; }
238
+ }
239
+
240
+ &.outline {
241
+ color: $evo-danger-color;
242
+ border-color: $evo-danger-color;
243
+
244
+ &:hover:not(:disabled) { color: $evo-danger-fg; background-color: $evo-danger-color; }
245
+ &:active:not(:disabled) { color: $evo-danger-fg; background-color: $evo-danger-hover; border-color: $evo-danger-hover; }
246
+ &:focus-visible { outline-color: $evo-danger-focus; }
247
+ }
248
+
249
+ &.ghost {
250
+ color: $evo-danger-color;
251
+
252
+ &:hover:not(:disabled) { background-color: $evo-danger-soft; }
253
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-danger-color 22%, transparent); }
254
+ &:focus-visible { outline-color: $evo-danger-focus; }
255
+ }
256
+
257
+ &.soft {
258
+ color: $evo-danger-color;
259
+ background-color: $evo-danger-soft;
260
+
261
+ &:hover:not(:disabled) { background-color: color-mix(in srgb, $evo-danger-color 22%, transparent); }
262
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-danger-color 30%, transparent); }
263
+ &:focus-visible { outline-color: $evo-danger-focus; }
264
+ }
265
+ }
266
+
267
+ .warning {
268
+ &.solid {
269
+ color: $evo-warning-text;
270
+ background-color: $evo-warning-color;
271
+ border-color: $evo-warning-color;
272
+
273
+ &:hover:not(:disabled) { background-color: $evo-warning-hover; border-color: $evo-warning-hover; }
274
+ &:active:not(:disabled) { background-color: $evo-warning-active; border-color: $evo-warning-active; }
275
+ &:focus-visible { outline-color: $evo-warning-focus; }
276
+ }
277
+
278
+ &.outline {
279
+ color: $evo-warning-text;
280
+ border-color: $evo-warning-color;
281
+
282
+ &:hover:not(:disabled) { background-color: $evo-warning-color; }
283
+ &:active:not(:disabled) { background-color: $evo-warning-hover; border-color: $evo-warning-hover; }
284
+ &:focus-visible { outline-color: $evo-warning-focus; }
285
+ }
286
+
287
+ &.ghost {
288
+ color: $evo-warning-text;
289
+
290
+ &:hover:not(:disabled) { background-color: $evo-warning-soft; }
291
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-warning-color 22%, transparent); }
292
+ &:focus-visible { outline-color: $evo-warning-focus; }
293
+ }
294
+
295
+ &.soft {
296
+ color: $evo-warning-text;
297
+ background-color: $evo-warning-soft;
298
+
299
+ &:hover:not(:disabled) { background-color: color-mix(in srgb, $evo-warning-color 22%, transparent); }
300
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-warning-color 30%, transparent); }
301
+ &:focus-visible { outline-color: $evo-warning-focus; }
302
+ }
303
+ }
304
+
305
+ .success {
306
+ &.solid {
307
+ color: $evo-success-fg;
308
+ background-color: $evo-success-color;
309
+ border-color: $evo-success-color;
310
+
311
+ &:hover:not(:disabled) { background-color: $evo-success-hover; border-color: $evo-success-hover; }
312
+ &:active:not(:disabled) { background-color: $evo-success-active; border-color: $evo-success-active; }
313
+ &:focus-visible { outline-color: $evo-success-focus; }
314
+ }
315
+
316
+ &.outline {
317
+ color: $evo-success-color;
318
+ border-color: $evo-success-color;
319
+
320
+ &:hover:not(:disabled) { color: $evo-success-fg; background-color: $evo-success-color; }
321
+ &:active:not(:disabled) { color: $evo-success-fg; background-color: $evo-success-hover; border-color: $evo-success-hover; }
322
+ &:focus-visible { outline-color: $evo-success-focus; }
323
+ }
324
+
325
+ &.ghost {
326
+ color: $evo-success-color;
327
+
328
+ &:hover:not(:disabled) { background-color: $evo-success-soft; }
329
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-success-color 22%, transparent); }
330
+ &:focus-visible { outline-color: $evo-success-focus; }
331
+ }
332
+
333
+ &.soft {
334
+ color: $evo-success-color;
335
+ background-color: $evo-success-soft;
336
+
337
+ &:hover:not(:disabled) { background-color: color-mix(in srgb, $evo-success-color 22%, transparent); }
338
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-success-color 30%, transparent); }
339
+ &:focus-visible { outline-color: $evo-success-focus; }
340
+ }
341
+ }
342
+
343
+ .info {
344
+ &.solid {
345
+ color: $evo-info-fg;
346
+ background-color: $evo-info-color;
347
+ border-color: $evo-info-color;
348
+
349
+ &:hover:not(:disabled) { background-color: $evo-info-hover; border-color: $evo-info-hover; }
350
+ &:active:not(:disabled) { background-color: $evo-info-active; border-color: $evo-info-active; }
351
+ &:focus-visible { outline-color: $evo-info-focus; }
352
+ }
353
+
354
+ &.outline {
355
+ color: $evo-info-color;
356
+ border-color: $evo-info-color;
357
+
358
+ &:hover:not(:disabled) { color: $evo-info-fg; background-color: $evo-info-color; }
359
+ &:active:not(:disabled) { color: $evo-info-fg; background-color: $evo-info-hover; border-color: $evo-info-hover; }
360
+ &:focus-visible { outline-color: $evo-info-focus; }
361
+ }
362
+
363
+ &.ghost {
364
+ color: $evo-info-color;
365
+
366
+ &:hover:not(:disabled) { background-color: $evo-info-soft; }
367
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-info-color 22%, transparent); }
368
+ &:focus-visible { outline-color: $evo-info-focus; }
369
+ }
370
+
371
+ &.soft {
372
+ color: $evo-info-color;
373
+ background-color: $evo-info-soft;
374
+
375
+ &:hover:not(:disabled) { background-color: color-mix(in srgb, $evo-info-color 22%, transparent); }
376
+ &:active:not(:disabled) { background-color: color-mix(in srgb, $evo-info-color 30%, transparent); }
377
+ &:focus-visible { outline-color: $evo-info-focus; }
378
+ }
379
+ }
380
+
381
+ // Respect users who prefer reduced motion
382
+ @media (prefers-reduced-motion: reduce) {
383
+ .button { transition: none; }
384
+ .spinner { animation-duration: 1.6s; }
385
+ }
@@ -0,0 +1,217 @@
1
+ @use './base/variables' as *;
2
+
3
+ // ============================================================
4
+ // EvoCard
5
+ // ------------------------------------------------------------
6
+ // Three orthogonal axes:
7
+ // 1. variant — elevated / outlined / ghost
8
+ // 2. orientation — vertical / horizontal / responsive
9
+ // 3. interactive — adds hover / focus / press states for
10
+ // when the root is a <button> or <a>
11
+ //
12
+ // All colours / shadows come from semantic tokens so the same
13
+ // rules render correctly under data-theme="light" and "dark".
14
+ // ============================================================
15
+
16
+ .root {
17
+ display: flex;
18
+ flex-direction: column;
19
+ background-color: $color-surface-elevated;
20
+ color: $color-text-primary;
21
+ border: 1px solid transparent;
22
+ border-radius: $evo-border-radius-lg;
23
+ overflow: hidden;
24
+ text-align: left;
25
+ text-decoration: none;
26
+ font-family: inherit;
27
+ transition:
28
+ box-shadow $transition-fast,
29
+ transform $transition-fast,
30
+ border-color $transition-fast,
31
+ background-color $transition-fast;
32
+ }
33
+
34
+ // ----- Variants ---------------------------------------------
35
+
36
+ .elevated {
37
+ background-color: $color-surface-elevated;
38
+ box-shadow: $shadow-md;
39
+ }
40
+
41
+ .outlined {
42
+ background-color: $color-surface;
43
+ border-color: $color-border;
44
+ box-shadow: none;
45
+ }
46
+
47
+ .ghost {
48
+ background-color: transparent;
49
+ border-color: transparent;
50
+ box-shadow: none;
51
+ }
52
+
53
+ // ----- Orientation ------------------------------------------
54
+ //
55
+ // Horizontal layouts use CSS grid: a fixed-ish media column on
56
+ // the left and content stacked on the right. `:has(> .media)`
57
+ // gates the two-column grid so cards without a media slot stay
58
+ // single-column even in horizontal/responsive mode.
59
+
60
+ .orient-vertical {
61
+ flex-direction: column;
62
+ }
63
+
64
+ .orient-horizontal {
65
+ display: grid;
66
+ grid-template-columns: minmax(0, 1fr);
67
+
68
+ &:has(> .media) {
69
+ grid-template-columns: minmax(0, 40%) minmax(0, 1fr);
70
+ }
71
+
72
+ > .media {
73
+ grid-row: 1 / -1;
74
+ grid-column: 1;
75
+ }
76
+
77
+ > :not(.media) {
78
+ grid-column: -2;
79
+ }
80
+ }
81
+
82
+ .orient-responsive {
83
+ flex-direction: column;
84
+
85
+ @media (min-width: 768px) {
86
+ display: grid;
87
+ flex-direction: initial;
88
+ grid-template-columns: minmax(0, 1fr);
89
+
90
+ &:has(> .media) {
91
+ grid-template-columns: minmax(0, 40%) minmax(0, 1fr);
92
+ }
93
+
94
+ > .media {
95
+ grid-row: 1 / -1;
96
+ grid-column: 1;
97
+ }
98
+
99
+ > :not(.media) {
100
+ grid-column: -2;
101
+ }
102
+ }
103
+ }
104
+
105
+ // ----- Interactive (real <button> / <a>) --------------------
106
+
107
+ .interactive {
108
+ cursor: pointer;
109
+ user-select: none;
110
+ appearance: none;
111
+ width: 100%;
112
+ font: inherit;
113
+ -webkit-tap-highlight-color: transparent;
114
+ touch-action: manipulation;
115
+ // a11y: cards used as buttons can be short — guarantee a 44px
116
+ // touch target so they stay tappable on mobile.
117
+ min-height: 2.75rem;
118
+
119
+ &:hover:not(:disabled) {
120
+ transform: translateY(-2px);
121
+ box-shadow: $shadow-lg;
122
+ }
123
+
124
+ &.outlined:hover:not(:disabled) {
125
+ border-color: $color-border-strong;
126
+ }
127
+
128
+ &.ghost:hover:not(:disabled) {
129
+ background-color: $color-surface-hover;
130
+ }
131
+
132
+ &:active:not(:disabled) {
133
+ transform: translateY(0);
134
+ box-shadow: $shadow-sm;
135
+ }
136
+
137
+ &:focus-visible {
138
+ outline: 2px solid $evo-primary-focus;
139
+ outline-offset: 2px;
140
+ }
141
+
142
+ &:disabled,
143
+ &[aria-disabled='true'] {
144
+ opacity: 0.5;
145
+ cursor: not-allowed;
146
+ pointer-events: none;
147
+ }
148
+ }
149
+
150
+ // ----- Slots ------------------------------------------------
151
+
152
+ .media {
153
+ display: block;
154
+ overflow: hidden;
155
+ background-color: $color-surface-sunken;
156
+ }
157
+
158
+ .mediaImg {
159
+ display: block;
160
+ width: 100%;
161
+ height: 100%;
162
+ object-fit: cover;
163
+ }
164
+
165
+ .header {
166
+ display: flex;
167
+ flex-direction: column;
168
+ gap: 0.25rem;
169
+ padding: 1rem 1.25rem 0.5rem;
170
+ }
171
+
172
+ .body {
173
+ flex: 1 1 auto;
174
+ min-width: 0;
175
+ padding: 0.5rem 1.25rem 1rem;
176
+ }
177
+
178
+ .footer {
179
+ padding: 0.75rem 1.25rem;
180
+ border-top: 1px solid $color-border-subtle;
181
+ }
182
+
183
+ // When the card has only a body (no header/footer), pad the body
184
+ // like a full block so content doesn't look cramped.
185
+ .root > .body:only-child {
186
+ padding: 1.25rem;
187
+ }
188
+
189
+ // ----- Typography -------------------------------------------
190
+
191
+ .title {
192
+ margin: 0;
193
+ font-size: $text-lg;
194
+ font-weight: 700;
195
+ line-height: 1.3;
196
+ color: $color-text-primary;
197
+ }
198
+
199
+ .description {
200
+ margin: 0;
201
+ font-size: $text-sm;
202
+ line-height: 1.55;
203
+ color: $color-text-secondary;
204
+ }
205
+
206
+ // ----- Motion preferences -----------------------------------
207
+
208
+ @media (prefers-reduced-motion: reduce) {
209
+ .root,
210
+ .interactive {
211
+ transition: none;
212
+
213
+ &:hover:not(:disabled) {
214
+ transform: none;
215
+ }
216
+ }
217
+ }
@@ -0,0 +1,120 @@
1
+ @use 'base/variables' as *;
2
+ @use 'base/color' as *;
3
+
4
+ .group {
5
+ border: none;
6
+ padding: 0;
7
+ margin: 0;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 0.5rem;
11
+ }
12
+
13
+ .groupLabel {
14
+ font-size: $text-sm;
15
+ font-weight: 600;
16
+ color: $color-text-primary;
17
+ margin-bottom: 0.125rem;
18
+ font-family: $font-sans;
19
+ }
20
+
21
+ .checkbox {
22
+ display: flex;
23
+ flex-direction: column;
24
+ gap: 0.25rem;
25
+
26
+ &.disabled {
27
+ opacity: 0.5;
28
+ pointer-events: none;
29
+ }
30
+ }
31
+
32
+ .input {
33
+ position: absolute;
34
+ opacity: 0;
35
+ width: 0;
36
+ height: 0;
37
+ pointer-events: none;
38
+
39
+ &:checked + .label .checkmark {
40
+ background-color: $evo-primary-color;
41
+ border-color: $evo-primary-color;
42
+
43
+ &::after {
44
+ opacity: 1;
45
+ transform: rotate(45deg) scale(1);
46
+ }
47
+ }
48
+
49
+ &:indeterminate + .label .checkmark {
50
+ background-color: $evo-primary-color;
51
+ border-color: $evo-primary-color;
52
+
53
+ &::after {
54
+ content: '';
55
+ position: absolute;
56
+ top: 50%;
57
+ left: 50%;
58
+ transform: translate(-50%, -50%) !important;
59
+ width: 8px;
60
+ height: 2px;
61
+ background: $evo-primary-fg;
62
+ border: none;
63
+ opacity: 1;
64
+ }
65
+ }
66
+
67
+ &:focus-visible + .label .checkmark {
68
+ outline: 2px solid $evo-primary-focus;
69
+ outline-offset: 2px;
70
+ }
71
+ }
72
+
73
+ .label {
74
+ display: flex;
75
+ align-items: center;
76
+ gap: 0.5rem;
77
+ cursor: pointer;
78
+ font-family: $font-sans;
79
+ }
80
+
81
+ .checkmark {
82
+ position: relative;
83
+ display: inline-flex;
84
+ align-items: center;
85
+ justify-content: center;
86
+ width: 1rem;
87
+ height: 1rem;
88
+ border: 2px solid $color-border;
89
+ border-radius: $radius-sm;
90
+ background: $color-surface;
91
+ transition: background-color $transition-fast, border-color $transition-fast;
92
+ flex-shrink: 0;
93
+
94
+ &::after {
95
+ content: '';
96
+ position: absolute;
97
+ top: 0px;
98
+ left: 3px;
99
+ width: 5px;
100
+ height: 8px;
101
+ border: 2px solid $evo-primary-fg;
102
+ border-top: none;
103
+ border-left: none;
104
+ opacity: 0;
105
+ transform: rotate(45deg) scale(0.5);
106
+ transition: opacity $transition-fast, transform $transition-fast;
107
+ }
108
+ }
109
+
110
+ .labelText {
111
+ font-size: $text-sm;
112
+ color: $color-text-primary;
113
+ }
114
+
115
+ .helperText {
116
+ font-size: $text-xs;
117
+ color: $color-text-muted;
118
+ padding-left: 1.5rem;
119
+ margin: 0;
120
+ }