@visns-studio/visns-components 6.17.2 → 6.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/package.json +10 -7
  2. package/src/components/AssociationManager.jsx +344 -177
  3. package/src/components/AsyncSelect.jsx +2 -2
  4. package/src/components/Autocomplete.jsx +57 -12
  5. package/src/components/Breadcrumb.jsx +23 -4
  6. package/src/components/ClientAssociationManager.jsx +321 -145
  7. package/src/components/DataGrid.jsx +625 -20
  8. package/src/components/DropZone.jsx +4 -1
  9. package/src/components/Form.jsx +233 -6
  10. package/src/components/MultiSelect.jsx +2 -2
  11. package/src/components/Navigation.jsx +39 -23
  12. package/src/components/TableFilter.jsx +172 -12
  13. package/src/components/UniversalSearch.jsx +390 -0
  14. package/src/components/auth/Login.jsx +164 -8
  15. package/src/components/auth/Profile.jsx +209 -22
  16. package/src/components/auth/authEndpoints.js +4 -0
  17. package/src/components/auth/webauthn.js +194 -0
  18. package/src/components/columns/ColumnRenderers.jsx +6 -1
  19. package/src/components/controls/DataGridSearch.jsx +11 -5
  20. package/src/components/generic/GenericAuth.jsx +6 -0
  21. package/src/components/generic/GenericDashboard.jsx +93 -36
  22. package/src/components/generic/GenericDetail.jsx +146 -61
  23. package/src/components/generic/GenericIndex.jsx +134 -38
  24. package/src/components/generic/GenericSettings.jsx +220 -0
  25. package/src/components/generic/StandardModal.jsx +165 -8
  26. package/src/components/generic/dashboardChartDefaults.js +230 -0
  27. package/src/components/generic/shared/indexLayout.js +38 -0
  28. package/src/components/settings/IntegrationsManager.jsx +474 -0
  29. package/src/components/sms/SmsLineSettings.jsx +16 -12
  30. package/src/components/styles/AssociationManager.module.scss +4 -363
  31. package/src/components/styles/AuditLog.module.scss +6 -6
  32. package/src/components/styles/AuditLogs.module.scss +3 -3
  33. package/src/components/styles/Autocomplete.module.scss +7 -7
  34. package/src/components/styles/BusinessCardOcr.module.scss +6 -1
  35. package/src/components/styles/CallQueueSettings.module.scss +4 -1
  36. package/src/components/styles/CameraPlacement.module.scss +19 -19
  37. package/src/components/styles/CategorizedDropZone.module.scss +6 -1
  38. package/src/components/styles/ClientAssociationManager.module.scss +5 -289
  39. package/src/components/styles/ClientAuth.module.scss +1 -1
  40. package/src/components/styles/DataGrid.module.scss +455 -8
  41. package/src/components/styles/Field.module.scss +32 -24
  42. package/src/components/styles/Form.module.scss +123 -3
  43. package/src/components/styles/GenericDashboard.module.scss +612 -825
  44. package/src/components/styles/GenericDetail.module.scss +406 -61
  45. package/src/components/styles/GenericDynamic.module.scss +25 -25
  46. package/src/components/styles/GenericEditableTable.module.scss +33 -33
  47. package/src/components/styles/GenericFormBuilder.module.scss +119 -119
  48. package/src/components/styles/GenericIndex.module.scss +483 -38
  49. package/src/components/styles/GenericMain.module.scss +6 -3
  50. package/src/components/styles/GenericQuote.module.scss +399 -81
  51. package/src/components/styles/GenericReport.module.scss +8 -8
  52. package/src/components/styles/GenericSettings.module.scss +209 -0
  53. package/src/components/styles/GenericSort.module.scss +9 -9
  54. package/src/components/styles/Integrations.module.scss +321 -0
  55. package/src/components/styles/List.module.scss +4 -4
  56. package/src/components/styles/Login.module.scss +57 -0
  57. package/src/components/styles/MultiSelect.module.scss +4 -4
  58. package/src/components/styles/Navigation.module.scss +12 -12
  59. package/src/components/styles/Notification.module.scss +12 -8
  60. package/src/components/styles/NotificationList.module.scss +5 -5
  61. package/src/components/styles/Profile.module.scss +5 -5
  62. package/src/components/styles/QrCode.module.scss +4 -4
  63. package/src/components/styles/QuickAction.module.scss +20 -20
  64. package/src/components/styles/SortableQuoteItems.module.scss +4 -4
  65. package/src/components/styles/TableFilter.module.scss +128 -17
  66. package/src/components/styles/TicketConversation.module.scss +497 -0
  67. package/src/components/styles/UniversalSearch.module.scss +229 -0
  68. package/src/components/styles/Vault.module.scss +413 -0
  69. package/src/components/styles/_associationManager.scss +467 -0
  70. package/src/components/styles/_authBrandPanel.scss +15 -9
  71. package/src/components/styles/_controls.scss +43 -39
  72. package/src/components/styles/global-datagrid.css +196 -0
  73. package/src/components/styles/global.css +168 -17
  74. package/src/components/tickets/TicketConversation.jsx +455 -0
  75. package/src/components/utils/rowActionSettings.js +117 -0
  76. package/src/components/vault/VaultClientPicker.jsx +183 -0
  77. package/src/components/vault/VaultEntryForm.jsx +23 -0
  78. package/src/components/vault/VaultManager.jsx +382 -34
  79. package/src/components/vault/VaultShareModal.jsx +454 -0
  80. package/src/components/vault/vaultEndpoints.js +16 -2
  81. package/src/components/vault/vaultShares.js +247 -0
  82. package/src/index.js +35 -0
  83. package/src/utils/fetchUtil.js +38 -14
@@ -0,0 +1,467 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Association manager — shared presentation
3
+ // ---------------------------------------------------------------------------
4
+ // `AssociationManager` and the older `ClientAssociationManager` render the same
5
+ // thing (GenericDetail routes BOTH `component: "AssociationManager"` and
6
+ // `component: "ClientAssociationManager"` to the former), so the look lives in
7
+ // one mixin and each CSS module includes it. Class names are hashed per file,
8
+ // so the two stay independent modules while sharing a single definition.
9
+ //
10
+ // The panel renders inside a host page's card — the "Customer Assignments" tab
11
+ // on a contact, the "Assignments" tab on a customer — so it paints NO surface
12
+ // of its own at the top level. It also draws no title: the tab above it already
13
+ // names the thing, and repeating it inside the panel was the loudest part of
14
+ // the old design. `config.showTitle` opts a standalone mount back in.
15
+ //
16
+ // Everything is a CSS custom property with a fallback, in the library's usual
17
+ // pattern: the package carries no palette.
18
+ // ---------------------------------------------------------------------------
19
+
20
+ @use 'controls' as *;
21
+
22
+ @mixin association-manager {
23
+ .container {
24
+ // Local aliases so a row, a chip and a field all mix against the same
25
+ // ink rather than each restating an rgba() of their own.
26
+ --am-ink: var(--paragraph-color, #2b2b2b);
27
+ --am-accent: var(--primary-color, #1b3933);
28
+ --am-surface: var(--paper, #fff);
29
+ --am-line: var(--border-color, #{rgba(43, 43, 43, 0.14)});
30
+ --am-muted: color-mix(in srgb, var(--am-ink) 72%, transparent);
31
+ --am-quiet: color-mix(in srgb, var(--am-ink) 3%, transparent);
32
+ --am-radius: var(--card-radius, 10px);
33
+
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: var(--spacing-lg, 1.5rem);
37
+ color: var(--am-ink);
38
+ font-size: 0.85rem;
39
+ }
40
+
41
+ // ----------------------------------------------------------------------
42
+ // optional heading (standalone mounts only — see the note above)
43
+ // ----------------------------------------------------------------------
44
+
45
+ .heading {
46
+ margin: 0;
47
+ color: var(--am-accent);
48
+ font-size: 1rem;
49
+ font-weight: 600;
50
+ }
51
+
52
+ .headingNote {
53
+ margin: 0.15rem 0 0;
54
+ color: var(--am-muted);
55
+ font-size: 0.8rem;
56
+ }
57
+
58
+ .loading,
59
+ .empty {
60
+ margin: 0;
61
+ color: var(--am-muted);
62
+ font-size: 0.8rem;
63
+ }
64
+
65
+ .loading {
66
+ padding: 1rem 0;
67
+ }
68
+
69
+ // An empty list is a dashed outline rather than a filled slab: nothing to
70
+ // read here, so nothing should draw the eye.
71
+ .empty {
72
+ padding: 1.1rem 1rem;
73
+ text-align: center;
74
+ border: 1px dashed var(--am-line);
75
+ border-radius: var(--am-radius);
76
+ }
77
+
78
+ // ----------------------------------------------------------------------
79
+ // the multiple-primary notice
80
+ // ----------------------------------------------------------------------
81
+
82
+ // Stated once, quietly, in the amber family — not a filled banner with a
83
+ // 4px bar down its side.
84
+ .notice {
85
+ display: flex;
86
+ gap: 0.55rem;
87
+ align-items: flex-start;
88
+ padding: 0.65rem 0.8rem;
89
+ color: #7a5c00;
90
+ font-size: 0.8rem;
91
+ background: color-mix(in srgb, #b98900 8%, transparent);
92
+ border: 1px solid color-mix(in srgb, #b98900 32%, transparent);
93
+ border-radius: var(--am-radius);
94
+ }
95
+
96
+ .noticeIcon {
97
+ flex: none;
98
+ margin-top: 0.1rem;
99
+ }
100
+
101
+ .noticeBody {
102
+ flex: 1 1 auto;
103
+ min-width: 0;
104
+
105
+ strong {
106
+ display: block;
107
+ font-weight: 600;
108
+ }
109
+
110
+ p {
111
+ margin: 0.15rem 0 0;
112
+ line-height: 1.4;
113
+ }
114
+ }
115
+
116
+ .noticeAction {
117
+ @include button-base;
118
+ @include button-small;
119
+
120
+ margin-top: 0.55rem;
121
+ color: inherit;
122
+ background: none;
123
+ border-color: currentcolor;
124
+
125
+ &:hover:not(:disabled) {
126
+ background: color-mix(in srgb, #b98900 14%, transparent);
127
+ }
128
+ }
129
+
130
+ // ----------------------------------------------------------------------
131
+ // the list
132
+ // ----------------------------------------------------------------------
133
+
134
+ .section {
135
+ display: flex;
136
+ flex-direction: column;
137
+ gap: 0.5rem;
138
+ min-width: 0;
139
+ }
140
+
141
+ .sectionHead {
142
+ display: flex;
143
+ gap: var(--spacing-sm, 0.5rem);
144
+ align-items: center;
145
+ justify-content: space-between;
146
+ }
147
+
148
+ .sectionTitle {
149
+ margin: 0;
150
+ color: var(--am-muted);
151
+ font-size: 0.72rem;
152
+ font-weight: 600;
153
+ letter-spacing: 0.04em;
154
+ text-transform: uppercase;
155
+ }
156
+
157
+ .list {
158
+ display: flex;
159
+ flex-direction: column;
160
+ overflow: hidden;
161
+ background: var(--am-surface);
162
+ border: 1px solid var(--am-line);
163
+ border-radius: var(--am-radius);
164
+ }
165
+
166
+ .row {
167
+ display: flex;
168
+ gap: var(--spacing-md, 1rem);
169
+ align-items: center;
170
+ padding: 0.6rem 0.85rem;
171
+ border-bottom: 1px solid var(--am-line);
172
+ transition: background var(--speed, 160ms)
173
+ var(--ease, cubic-bezier(0.4, 0, 0.2, 1));
174
+
175
+ &:last-child {
176
+ border-bottom: 0;
177
+ }
178
+
179
+ &:hover {
180
+ background: var(--hover-color, rgb(0 0 0 / 3%));
181
+ }
182
+ }
183
+
184
+ /* The primary row is marked by a rule down its edge, not by tinting the
185
+ whole slab — one row in a list of five should read as *marked*, not as a
186
+ different kind of object. */
187
+ .rowPrimary {
188
+ box-shadow: inset 2px 0 0 0 var(--am-accent);
189
+ }
190
+
191
+ .main {
192
+ display: flex;
193
+ flex: 1 1 auto;
194
+ flex-direction: column;
195
+ gap: 0.15rem;
196
+ min-width: 0;
197
+ }
198
+
199
+ .entityName {
200
+ display: flex;
201
+ gap: 0.4rem;
202
+ align-items: center;
203
+ font-weight: 600;
204
+ line-height: 1.3;
205
+ overflow-wrap: anywhere;
206
+ }
207
+
208
+ .chip {
209
+ display: inline-flex;
210
+ flex: none;
211
+ gap: 0.2rem;
212
+ align-items: center;
213
+ padding: 1px 7px;
214
+ color: var(--am-muted);
215
+ font-size: 0.7rem;
216
+ font-weight: 600;
217
+ white-space: nowrap;
218
+ background: var(--am-quiet);
219
+ border: 1px solid var(--am-line);
220
+ border-radius: 999px;
221
+ }
222
+
223
+ /* Outlined, in the brand ink — the old badge was a solid dark slab with
224
+ uppercase white type, which read as a warning. */
225
+ .chipPrimary {
226
+ color: var(--am-accent);
227
+ background: color-mix(in srgb, var(--am-accent) 8%, transparent);
228
+ border-color: color-mix(in srgb, var(--am-accent) 35%, transparent);
229
+ }
230
+
231
+ /* Relationship, role, notes and the date all read as one quiet line of
232
+ metadata under the name, separated by middots, the way the rest of the
233
+ app writes a subtitle. */
234
+ .meta {
235
+ display: flex;
236
+ flex-wrap: wrap;
237
+ gap: 0.3rem;
238
+ align-items: center;
239
+ color: var(--am-muted);
240
+ font-size: 0.75rem;
241
+ line-height: 1.4;
242
+ }
243
+
244
+ .sep {
245
+ opacity: 0.5;
246
+ }
247
+
248
+ .relationshipType {
249
+ text-transform: capitalize;
250
+ }
251
+
252
+ .notes {
253
+ overflow-wrap: anywhere;
254
+ }
255
+
256
+ .date {
257
+ display: inline-flex;
258
+ gap: 0.25rem;
259
+ align-items: center;
260
+ white-space: nowrap;
261
+ }
262
+
263
+ // ----------------------------------------------------------------------
264
+ // row actions
265
+ // ----------------------------------------------------------------------
266
+
267
+ /* Revealed on hover, but never *only* on hover: `:focus-within` keeps them
268
+ reachable from the keyboard, and a coarse pointer (the iPad this is used
269
+ on) has no hover state at all, so there they are simply always shown. */
270
+ .actions {
271
+ display: flex;
272
+ flex: none;
273
+ gap: 0.15rem;
274
+ align-items: center;
275
+ opacity: 0;
276
+ transition: opacity var(--speed, 160ms)
277
+ var(--ease, cubic-bezier(0.4, 0, 0.2, 1));
278
+ }
279
+
280
+ .row:hover .actions,
281
+ .row:focus-within .actions {
282
+ opacity: 1;
283
+ }
284
+
285
+ @media (hover: none), (pointer: coarse) {
286
+ .actions {
287
+ opacity: 1;
288
+ }
289
+ }
290
+
291
+ .iconButton {
292
+ display: inline-flex;
293
+ align-items: center;
294
+ justify-content: center;
295
+ width: 28px;
296
+ height: 28px;
297
+ padding: 0;
298
+ color: var(--am-muted);
299
+ background: none;
300
+ border: 0;
301
+ border-radius: 6px;
302
+ cursor: pointer;
303
+ transition:
304
+ color var(--speed, 160ms) var(--ease, cubic-bezier(0.4, 0, 0.2, 1)),
305
+ background var(--speed, 160ms) var(--ease, cubic-bezier(0.4, 0, 0.2, 1));
306
+
307
+ &:hover:not(:disabled) {
308
+ color: var(--am-ink);
309
+ background: var(--am-quiet);
310
+ }
311
+
312
+ &:focus-visible {
313
+ opacity: 1;
314
+ outline: 2px solid var(--highlight-color, #3cbf7d);
315
+ outline-offset: 1px;
316
+ }
317
+ }
318
+
319
+ /* Currently primary: the star is already lit, and the button takes it back
320
+ off. Coloured so the state is legible before the hover. */
321
+ .iconButtonOn {
322
+ color: var(--am-accent);
323
+ }
324
+
325
+ /* Destructive, stated in colour on hover only — a solid red button on every
326
+ row made removal the most prominent thing on the panel. */
327
+ .iconButtonDanger {
328
+ &:hover:not(:disabled) {
329
+ color: var(--danger-color, #be3e3e);
330
+ background: var(--danger-light, rgb(190 62 62 / 12%));
331
+ }
332
+ }
333
+
334
+ // ----------------------------------------------------------------------
335
+ // the add form
336
+ // ----------------------------------------------------------------------
337
+
338
+ .addToggle {
339
+ @include button-secondary;
340
+ @include button-small;
341
+ }
342
+
343
+ /* Two columns from 820px — the width at which a `Customer` and a `Site`
344
+ select can sit side by side without either becoming a slot machine. */
345
+ .form {
346
+ display: grid;
347
+ grid-template-columns: minmax(0, 1fr);
348
+ gap: var(--spacing-md, 1rem);
349
+ padding: var(--spacing-md, 1rem);
350
+ background: var(--am-quiet);
351
+ border: 1px solid var(--am-line);
352
+ border-radius: var(--am-radius);
353
+ }
354
+
355
+ @media (min-width: 820px) {
356
+ .form {
357
+ grid-template-columns: repeat(2, minmax(0, 1fr));
358
+ }
359
+ }
360
+
361
+ .field {
362
+ display: flex;
363
+ flex-direction: column;
364
+ gap: 0.3rem;
365
+ min-width: 0;
366
+ }
367
+
368
+ /* A textarea, a checkbox and the button row each own the full width: they
369
+ do not pair with anything. */
370
+ .fieldWide,
371
+ .formActions {
372
+ grid-column: 1 / -1;
373
+ }
374
+
375
+ .label {
376
+ color: var(--am-muted);
377
+ font-size: 0.72rem;
378
+ font-weight: 600;
379
+ }
380
+
381
+ .required {
382
+ color: var(--danger-color, #be3e3e);
383
+ }
384
+
385
+ .selectField,
386
+ .textField,
387
+ .textareaField {
388
+ @include input-base;
389
+
390
+ font-size: 0.85rem;
391
+ }
392
+
393
+ .textareaField {
394
+ min-height: 4.5rem;
395
+ padding-top: 0.55rem;
396
+ padding-bottom: 0.55rem;
397
+ line-height: 1.4;
398
+ resize: vertical;
399
+ }
400
+
401
+ /* The checkbox is pinned on every axis. The host CRM styles bare `input`
402
+ with a text-field height and a pill radius, and a flex parent will happily
403
+ stretch a box that has not been told its maximum — which rendered the
404
+ "Primary Assignment" tick as a wide oval. */
405
+ .checkboxLabel {
406
+ display: inline-flex;
407
+ gap: 0.5rem;
408
+ align-items: center;
409
+ margin-top: 0.2rem;
410
+ color: var(--am-ink);
411
+ font-size: 0.8rem;
412
+ cursor: pointer;
413
+
414
+ input[type='checkbox'] {
415
+ flex: 0 0 16px;
416
+ box-sizing: border-box;
417
+ width: 16px;
418
+ min-width: 16px;
419
+ max-width: 16px;
420
+ height: 16px;
421
+ min-height: 16px;
422
+ max-height: 16px;
423
+ padding: 0;
424
+ margin: 0;
425
+ border-radius: 3px;
426
+ cursor: pointer;
427
+ }
428
+ }
429
+
430
+ .formActions {
431
+ display: flex;
432
+ gap: 0.5rem;
433
+ align-items: center;
434
+ justify-content: flex-end;
435
+ }
436
+
437
+ .primaryButton {
438
+ @include button-primary;
439
+ @include button-small;
440
+ }
441
+
442
+ .secondaryButton {
443
+ @include button-secondary;
444
+ @include button-small;
445
+ }
446
+
447
+ @media (max-width: 640px) {
448
+ .row {
449
+ flex-wrap: wrap;
450
+ }
451
+
452
+ .actions {
453
+ margin-left: auto;
454
+ }
455
+
456
+ /* Reversed so the primary action is under the thumb on the right, and
457
+ both stretched so neither is a small target on a phone. */
458
+ .formActions {
459
+ flex-direction: row-reverse;
460
+
461
+ > * {
462
+ flex: 1 1 0;
463
+ width: auto;
464
+ }
465
+ }
466
+ }
467
+ }
@@ -27,7 +27,7 @@
27
27
  @mixin auth-brand-panel($collapse: hide) {
28
28
  .brand {
29
29
  position: relative;
30
- background-color: var(--navy-deep);
30
+ background-color: var(--navy-deep, #16202e);
31
31
  background-size: cover;
32
32
  background-position: center 62%;
33
33
  overflow: hidden;
@@ -46,19 +46,25 @@
46
46
  content: '';
47
47
  position: absolute;
48
48
  inset: 0;
49
+ // The nine stops here were hardcoded `rgba(16, 25, 42, …)` — the
50
+ // navy of the CRM this component was first written for. Over a
51
+ // green-branded consumer's artwork the bottom half of the panel
52
+ // turned blue, so the sign-in screen advertised somebody else's
53
+ // brand. `--brand-ground` is the consumer's own ground; the navy
54
+ // survives only as its fallback.
49
55
  background:
50
56
  linear-gradient(
51
57
  to top,
52
- rgba(16, 25, 42, 0.94) 0%,
53
- rgba(16, 25, 42, 0.88) 18%,
54
- rgba(16, 25, 42, 0.62) 42%,
55
- rgba(16, 25, 42, 0.22) 70%,
56
- rgba(16, 25, 42, 0.06) 100%
58
+ color-mix(in srgb, var(--brand-ground, #10192a) 94%, transparent) 0%,
59
+ color-mix(in srgb, var(--brand-ground, #10192a) 88%, transparent) 18%,
60
+ color-mix(in srgb, var(--brand-ground, #10192a) 62%, transparent) 42%,
61
+ color-mix(in srgb, var(--brand-ground, #10192a) 22%, transparent) 70%,
62
+ color-mix(in srgb, var(--brand-ground, #10192a) 6%, transparent) 100%
57
63
  ),
58
64
  linear-gradient(
59
65
  to right,
60
- rgba(16, 25, 42, 0.5) 0%,
61
- rgba(16, 25, 42, 0) 55%
66
+ color-mix(in srgb, var(--brand-ground, #10192a) 50%, transparent) 0%,
67
+ color-mix(in srgb, var(--brand-ground, #10192a) 0%, transparent) 55%
62
68
  );
63
69
  }
64
70
 
@@ -111,7 +117,7 @@
111
117
  top: 50%;
112
118
  width: 1.5rem;
113
119
  height: 2px;
114
- background: var(--flag);
120
+ background: var(--flag, #8a8f98);
115
121
  }
116
122
  }
117
123
 
@@ -18,13 +18,13 @@
18
18
  gap: 0.4rem;
19
19
  width: max-content;
20
20
  min-height: var(--control-height, 2.5rem);
21
- padding: 0 var(--spacing-md);
21
+ padding: 0 var(--spacing-md, 1rem);
22
22
  border: 1px solid transparent;
23
- border-radius: var(--radius-sm);
24
- font-family: var(--font-family);
23
+ border-radius: var(--radius-sm, 8px);
24
+ font-family: var(--font-family, inherit);
25
25
  // Was 1rem against 15px body text, so buttons shouted slightly.
26
- font-size: var(--font-size-sm);
27
- font-weight: var(--font-weight-semibold);
26
+ font-size: var(--font-size-sm, 0.8125rem);
27
+ font-weight: var(--font-weight-semibold, 600);
28
28
  line-height: 1;
29
29
  text-decoration: none;
30
30
  white-space: nowrap;
@@ -32,9 +32,9 @@
32
32
  // Was `all 0.2s`, which animates layout properties too and makes hover
33
33
  // feel laggy. Only what actually changes.
34
34
  transition:
35
- background-color var(--speed) var(--ease),
36
- border-color var(--speed) var(--ease),
37
- color var(--speed) var(--ease);
35
+ background-color var(--speed, 160ms) var(--ease, cubic-bezier(0.4, 0, 0.2, 1)),
36
+ border-color var(--speed, 160ms) var(--ease, cubic-bezier(0.4, 0, 0.2, 1)),
37
+ color var(--speed, 160ms) var(--ease, cubic-bezier(0.4, 0, 0.2, 1));
38
38
 
39
39
  &:disabled,
40
40
  &[aria-disabled='true'] {
@@ -42,21 +42,25 @@
42
42
  cursor: not-allowed;
43
43
  }
44
44
 
45
- // Focus is handled globally by the app's _focus.scss; nothing here removes
46
- // the outline, which is what created the accessibility gap in the first
47
- // place.
45
+ // Focus was documented as "handled globally by the app's _focus.scss" —
46
+ // a file that does not exist in this package, so every shared control
47
+ // shipped with browser-default focus only. It is handled here now.
48
+ &:focus-visible {
49
+ outline: 2px solid var(--highlight-color, #3cbf7d);
50
+ outline-offset: 2px;
51
+ }
48
52
  }
49
53
 
50
54
  @mixin button-primary {
51
55
  @include button-base;
52
56
 
53
- background: var(--primary-color);
54
- border-color: var(--primary-color);
57
+ background: var(--primary-color, #1b3933);
58
+ border-color: var(--primary-color, #1b3933);
55
59
  color: #fff;
56
60
 
57
61
  &:hover:not(:disabled) {
58
- background: var(--primary-color-darker);
59
- border-color: var(--primary-color-darker);
62
+ background: var(--primary-color-darker, #12241f);
63
+ border-color: var(--primary-color-darker, #12241f);
60
64
  // Was a light-blue fill with navy text, which dropped below a legible
61
65
  // contrast on hover — the one state where feedback matters most.
62
66
  color: #fff;
@@ -66,13 +70,13 @@
66
70
  @mixin button-secondary {
67
71
  @include button-base;
68
72
 
69
- background: var(--paper);
70
- border-color: var(--border-color);
71
- color: var(--text-color);
73
+ background: var(--paper, #fff);
74
+ border-color: var(--border-color, #e1e1e1);
75
+ color: var(--text-color, #2b2b2b);
72
76
 
73
77
  &:hover:not(:disabled) {
74
- border-color: var(--border-color-dark);
75
- background: var(--hover-color);
78
+ border-color: var(--border-color-dark, #c9c9c9);
79
+ background: var(--hover-color, rgba(27, 57, 51, 0.06));
76
80
  }
77
81
  }
78
82
 
@@ -81,10 +85,10 @@
81
85
 
82
86
  background: none;
83
87
  border-color: transparent;
84
- color: var(--primary-color);
88
+ color: var(--primary-color, #1b3933);
85
89
 
86
90
  &:hover:not(:disabled) {
87
- background: var(--primary-color-light);
91
+ background: var(--primary-color-light, rgba(27, 57, 51, 0.16));
88
92
  }
89
93
  }
90
94
 
@@ -93,18 +97,18 @@
93
97
 
94
98
  background: none;
95
99
  border-color: transparent;
96
- color: var(--danger-color);
100
+ color: var(--danger-color, #be3e3e);
97
101
 
98
102
  &:hover:not(:disabled) {
99
- background: var(--danger-light);
100
- border-color: var(--danger-color);
103
+ background: var(--danger-light, rgba(190, 62, 62, 0.12));
104
+ border-color: var(--danger-color, #be3e3e);
101
105
  }
102
106
  }
103
107
 
104
108
  @mixin button-small {
105
109
  min-height: var(--control-height-sm, 2rem);
106
- padding: 0 var(--spacing-sm);
107
- font-size: var(--font-size-xs);
110
+ padding: 0 var(--spacing-sm, 0.5rem);
111
+ font-size: var(--font-size-xs, 0.75rem);
108
112
  }
109
113
 
110
114
  /* --- Inputs -------------------------------------------------------------- */
@@ -113,27 +117,27 @@
113
117
  width: 100%;
114
118
  min-height: var(--control-height, 2.5rem);
115
119
  padding: 0 var(--input-padding-x, 0.65rem);
116
- border: 1px solid var(--border-color);
117
- border-radius: var(--radius-sm);
118
- background: var(--paper);
119
- font-family: var(--font-family);
120
- font-size: var(--font-size-md);
121
- color: var(--text-color);
120
+ border: 1px solid var(--border-color, #e1e1e1);
121
+ border-radius: var(--radius-sm, 8px);
122
+ background: var(--paper, #fff);
123
+ font-family: var(--font-family, inherit);
124
+ font-size: var(--font-size-md, 0.9375rem);
125
+ color: var(--text-color, #2b2b2b);
122
126
  transition:
123
- border-color var(--speed) var(--ease),
124
- box-shadow var(--speed) var(--ease);
127
+ border-color var(--speed, 160ms) var(--ease, cubic-bezier(0.4, 0, 0.2, 1)),
128
+ box-shadow var(--speed, 160ms) var(--ease, cubic-bezier(0.4, 0, 0.2, 1));
125
129
 
126
130
  &::placeholder {
127
- color: var(--muted-color);
131
+ color: var(--muted-color, rgba(43, 60, 43, 0.72));
128
132
  }
129
133
 
130
134
  &:hover:not(:disabled) {
131
- border-color: var(--border-color-dark);
135
+ border-color: var(--border-color-dark, #c9c9c9);
132
136
  }
133
137
 
134
138
  &:disabled {
135
- background: var(--bg-color-secondary);
136
- color: var(--disabled-color);
139
+ background: var(--bg-color-secondary, #eeece7);
140
+ color: var(--disabled-color, rgba(43, 60, 43, 0.38));
137
141
  cursor: not-allowed;
138
142
  }
139
143
  }