@webority/theme 0.0.10 → 0.3.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.
- package/README.md +11 -4
- package/dist/webority-theme.css +1 -1
- package/package.json +2 -1
- package/scss/_components.scss +61 -82
- package/scss/_flags.scss +270 -0
- package/scss/_tokens.scss +37 -19
- package/scss/_variables.scss +27 -24
- package/scss/_wui-aliases.scss +3 -1
- package/scss/_wui-components.scss +548 -103
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
2
|
// _wui-components.scss
|
|
3
3
|
// ----------------------------------------------------------------------------
|
|
4
|
-
// Extracted portal component layer —
|
|
5
|
-
// customer portal's `design-system.css`,
|
|
6
|
-
// `portal-helpers.css
|
|
4
|
+
// Extracted portal component layer — the shared, reusable UI-component
|
|
5
|
+
// CSS pulled from the customer portal's `design-system.css`,
|
|
6
|
+
// `components.css`, and `portal-helpers.css`.
|
|
7
7
|
//
|
|
8
8
|
// Only rules whose subject is a genuinely shared, reusable UI-component class
|
|
9
9
|
// were kept (fields, badges, modals, tables, drawers, phone/date/search
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// ============================================================================
|
|
17
17
|
|
|
18
18
|
// ── Checkbox & Radio ──
|
|
19
|
-
//
|
|
19
|
+
// Selection controls: a 20px glyph centred in a 36px circular hit area
|
|
20
20
|
// that shows a soft primary-tinted ripple on hover. Unchecked = muted
|
|
21
21
|
// grey-blue outline; checked = solid primary. Checkbox = squircle box + white
|
|
22
22
|
// tick; radio = ring + filled centre dot. Pure CSS, no JS — drives the native
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
background-color 0.15s ease;
|
|
70
70
|
}
|
|
71
71
|
.lx-check {
|
|
72
|
-
border-radius:
|
|
72
|
+
border-radius: var(--radius-sm);
|
|
73
73
|
corner-shape: squircle;
|
|
74
74
|
}
|
|
75
75
|
.lx-radio {
|
|
@@ -190,9 +190,9 @@
|
|
|
190
190
|
.textarea {
|
|
191
191
|
box-sizing: border-box;
|
|
192
192
|
width: 100%;
|
|
193
|
-
height:
|
|
193
|
+
height: var(--wui-control-height);
|
|
194
194
|
padding: 12px;
|
|
195
|
-
border-radius:
|
|
195
|
+
border-radius: var(--radius);
|
|
196
196
|
border: 1px solid var(--color-border);
|
|
197
197
|
background: var(--color-surface);
|
|
198
198
|
font-size: 14px;
|
|
@@ -222,7 +222,7 @@
|
|
|
222
222
|
outline: none;
|
|
223
223
|
background-color: var(--color-bg-muted);
|
|
224
224
|
color: var(--color-text);
|
|
225
|
-
border-color:
|
|
225
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
226
226
|
box-shadow: none;
|
|
227
227
|
}
|
|
228
228
|
// Textarea renders as .form-control.textarea (react-bootstrap). Match the
|
|
@@ -233,7 +233,7 @@ textarea.textarea.form-control:hover:not(:focus):not(:disabled):not([readonly])
|
|
|
233
233
|
}
|
|
234
234
|
textarea.textarea:focus {
|
|
235
235
|
outline: none;
|
|
236
|
-
border-color:
|
|
236
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
237
237
|
box-shadow: none;
|
|
238
238
|
}
|
|
239
239
|
.input::placeholder,
|
|
@@ -269,17 +269,27 @@ textarea.textarea:focus {
|
|
|
269
269
|
|
|
270
270
|
// Invalid field state (required-field validation) — soft danger-tinted
|
|
271
271
|
// border on the surface.
|
|
272
|
+
// `[aria-invalid="true"]` is styled alongside `.is-invalid`: React sets the ARIA
|
|
273
|
+
// attribute (it is the accessible source of truth) and does not always add the
|
|
274
|
+
// Bootstrap class, so keying on the class alone left React error states unstyled.
|
|
272
275
|
.input.is-invalid,
|
|
273
276
|
.select.is-invalid,
|
|
274
277
|
.textarea.is-invalid,
|
|
275
278
|
.input.is-invalid:focus,
|
|
276
279
|
.select.is-invalid:focus,
|
|
277
|
-
.textarea.is-invalid:focus
|
|
280
|
+
.textarea.is-invalid:focus,
|
|
281
|
+
.input[aria-invalid="true"],
|
|
282
|
+
.select[aria-invalid="true"],
|
|
283
|
+
.textarea[aria-invalid="true"],
|
|
284
|
+
.input[aria-invalid="true"]:focus,
|
|
285
|
+
.select[aria-invalid="true"]:focus,
|
|
286
|
+
.textarea[aria-invalid="true"]:focus {
|
|
278
287
|
background: var(--color-surface);
|
|
279
288
|
border-color: color-mix(in srgb, var(--color-danger) 45%, var(--color-surface));
|
|
280
289
|
box-shadow: none;
|
|
281
290
|
}
|
|
282
|
-
.input.is-invalid
|
|
291
|
+
.input.is-invalid,
|
|
292
|
+
.input[aria-invalid="true"] {
|
|
283
293
|
padding-right: 41px;
|
|
284
294
|
}
|
|
285
295
|
.field-error {
|
|
@@ -472,7 +482,7 @@ textarea.textarea:focus {
|
|
|
472
482
|
border: none;
|
|
473
483
|
background: transparent;
|
|
474
484
|
color: var(--color-text-subtle);
|
|
475
|
-
border-radius:
|
|
485
|
+
border-radius: var(--radius);
|
|
476
486
|
cursor: pointer;
|
|
477
487
|
transition:
|
|
478
488
|
color 0.12s ease,
|
|
@@ -546,9 +556,11 @@ textarea.textarea:focus {
|
|
|
546
556
|
.field > .field-error {
|
|
547
557
|
align-self: stretch;
|
|
548
558
|
}
|
|
559
|
+
// Doubled selectors — see the lg-tier block below: a media query carries no
|
|
560
|
+
// extra specificity, so a consumer's later single-class rule would win.
|
|
549
561
|
@media (max-width: 575.98px) {
|
|
550
|
-
.field-row,
|
|
551
|
-
.field-row-3 {
|
|
562
|
+
.field-row.field-row,
|
|
563
|
+
.field-row-3.field-row-3 {
|
|
552
564
|
grid-template-columns: 1fr;
|
|
553
565
|
}
|
|
554
566
|
}
|
|
@@ -556,7 +568,7 @@ textarea.textarea:focus {
|
|
|
556
568
|
// red border around a non-input group (e.g. a structure-form card row)
|
|
557
569
|
.group-invalid {
|
|
558
570
|
border: 1px solid var(--color-danger);
|
|
559
|
-
border-radius:
|
|
571
|
+
border-radius: var(--radius);
|
|
560
572
|
padding: 8px;
|
|
561
573
|
}
|
|
562
574
|
|
|
@@ -566,7 +578,7 @@ textarea.textarea:focus {
|
|
|
566
578
|
display: flex;
|
|
567
579
|
align-items: stretch;
|
|
568
580
|
border: 1px solid var(--color-border);
|
|
569
|
-
border-radius:
|
|
581
|
+
border-radius: var(--radius-lg);
|
|
570
582
|
corner-shape: squircle;
|
|
571
583
|
background: var(--color-surface);
|
|
572
584
|
transition:
|
|
@@ -580,7 +592,7 @@ textarea.textarea:focus {
|
|
|
580
592
|
}
|
|
581
593
|
.input-with-unit:focus-within {
|
|
582
594
|
background: var(--color-bg-muted);
|
|
583
|
-
border-color:
|
|
595
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
584
596
|
}
|
|
585
597
|
.input-with-unit:has(.num-input.error) {
|
|
586
598
|
border-color: var(--color-danger);
|
|
@@ -657,7 +669,7 @@ textarea.textarea:focus {
|
|
|
657
669
|
background: currentColor;
|
|
658
670
|
}
|
|
659
671
|
|
|
660
|
-
//
|
|
672
|
+
// "Soft" status badge — pill, 12/600, muted tone fill.
|
|
661
673
|
.status-label {
|
|
662
674
|
display: inline-flex;
|
|
663
675
|
align-items: center;
|
|
@@ -670,24 +682,40 @@ textarea.textarea:focus {
|
|
|
670
682
|
line-height: 1;
|
|
671
683
|
white-space: nowrap;
|
|
672
684
|
}
|
|
685
|
+
// Tones are SEMANTIC (success/warning/danger/info/neutral) — the same
|
|
686
|
+
// vocabulary .chip and .badge use — and derive from the same semantic ramp.
|
|
687
|
+
// The colour-named `.tone-*` classes are kept as deprecated aliases so older
|
|
688
|
+
// Razor call sites keep working; author new markup with the semantic names.
|
|
689
|
+
.status-label.success,
|
|
673
690
|
.status-label.tone-green {
|
|
674
|
-
background:
|
|
691
|
+
background: color-mix(in srgb, var(--color-success) 16%, transparent);
|
|
675
692
|
color: var(--color-success-strong);
|
|
676
693
|
}
|
|
694
|
+
.status-label.warning,
|
|
677
695
|
.status-label.tone-amber {
|
|
678
|
-
background:
|
|
696
|
+
background: color-mix(in srgb, var(--color-warning) 16%, transparent);
|
|
679
697
|
color: var(--color-warning-strong);
|
|
680
698
|
}
|
|
699
|
+
.status-label.danger,
|
|
681
700
|
.status-label.tone-red {
|
|
682
|
-
background:
|
|
701
|
+
background: color-mix(in srgb, var(--color-danger) 16%, transparent);
|
|
683
702
|
color: var(--color-danger-strong);
|
|
684
703
|
}
|
|
704
|
+
.status-label.info,
|
|
685
705
|
.status-label.tone-blue {
|
|
686
|
-
background:
|
|
706
|
+
background: color-mix(in srgb, var(--color-info) 16%, transparent);
|
|
687
707
|
color: var(--color-info-strong);
|
|
688
708
|
}
|
|
709
|
+
.status-label.primary {
|
|
710
|
+
background: var(--color-primary-soft);
|
|
711
|
+
color: var(--color-primary);
|
|
712
|
+
}
|
|
713
|
+
// Default/neutral — also the bare `.status-label` look, so an unknown or
|
|
714
|
+
// omitted tone still renders as a proper pill instead of transparent text.
|
|
715
|
+
.status-label,
|
|
716
|
+
.status-label.neutral,
|
|
689
717
|
.status-label.tone-grey {
|
|
690
|
-
background:
|
|
718
|
+
background: color-mix(in srgb, var(--color-text-subtle) 16%, transparent);
|
|
691
719
|
color: var(--color-text-subtle);
|
|
692
720
|
}
|
|
693
721
|
|
|
@@ -735,7 +763,7 @@ textarea.textarea:focus {
|
|
|
735
763
|
gap: 11px;
|
|
736
764
|
padding: 20px;
|
|
737
765
|
border: 1.5px solid var(--color-border);
|
|
738
|
-
border-radius:
|
|
766
|
+
border-radius: var(--radius);
|
|
739
767
|
background: var(--color-surface);
|
|
740
768
|
cursor: pointer;
|
|
741
769
|
transition:
|
|
@@ -743,10 +771,10 @@ textarea.textarea:focus {
|
|
|
743
771
|
background 0.12s ease;
|
|
744
772
|
}
|
|
745
773
|
.struct-card:hover {
|
|
746
|
-
border-color:
|
|
774
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
747
775
|
}
|
|
748
776
|
.struct-card.checked {
|
|
749
|
-
border-color:
|
|
777
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
750
778
|
background: color-mix(in srgb, var(--color-primary) 5%, transparent);
|
|
751
779
|
}
|
|
752
780
|
.struct-card.checked .struct-card-title {
|
|
@@ -893,7 +921,7 @@ textarea.textarea:focus {
|
|
|
893
921
|
}
|
|
894
922
|
.search-select-control.open {
|
|
895
923
|
background: var(--color-bg-muted);
|
|
896
|
-
border-color:
|
|
924
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
897
925
|
}
|
|
898
926
|
.search-select-caret {
|
|
899
927
|
flex-shrink: 0;
|
|
@@ -919,9 +947,9 @@ textarea.textarea:focus {
|
|
|
919
947
|
right: 0;
|
|
920
948
|
background: var(--color-surface);
|
|
921
949
|
border: 1px solid var(--color-border);
|
|
922
|
-
border-radius:
|
|
950
|
+
border-radius: var(--radius-lg);
|
|
923
951
|
corner-shape: squircle;
|
|
924
|
-
box-shadow:
|
|
952
|
+
box-shadow: var(--shadow-overlay);
|
|
925
953
|
padding: 4px;
|
|
926
954
|
overflow: hidden;
|
|
927
955
|
}
|
|
@@ -945,7 +973,7 @@ textarea.textarea:focus {
|
|
|
945
973
|
padding: 0 12px;
|
|
946
974
|
height: 40px;
|
|
947
975
|
border: 1px solid var(--color-border);
|
|
948
|
-
border-radius:
|
|
976
|
+
border-radius: var(--radius);
|
|
949
977
|
corner-shape: squircle;
|
|
950
978
|
background: var(--color-surface);
|
|
951
979
|
color: var(--color-text-subtle);
|
|
@@ -1001,7 +1029,7 @@ textarea.textarea:focus {
|
|
|
1001
1029
|
padding: 9px 14px;
|
|
1002
1030
|
line-height: 1.4;
|
|
1003
1031
|
border: none;
|
|
1004
|
-
border-radius:
|
|
1032
|
+
border-radius: var(--radius);
|
|
1005
1033
|
corner-shape: squircle;
|
|
1006
1034
|
background: transparent;
|
|
1007
1035
|
font-size: 14px;
|
|
@@ -1043,7 +1071,7 @@ textarea.textarea:focus {
|
|
|
1043
1071
|
}
|
|
1044
1072
|
.datepicker.open .datepicker-trigger {
|
|
1045
1073
|
background-color: var(--color-bg-muted);
|
|
1046
|
-
border-color:
|
|
1074
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
1047
1075
|
box-shadow: none;
|
|
1048
1076
|
}
|
|
1049
1077
|
// Portalled to <body> and fixed-positioned from JS so a modal's
|
|
@@ -1057,8 +1085,9 @@ textarea.textarea:focus {
|
|
|
1057
1085
|
padding: 18px;
|
|
1058
1086
|
background: var(--color-surface);
|
|
1059
1087
|
border: 1px solid var(--color-border);
|
|
1060
|
-
border-radius:
|
|
1088
|
+
border-radius: var(--radius-xl);
|
|
1061
1089
|
corner-shape: squircle;
|
|
1090
|
+
box-shadow: var(--shadow-overlay);
|
|
1062
1091
|
animation: datepicker-in 0.14s ease;
|
|
1063
1092
|
}
|
|
1064
1093
|
.datepicker-pop.drop-up {
|
|
@@ -1198,7 +1227,7 @@ textarea.textarea:focus {
|
|
|
1198
1227
|
border: none;
|
|
1199
1228
|
background: transparent;
|
|
1200
1229
|
padding: 6px 12px;
|
|
1201
|
-
border-radius:
|
|
1230
|
+
border-radius: var(--radius);
|
|
1202
1231
|
font-size: 13px;
|
|
1203
1232
|
font-weight: 600;
|
|
1204
1233
|
color: var(--color-primary);
|
|
@@ -1217,24 +1246,65 @@ textarea.textarea:focus {
|
|
|
1217
1246
|
.app-phone-number-input {
|
|
1218
1247
|
width: 100%;
|
|
1219
1248
|
}
|
|
1249
|
+
// One field, one border: the WRAPPER owns the chrome (same pattern as
|
|
1250
|
+
// .input-with-unit) so the dial-code picker and the number light up together.
|
|
1251
|
+
// The adornment used to be absolutely positioned over the input, which let the
|
|
1252
|
+
// placeholder run underneath it — masked only by the UA button's grey fill.
|
|
1220
1253
|
.app-phone-number-input .phone-input-wrap {
|
|
1221
|
-
|
|
1254
|
+
display: flex;
|
|
1255
|
+
align-items: center;
|
|
1222
1256
|
width: 100%;
|
|
1257
|
+
height: var(--wui-control-height);
|
|
1258
|
+
border: 1px solid var(--color-border);
|
|
1259
|
+
border-radius: var(--radius);
|
|
1260
|
+
corner-shape: squircle;
|
|
1261
|
+
background: var(--color-surface);
|
|
1262
|
+
transition:
|
|
1263
|
+
border-color 0.12s,
|
|
1264
|
+
background-color 0.12s;
|
|
1223
1265
|
}
|
|
1224
|
-
.app-phone-number-input .phone-input-wrap
|
|
1225
|
-
|
|
1266
|
+
.app-phone-number-input .phone-input-wrap:hover:not(:focus-within) {
|
|
1267
|
+
background: var(--color-bg-muted);
|
|
1268
|
+
border-color: var(--color-border-strong);
|
|
1226
1269
|
}
|
|
1227
|
-
.app-phone-number-input .phone-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1270
|
+
.app-phone-number-input .phone-input-wrap:focus-within {
|
|
1271
|
+
background: var(--color-bg-muted);
|
|
1272
|
+
border-color: var(--color-primary);
|
|
1273
|
+
}
|
|
1274
|
+
.app-phone-number-input .phone-input-wrap:has(> .input:disabled) {
|
|
1275
|
+
background: var(--color-bg-muted);
|
|
1276
|
+
cursor: not-allowed;
|
|
1277
|
+
}
|
|
1278
|
+
.app-phone-number-input .phone-input-wrap:has(.input.error),
|
|
1279
|
+
.app-phone-number-input .phone-input-wrap:has(.input.is-invalid) {
|
|
1280
|
+
border-color: color-mix(in srgb, var(--color-danger) 45%, var(--color-surface));
|
|
1281
|
+
}
|
|
1282
|
+
// The inner control drops its own chrome — the shell carries it.
|
|
1283
|
+
.app-phone-number-input .phone-input-wrap > .input,
|
|
1284
|
+
.app-phone-number-input .phone-input-wrap > .input:hover,
|
|
1285
|
+
.app-phone-number-input .phone-input-wrap > .input:focus {
|
|
1286
|
+
flex: 1 1 auto;
|
|
1287
|
+
min-width: 0;
|
|
1288
|
+
width: auto;
|
|
1231
1289
|
height: 100%;
|
|
1290
|
+
border: 0;
|
|
1291
|
+
background: transparent;
|
|
1292
|
+
box-shadow: none;
|
|
1293
|
+
}
|
|
1294
|
+
// `relative` so the menu anchors here (it is NOT positioned by JS — it used to
|
|
1295
|
+
// rely on the adornment being absolute, which is why it appeared to float).
|
|
1296
|
+
.app-phone-number-input .phone-country-adornment {
|
|
1297
|
+
position: relative;
|
|
1298
|
+
flex: none;
|
|
1232
1299
|
display: flex;
|
|
1233
1300
|
align-items: center;
|
|
1234
1301
|
padding-left: 12px;
|
|
1235
1302
|
z-index: 5;
|
|
1236
1303
|
}
|
|
1237
|
-
|
|
1304
|
+
// NOT scoped under `.country-dropdown` — <wui-phone> renders the toggle inside
|
|
1305
|
+
// `.phone-country-adornment`, with no such wrapper. Scoping it there left the
|
|
1306
|
+
// button on raw UA styling (2px outset border + grey fill) inside the field.
|
|
1307
|
+
.country-toggle {
|
|
1238
1308
|
display: flex;
|
|
1239
1309
|
align-items: center;
|
|
1240
1310
|
gap: 6px;
|
|
@@ -1243,29 +1313,43 @@ textarea.textarea:focus {
|
|
|
1243
1313
|
background: transparent;
|
|
1244
1314
|
box-shadow: none;
|
|
1245
1315
|
color: var(--color-text);
|
|
1316
|
+
cursor: pointer;
|
|
1317
|
+
font: inherit;
|
|
1318
|
+
line-height: 1;
|
|
1246
1319
|
}
|
|
1247
|
-
.country-
|
|
1320
|
+
.country-toggle::after {
|
|
1248
1321
|
display: none;
|
|
1249
1322
|
}
|
|
1250
|
-
.country-
|
|
1251
|
-
.country-
|
|
1252
|
-
.country-
|
|
1253
|
-
background: transparent
|
|
1254
|
-
box-shadow: none
|
|
1255
|
-
color: var(--color-text)
|
|
1323
|
+
.country-toggle:hover,
|
|
1324
|
+
.country-toggle:focus,
|
|
1325
|
+
.country-toggle:active {
|
|
1326
|
+
background: transparent;
|
|
1327
|
+
box-shadow: none;
|
|
1328
|
+
color: var(--color-text);
|
|
1256
1329
|
}
|
|
1257
|
-
.
|
|
1258
|
-
|
|
1259
|
-
height: 15px;
|
|
1260
|
-
border-radius: 2px;
|
|
1261
|
-
object-fit: cover;
|
|
1262
|
-
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.06);
|
|
1330
|
+
.country-toggle:hover .phone-chevron {
|
|
1331
|
+
color: var(--color-text);
|
|
1263
1332
|
}
|
|
1333
|
+
.country-toggle:focus {
|
|
1334
|
+
outline: none;
|
|
1335
|
+
}
|
|
1336
|
+
.country-toggle:focus-visible {
|
|
1337
|
+
outline: 2px solid var(--color-primary);
|
|
1338
|
+
outline-offset: 2px;
|
|
1339
|
+
border-radius: var(--radius-sm);
|
|
1340
|
+
}
|
|
1341
|
+
.country-toggle:disabled {
|
|
1342
|
+
cursor: not-allowed;
|
|
1343
|
+
opacity: 0.6;
|
|
1344
|
+
}
|
|
1345
|
+
// Flag glyphs come from the generated .wui-flag-* classes (_flags.scss).
|
|
1264
1346
|
.app-phone-number-input .phone-chevron {
|
|
1265
1347
|
color: var(--color-text-muted);
|
|
1266
1348
|
transition: transform 0.2s ease;
|
|
1267
1349
|
}
|
|
1268
|
-
|
|
1350
|
+
// Keyed off the element's own ARIA state — <wui-phone> toggles aria-expanded,
|
|
1351
|
+
// it never adds react-bootstrap's `.show`.
|
|
1352
|
+
.country-toggle[aria-expanded="true"] .phone-chevron {
|
|
1269
1353
|
transform: rotate(180deg);
|
|
1270
1354
|
}
|
|
1271
1355
|
.app-phone-number-input .phone-dial {
|
|
@@ -1274,34 +1358,48 @@ textarea.textarea:focus {
|
|
|
1274
1358
|
color: var(--color-text);
|
|
1275
1359
|
}
|
|
1276
1360
|
|
|
1361
|
+
// Menu, search field and rows all share the .search-select language so the
|
|
1362
|
+
// country picker reads as the same component family as every other picker.
|
|
1363
|
+
// Fixed, with top/left set from the trigger rect by <wui-phone>, so an
|
|
1364
|
+
// overflow-hidden card/modal/table wrapper can't clip the list. Sits above the
|
|
1365
|
+
// modal layer, matching .search-select-menu-fixed and .datepicker-pop.
|
|
1277
1366
|
.country-dropdown-menu {
|
|
1367
|
+
position: fixed;
|
|
1368
|
+
z-index: 1600;
|
|
1278
1369
|
width: 320px;
|
|
1279
|
-
|
|
1370
|
+
max-width: calc(100vw - 16px);
|
|
1371
|
+
padding: 4px;
|
|
1280
1372
|
border: 1px solid var(--color-border);
|
|
1281
|
-
border-radius:
|
|
1373
|
+
border-radius: var(--radius-lg);
|
|
1374
|
+
corner-shape: squircle;
|
|
1282
1375
|
background: var(--color-surface);
|
|
1283
|
-
box-shadow:
|
|
1376
|
+
box-shadow: var(--shadow-overlay);
|
|
1284
1377
|
overflow: hidden;
|
|
1285
1378
|
}
|
|
1286
1379
|
.dropdown-search-wrapper {
|
|
1287
1380
|
position: relative;
|
|
1288
|
-
|
|
1381
|
+
margin: 4px;
|
|
1289
1382
|
}
|
|
1290
1383
|
.dropdown-search-icon {
|
|
1291
1384
|
position: absolute;
|
|
1292
|
-
left:
|
|
1293
|
-
top:
|
|
1385
|
+
left: 12px;
|
|
1386
|
+
top: 50%;
|
|
1294
1387
|
transform: translateY(-50%);
|
|
1295
1388
|
display: flex;
|
|
1296
|
-
color: var(--color-text-
|
|
1389
|
+
color: var(--color-text-subtle);
|
|
1297
1390
|
pointer-events: none;
|
|
1391
|
+
transition: color 0.12s ease;
|
|
1392
|
+
}
|
|
1393
|
+
.dropdown-search-wrapper:focus-within .dropdown-search-icon {
|
|
1394
|
+
color: var(--color-primary);
|
|
1298
1395
|
}
|
|
1299
1396
|
.dropdown-search-input {
|
|
1300
1397
|
width: 100%;
|
|
1301
|
-
height:
|
|
1398
|
+
height: 40px;
|
|
1302
1399
|
padding: 0 12px 0 36px;
|
|
1303
1400
|
border: 1px solid var(--color-border);
|
|
1304
|
-
border-radius:
|
|
1401
|
+
border-radius: var(--radius);
|
|
1402
|
+
corner-shape: squircle;
|
|
1305
1403
|
font-size: 14px;
|
|
1306
1404
|
color: var(--color-text);
|
|
1307
1405
|
background: var(--color-surface);
|
|
@@ -1311,47 +1409,58 @@ textarea.textarea:focus {
|
|
|
1311
1409
|
background: var(--color-bg-muted);
|
|
1312
1410
|
border-color: var(--color-border-strong);
|
|
1313
1411
|
}
|
|
1412
|
+
// Border is the focus cue everywhere else in the system — no glow ring here.
|
|
1314
1413
|
.dropdown-search-input:focus {
|
|
1315
1414
|
background: var(--color-bg-muted);
|
|
1316
1415
|
border-color: var(--color-primary);
|
|
1317
|
-
box-shadow: 0 0 0 3px var(--color-primary-soft);
|
|
1318
1416
|
}
|
|
1319
1417
|
.country-list-scroll {
|
|
1320
1418
|
max-height: 250px;
|
|
1321
1419
|
overflow-y: auto;
|
|
1322
1420
|
overflow-x: hidden;
|
|
1421
|
+
padding: 4px;
|
|
1422
|
+
display: flex;
|
|
1423
|
+
flex-direction: column;
|
|
1424
|
+
gap: 4px;
|
|
1323
1425
|
scrollbar-width: thin;
|
|
1426
|
+
scrollbar-gutter: stable;
|
|
1324
1427
|
}
|
|
1325
1428
|
.country-list-scroll::-webkit-scrollbar {
|
|
1326
1429
|
width: 6px;
|
|
1327
1430
|
}
|
|
1328
1431
|
.country-list-scroll::-webkit-scrollbar-thumb {
|
|
1329
1432
|
background: var(--color-border);
|
|
1330
|
-
border-radius:
|
|
1433
|
+
border-radius: 999px;
|
|
1331
1434
|
}
|
|
1435
|
+
// Rounded, inset rows with a primary-tinted hover — identical to
|
|
1436
|
+
// .search-select-option. (!important is required: react-bootstrap's
|
|
1437
|
+
// .dropdown-item ships its own colour/padding at equal specificity.)
|
|
1332
1438
|
.country-dropdown-item {
|
|
1333
1439
|
display: flex !important;
|
|
1334
1440
|
align-items: center;
|
|
1335
1441
|
gap: 10px;
|
|
1336
|
-
padding: 9px
|
|
1442
|
+
padding: 9px 14px !important;
|
|
1443
|
+
border-radius: var(--radius) !important;
|
|
1444
|
+
corner-shape: squircle;
|
|
1337
1445
|
cursor: pointer;
|
|
1338
1446
|
color: var(--color-text) !important;
|
|
1339
1447
|
background: transparent !important;
|
|
1340
1448
|
border: 0 !important;
|
|
1449
|
+
transition:
|
|
1450
|
+
background 0.12s ease,
|
|
1451
|
+
color 0.12s ease;
|
|
1341
1452
|
}
|
|
1342
1453
|
.country-dropdown-item:hover,
|
|
1343
1454
|
.country-dropdown-item:focus {
|
|
1344
|
-
background: var(--color-
|
|
1455
|
+
background: var(--color-primary-soft) !important;
|
|
1456
|
+
color: var(--color-primary) !important;
|
|
1345
1457
|
}
|
|
1346
1458
|
.country-dropdown-item.selected {
|
|
1347
1459
|
background: var(--color-primary-soft) !important;
|
|
1460
|
+
color: var(--color-primary) !important;
|
|
1461
|
+
font-weight: 500;
|
|
1348
1462
|
}
|
|
1349
|
-
.country-dropdown-menu .
|
|
1350
|
-
width: 22px;
|
|
1351
|
-
height: 15px;
|
|
1352
|
-
border-radius: 2px;
|
|
1353
|
-
object-fit: cover;
|
|
1354
|
-
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
|
1463
|
+
.country-dropdown-menu .wui-flag {
|
|
1355
1464
|
flex-shrink: 0;
|
|
1356
1465
|
}
|
|
1357
1466
|
.country-name-text {
|
|
@@ -1364,6 +1473,15 @@ textarea.textarea:focus {
|
|
|
1364
1473
|
font-weight: 400;
|
|
1365
1474
|
color: var(--color-text-muted);
|
|
1366
1475
|
}
|
|
1476
|
+
// On a tinted row the label must ride the row colour, not stay grey.
|
|
1477
|
+
.country-dropdown-item:hover .country-name-text,
|
|
1478
|
+
.country-dropdown-item:focus .country-name-text,
|
|
1479
|
+
.country-dropdown-item.selected .country-name-text,
|
|
1480
|
+
.country-dropdown-item:hover .country-code-text,
|
|
1481
|
+
.country-dropdown-item:focus .country-code-text,
|
|
1482
|
+
.country-dropdown-item.selected .country-code-text {
|
|
1483
|
+
color: currentColor;
|
|
1484
|
+
}
|
|
1367
1485
|
.selected-check-icon {
|
|
1368
1486
|
margin-left: auto;
|
|
1369
1487
|
display: flex;
|
|
@@ -1423,7 +1541,7 @@ textarea.textarea:focus {
|
|
|
1423
1541
|
gap: 12px;
|
|
1424
1542
|
padding: 12px 14px;
|
|
1425
1543
|
border: 1px dashed var(--color-border);
|
|
1426
|
-
border-radius:
|
|
1544
|
+
border-radius: var(--radius-lg);
|
|
1427
1545
|
background: var(--color-surface);
|
|
1428
1546
|
cursor: pointer;
|
|
1429
1547
|
transition:
|
|
@@ -1480,7 +1598,7 @@ textarea.textarea:focus {
|
|
|
1480
1598
|
.attach-preview {
|
|
1481
1599
|
width: 100%;
|
|
1482
1600
|
border: 1px solid var(--color-border);
|
|
1483
|
-
border-radius:
|
|
1601
|
+
border-radius: var(--radius-lg);
|
|
1484
1602
|
background: var(--color-surface);
|
|
1485
1603
|
overflow: hidden;
|
|
1486
1604
|
}
|
|
@@ -1566,7 +1684,7 @@ textarea.textarea:focus {
|
|
|
1566
1684
|
display: flex;
|
|
1567
1685
|
align-items: stretch;
|
|
1568
1686
|
width: 100%;
|
|
1569
|
-
border-radius:
|
|
1687
|
+
border-radius: var(--radius-lg);
|
|
1570
1688
|
corner-shape: squircle;
|
|
1571
1689
|
border: 1px solid var(--color-border);
|
|
1572
1690
|
background: var(--color-surface);
|
|
@@ -1679,7 +1797,7 @@ textarea.textarea:focus {
|
|
|
1679
1797
|
display: block;
|
|
1680
1798
|
width: 100%;
|
|
1681
1799
|
height: 350px;
|
|
1682
|
-
border-radius:
|
|
1800
|
+
border-radius: var(--radius-lg);
|
|
1683
1801
|
corner-shape: squircle;
|
|
1684
1802
|
border: 1px solid var(--color-border);
|
|
1685
1803
|
overflow: hidden;
|
|
@@ -1746,7 +1864,7 @@ textarea.textarea:focus {
|
|
|
1746
1864
|
font-size: 14px;
|
|
1747
1865
|
font-weight: 600;
|
|
1748
1866
|
border-radius: 999px;
|
|
1749
|
-
box-shadow:
|
|
1867
|
+
box-shadow: var(--shadow-overlay);
|
|
1750
1868
|
}
|
|
1751
1869
|
.file-preview-remove {
|
|
1752
1870
|
position: absolute;
|
|
@@ -2018,7 +2136,7 @@ textarea.textarea:focus {
|
|
|
2018
2136
|
place-items: center;
|
|
2019
2137
|
width: 30px;
|
|
2020
2138
|
height: 30px;
|
|
2021
|
-
border-radius:
|
|
2139
|
+
border-radius: var(--radius);
|
|
2022
2140
|
corner-shape: squircle;
|
|
2023
2141
|
background: var(--color-primary-soft);
|
|
2024
2142
|
color: var(--color-primary);
|
|
@@ -2035,16 +2153,215 @@ textarea.textarea:focus {
|
|
|
2035
2153
|
padding: 14px 16px 20px;
|
|
2036
2154
|
}
|
|
2037
2155
|
|
|
2156
|
+
// ── Tabs ──
|
|
2157
|
+
// The tab strip both surfaces render. React drives it as a controlled strip and
|
|
2158
|
+
// Razor additionally renders Bootstrap panels beneath it, but the strip markup —
|
|
2159
|
+
// and therefore the look — is identical.
|
|
2160
|
+
.app-tabs {
|
|
2161
|
+
display: flex;
|
|
2162
|
+
align-items: center;
|
|
2163
|
+
gap: 2px;
|
|
2164
|
+
border-bottom: 1px solid var(--color-border);
|
|
2165
|
+
overflow-x: auto;
|
|
2166
|
+
scrollbar-width: none;
|
|
2167
|
+
// Declared explicitly because the React AppTabs renders a react-bootstrap
|
|
2168
|
+
// <Nav>, so the element also carries `.nav` — whose `flex-wrap: wrap` would
|
|
2169
|
+
// otherwise apply unopposed (equal specificity, and we declared nothing),
|
|
2170
|
+
// wrapping the tabs onto a second line instead of scrolling.
|
|
2171
|
+
flex-wrap: nowrap;
|
|
2172
|
+
}
|
|
2173
|
+
.app-tabs::-webkit-scrollbar {
|
|
2174
|
+
display: none;
|
|
2175
|
+
}
|
|
2176
|
+
.app-tab {
|
|
2177
|
+
display: inline-flex;
|
|
2178
|
+
align-items: center;
|
|
2179
|
+
gap: 8px;
|
|
2180
|
+
padding: 10px 14px;
|
|
2181
|
+
border: 0;
|
|
2182
|
+
// Sits on the strip's own hairline rather than adding a second one.
|
|
2183
|
+
border-bottom: 2px solid transparent;
|
|
2184
|
+
margin-bottom: -1px;
|
|
2185
|
+
background: transparent;
|
|
2186
|
+
font-family: inherit;
|
|
2187
|
+
font-size: 14px;
|
|
2188
|
+
font-weight: 500;
|
|
2189
|
+
line-height: 1.4;
|
|
2190
|
+
color: var(--color-text-muted);
|
|
2191
|
+
white-space: nowrap;
|
|
2192
|
+
cursor: pointer;
|
|
2193
|
+
transition:
|
|
2194
|
+
color 0.12s ease,
|
|
2195
|
+
border-color 0.12s ease;
|
|
2196
|
+
}
|
|
2197
|
+
.app-tab:hover:not(:disabled):not(.active) {
|
|
2198
|
+
color: var(--color-text);
|
|
2199
|
+
}
|
|
2200
|
+
.app-tab.active {
|
|
2201
|
+
color: var(--color-primary);
|
|
2202
|
+
border-bottom-color: var(--color-primary);
|
|
2203
|
+
// The active tab carries weight as well as colour — colour alone is a weak
|
|
2204
|
+
// signal for anyone who reads the strip at a glance or cannot distinguish it.
|
|
2205
|
+
font-weight: 600;
|
|
2206
|
+
}
|
|
2207
|
+
.app-tab:disabled {
|
|
2208
|
+
color: var(--color-text-subtle);
|
|
2209
|
+
cursor: not-allowed;
|
|
2210
|
+
}
|
|
2211
|
+
.app-tab:focus-visible {
|
|
2212
|
+
outline: 2px solid var(--color-primary);
|
|
2213
|
+
outline-offset: -2px;
|
|
2214
|
+
border-radius: var(--radius-sm);
|
|
2215
|
+
}
|
|
2216
|
+
// A destructive tab reads as destructive whether or not it is the active one.
|
|
2217
|
+
.app-tab.danger {
|
|
2218
|
+
color: var(--color-danger);
|
|
2219
|
+
}
|
|
2220
|
+
.app-tab.danger:hover:not(:disabled):not(.active) {
|
|
2221
|
+
color: var(--color-danger-active);
|
|
2222
|
+
}
|
|
2223
|
+
.app-tab.danger.active {
|
|
2224
|
+
color: var(--color-danger);
|
|
2225
|
+
border-bottom-color: var(--color-danger);
|
|
2226
|
+
}
|
|
2227
|
+
.app-tab.danger .app-tab-count {
|
|
2228
|
+
background: var(--color-danger-soft);
|
|
2229
|
+
color: var(--color-danger-strong);
|
|
2230
|
+
}
|
|
2231
|
+
// Trailing count pill.
|
|
2232
|
+
.app-tab-count {
|
|
2233
|
+
display: inline-flex;
|
|
2234
|
+
align-items: center;
|
|
2235
|
+
justify-content: center;
|
|
2236
|
+
min-width: 20px;
|
|
2237
|
+
padding: 0 6px;
|
|
2238
|
+
height: 20px;
|
|
2239
|
+
border-radius: 999px;
|
|
2240
|
+
background: var(--color-bg-muted);
|
|
2241
|
+
color: var(--color-text-subtle);
|
|
2242
|
+
font-size: 12px;
|
|
2243
|
+
font-weight: 600;
|
|
2244
|
+
}
|
|
2245
|
+
.app-tab.active .app-tab-count {
|
|
2246
|
+
background: var(--color-primary-soft);
|
|
2247
|
+
color: var(--color-primary);
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
// Wrapper for a self-contained tabs widget (strip + panels), as <app-tabs>
|
|
2251
|
+
// renders server-side. React composes the strip and its panels itself.
|
|
2252
|
+
.app-tabs-group > .tab-content {
|
|
2253
|
+
padding-top: 16px;
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
// Pills variant — a segmented control rather than an underline.
|
|
2257
|
+
.app-tabs-pills {
|
|
2258
|
+
gap: 4px;
|
|
2259
|
+
border-bottom: 0;
|
|
2260
|
+
}
|
|
2261
|
+
.app-tabs-pills .app-tab {
|
|
2262
|
+
border-bottom: 0;
|
|
2263
|
+
margin-bottom: 0;
|
|
2264
|
+
border-radius: var(--radius);
|
|
2265
|
+
}
|
|
2266
|
+
.app-tabs-pills .app-tab.active {
|
|
2267
|
+
background: var(--color-primary-soft);
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
// ── Search field ──
|
|
2271
|
+
// The search control both surfaces render: a rounded field with a leading
|
|
2272
|
+
// magnifier that takes the brand colour on focus, plus a clear button that
|
|
2273
|
+
// appears once there's a value.
|
|
2274
|
+
.search-wrap {
|
|
2275
|
+
position: relative;
|
|
2276
|
+
display: flex;
|
|
2277
|
+
align-items: center;
|
|
2278
|
+
width: 100%;
|
|
2279
|
+
}
|
|
2280
|
+
.search-wrap > .search-icon {
|
|
2281
|
+
position: absolute;
|
|
2282
|
+
left: 13px;
|
|
2283
|
+
top: 50%;
|
|
2284
|
+
transform: translateY(-50%);
|
|
2285
|
+
display: grid;
|
|
2286
|
+
place-items: center;
|
|
2287
|
+
color: var(--color-text-subtle);
|
|
2288
|
+
pointer-events: none;
|
|
2289
|
+
transition: color 0.12s ease;
|
|
2290
|
+
}
|
|
2291
|
+
.search-wrap:focus-within > .search-icon {
|
|
2292
|
+
color: var(--color-primary);
|
|
2293
|
+
}
|
|
2294
|
+
.search-field,
|
|
2295
|
+
.search-input {
|
|
2296
|
+
width: 100%;
|
|
2297
|
+
padding-left: 40px;
|
|
2298
|
+
padding-right: 36px;
|
|
2299
|
+
}
|
|
2300
|
+
.search-clear {
|
|
2301
|
+
position: absolute;
|
|
2302
|
+
right: 8px;
|
|
2303
|
+
top: 50%;
|
|
2304
|
+
transform: translateY(-50%);
|
|
2305
|
+
display: grid;
|
|
2306
|
+
place-items: center;
|
|
2307
|
+
width: 26px;
|
|
2308
|
+
height: 26px;
|
|
2309
|
+
padding: 0;
|
|
2310
|
+
border: 0;
|
|
2311
|
+
border-radius: 999px;
|
|
2312
|
+
background: transparent;
|
|
2313
|
+
color: var(--color-text-subtle);
|
|
2314
|
+
cursor: pointer;
|
|
2315
|
+
opacity: 0;
|
|
2316
|
+
pointer-events: none;
|
|
2317
|
+
transition:
|
|
2318
|
+
opacity 0.12s ease,
|
|
2319
|
+
color 0.12s ease;
|
|
2320
|
+
}
|
|
2321
|
+
.search-wrap.has-value .search-clear,
|
|
2322
|
+
.search-wrap:focus-within .search-clear {
|
|
2323
|
+
opacity: 1;
|
|
2324
|
+
pointer-events: auto;
|
|
2325
|
+
}
|
|
2326
|
+
.search-clear:hover {
|
|
2327
|
+
color: var(--color-text);
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2038
2330
|
// ── Empty state ──
|
|
2039
2331
|
.empty-state {
|
|
2040
2332
|
text-align: center;
|
|
2041
2333
|
padding: 48px 24px;
|
|
2042
2334
|
color: var(--color-text-subtle);
|
|
2043
2335
|
}
|
|
2336
|
+
.empty-state-title {
|
|
2337
|
+
font-size: 16px;
|
|
2338
|
+
font-weight: 600;
|
|
2339
|
+
line-height: 1.35;
|
|
2340
|
+
color: var(--color-text);
|
|
2341
|
+
}
|
|
2342
|
+
.empty-state-message {
|
|
2343
|
+
margin: 6px 0 0;
|
|
2344
|
+
font-size: 14px;
|
|
2345
|
+
line-height: 1.5;
|
|
2346
|
+
color: var(--color-text-muted);
|
|
2347
|
+
}
|
|
2348
|
+
.empty-state-action {
|
|
2349
|
+
margin-top: 16px;
|
|
2350
|
+
}
|
|
2351
|
+
// Tighter scale for a widget/tile, as opposed to a full card or page.
|
|
2352
|
+
.empty-state-sm {
|
|
2353
|
+
padding: 28px 16px;
|
|
2354
|
+
}
|
|
2355
|
+
.empty-state-sm .empty-state-title {
|
|
2356
|
+
font-size: 14px;
|
|
2357
|
+
}
|
|
2358
|
+
.empty-state-sm .empty-state-message {
|
|
2359
|
+
font-size: 12px;
|
|
2360
|
+
}
|
|
2044
2361
|
.empty-state-icon {
|
|
2045
2362
|
width: 48px;
|
|
2046
2363
|
height: 48px;
|
|
2047
|
-
border-radius:
|
|
2364
|
+
border-radius: var(--radius-lg);
|
|
2048
2365
|
background: var(--color-primary-soft);
|
|
2049
2366
|
color: var(--color-primary);
|
|
2050
2367
|
display: inline-flex;
|
|
@@ -2109,6 +2426,76 @@ textarea.textarea:focus {
|
|
|
2109
2426
|
background: transparent;
|
|
2110
2427
|
}
|
|
2111
2428
|
|
|
2429
|
+
// ── Table (base) ──
|
|
2430
|
+
// The shared data-table look, on the plain `.table` class Bootstrap already
|
|
2431
|
+
// gives every table. Without this a product's tables fall back to stock
|
|
2432
|
+
// Bootstrap and drift from the design system; `.table-ledger` below is the
|
|
2433
|
+
// denser, card-inset variant, not a replacement for having a base.
|
|
2434
|
+
.table {
|
|
2435
|
+
--bs-table-bg: transparent;
|
|
2436
|
+
--bs-table-color: var(--color-text);
|
|
2437
|
+
width: 100%;
|
|
2438
|
+
margin-bottom: 0;
|
|
2439
|
+
color: var(--color-text);
|
|
2440
|
+
vertical-align: middle;
|
|
2441
|
+
border-color: var(--color-border);
|
|
2442
|
+
// `separate` is required for a cell to render a border-radius at all —
|
|
2443
|
+
// Bootstrap reboot sets `collapse`, under which .table-ledger's rounded
|
|
2444
|
+
// header corners silently do nothing. border-spacing 0 keeps cells flush.
|
|
2445
|
+
border-collapse: separate;
|
|
2446
|
+
border-spacing: 0;
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
// The cell rules are wrapped in :where() so they carry ZERO class specificity
|
|
2450
|
+
// — `:where(.table) > thead > tr > th` is (0,0,3), which any single-class
|
|
2451
|
+
// variant selector such as `.table-ledger thead th` (0,1,2) outranks.
|
|
2452
|
+
//
|
|
2453
|
+
// Without this the base out-ranks its own variant: written plainly,
|
|
2454
|
+
// `.table > thead > tr > th` is (0,1,3) and beats `.table-ledger thead th`,
|
|
2455
|
+
// so the ledger lost its own border-bottom/line-height/font-family to the
|
|
2456
|
+
// base. A base layer must always sit *under* the variants built on it.
|
|
2457
|
+
:where(.table) > thead > tr > th {
|
|
2458
|
+
height: 42px;
|
|
2459
|
+
padding: 0 12px;
|
|
2460
|
+
background: var(--color-bg-muted);
|
|
2461
|
+
border-bottom: 1px solid var(--color-border);
|
|
2462
|
+
font-size: 12px;
|
|
2463
|
+
font-weight: 500;
|
|
2464
|
+
letter-spacing: 0.04em;
|
|
2465
|
+
text-transform: uppercase;
|
|
2466
|
+
color: var(--color-text-subtle);
|
|
2467
|
+
white-space: nowrap;
|
|
2468
|
+
vertical-align: middle;
|
|
2469
|
+
transition:
|
|
2470
|
+
background-color 0.12s ease,
|
|
2471
|
+
color 0.12s ease;
|
|
2472
|
+
}
|
|
2473
|
+
:where(.table) > tbody > tr > td {
|
|
2474
|
+
padding: 12px;
|
|
2475
|
+
border-bottom: 1px solid var(--color-border);
|
|
2476
|
+
font-size: 14px;
|
|
2477
|
+
color: var(--color-text-muted);
|
|
2478
|
+
vertical-align: middle;
|
|
2479
|
+
transition: background-color 0.12s ease;
|
|
2480
|
+
}
|
|
2481
|
+
:where(.table) > tbody > tr:last-child > td {
|
|
2482
|
+
border-bottom: 0;
|
|
2483
|
+
}
|
|
2484
|
+
:where(.table) > tbody > tr:hover > td {
|
|
2485
|
+
background: var(--color-bg-subtle);
|
|
2486
|
+
}
|
|
2487
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2488
|
+
:where(.table) > thead > tr > th,
|
|
2489
|
+
:where(.table) > tbody > tr > td {
|
|
2490
|
+
transition: none;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
// Numeric / action columns align to the trailing edge.
|
|
2494
|
+
.table .cell-end,
|
|
2495
|
+
.table thead th.cell-end {
|
|
2496
|
+
text-align: right;
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2112
2499
|
// ── Table + pagination ──
|
|
2113
2500
|
// DataTable cell helpers
|
|
2114
2501
|
.table-ledger .lx-cell-end,
|
|
@@ -2141,11 +2528,11 @@ textarea.textarea:focus {
|
|
|
2141
2528
|
}
|
|
2142
2529
|
.table-ledger thead th:first-child {
|
|
2143
2530
|
padding-left: 20px;
|
|
2144
|
-
border-radius:
|
|
2531
|
+
border-radius: var(--radius-sm) 0 0 var(--radius-sm);
|
|
2145
2532
|
}
|
|
2146
2533
|
.table-ledger thead th:last-child {
|
|
2147
2534
|
padding-right: 20px;
|
|
2148
|
-
border-radius: 0
|
|
2535
|
+
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
2149
2536
|
}
|
|
2150
2537
|
.table-ledger tbody td {
|
|
2151
2538
|
height: 54px;
|
|
@@ -2191,7 +2578,9 @@ textarea.textarea:focus {
|
|
|
2191
2578
|
.table-ledger a.u-name:hover {
|
|
2192
2579
|
text-decoration: none;
|
|
2193
2580
|
}
|
|
2194
|
-
// Identity-cell subline.
|
|
2581
|
+
// Identity-cell subline. `.u-email` is in the group because an identity cell
|
|
2582
|
+
// renders the secondary line as either a generic .lx-text-xs or a .u-email.
|
|
2583
|
+
.table-ledger .u-meta > .u-email,
|
|
2195
2584
|
.table-ledger .u-meta > .lx-text-xs {
|
|
2196
2585
|
font-size: 12px;
|
|
2197
2586
|
font-weight: 400;
|
|
@@ -2247,7 +2636,7 @@ textarea.textarea:focus {
|
|
|
2247
2636
|
padding: 0 28px 0 12px;
|
|
2248
2637
|
font-size: 13px;
|
|
2249
2638
|
font-weight: 600;
|
|
2250
|
-
border-radius:
|
|
2639
|
+
border-radius: var(--radius);
|
|
2251
2640
|
background-position: right 8px center;
|
|
2252
2641
|
background-size: 14px;
|
|
2253
2642
|
}
|
|
@@ -2321,8 +2710,33 @@ textarea.textarea:focus {
|
|
|
2321
2710
|
padding-left: 12px;
|
|
2322
2711
|
padding-right: 0;
|
|
2323
2712
|
}
|
|
2324
|
-
|
|
2325
|
-
|
|
2713
|
+
// Selection is a brand-tinted state on the CELLS, not a grey fill on the row:
|
|
2714
|
+
// cells carry their own background (hover sets it too), so a background on the
|
|
2715
|
+
// <tr> is painted over and never shows. Grey also reads as "disabled" rather
|
|
2716
|
+
// than "chosen".
|
|
2717
|
+
.table-ledger tbody tr.is-selected > td {
|
|
2718
|
+
background: var(--color-primary-soft);
|
|
2719
|
+
}
|
|
2720
|
+
.table-ledger tbody tr.is-selected:hover > td {
|
|
2721
|
+
background: color-mix(in srgb, var(--color-primary) 14%, transparent);
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
// Row status affordance — a colour bar on the leading edge, so a row's state is
|
|
2725
|
+
// legible without relying on a tint alone.
|
|
2726
|
+
.table-ledger tbody tr[data-status] > td:first-child {
|
|
2727
|
+
box-shadow: inset 3px 0 0 0 var(--color-border-strong);
|
|
2728
|
+
}
|
|
2729
|
+
.table-ledger tbody tr[data-status="success"] > td:first-child {
|
|
2730
|
+
box-shadow: inset 3px 0 0 0 var(--color-success);
|
|
2731
|
+
}
|
|
2732
|
+
.table-ledger tbody tr[data-status="warning"] > td:first-child {
|
|
2733
|
+
box-shadow: inset 3px 0 0 0 var(--color-warning);
|
|
2734
|
+
}
|
|
2735
|
+
.table-ledger tbody tr[data-status="danger"] > td:first-child {
|
|
2736
|
+
box-shadow: inset 3px 0 0 0 var(--color-danger);
|
|
2737
|
+
}
|
|
2738
|
+
.table-ledger tbody tr[data-status="info"] > td:first-child {
|
|
2739
|
+
box-shadow: inset 3px 0 0 0 var(--color-info);
|
|
2326
2740
|
}
|
|
2327
2741
|
|
|
2328
2742
|
// Row actions — icon-only buttons replacing a kebab dropdown.
|
|
@@ -2384,27 +2798,27 @@ textarea.textarea:focus {
|
|
|
2384
2798
|
}
|
|
2385
2799
|
.u-action.u-action-preview {
|
|
2386
2800
|
background: var(--color-primary-soft);
|
|
2387
|
-
color:
|
|
2801
|
+
color: var(--color-primary);
|
|
2388
2802
|
}
|
|
2389
2803
|
.u-action.u-action-download {
|
|
2390
2804
|
background: var(--color-success-soft);
|
|
2391
|
-
color:
|
|
2805
|
+
color: var(--color-success-active);
|
|
2392
2806
|
}
|
|
2393
2807
|
.u-action.u-action-share {
|
|
2394
2808
|
background: var(--color-warning-soft);
|
|
2395
|
-
color:
|
|
2809
|
+
color: var(--color-warning-active);
|
|
2396
2810
|
}
|
|
2397
2811
|
.u-action.u-action-preview:hover:not(:disabled) {
|
|
2398
2812
|
background: var(--color-primary-soft);
|
|
2399
|
-
color:
|
|
2813
|
+
color: var(--color-primary);
|
|
2400
2814
|
}
|
|
2401
2815
|
.u-action.u-action-download:hover:not(:disabled) {
|
|
2402
2816
|
background: var(--color-success-soft);
|
|
2403
|
-
color:
|
|
2817
|
+
color: var(--color-success-active);
|
|
2404
2818
|
}
|
|
2405
2819
|
.u-action.u-action-share:hover:not(:disabled) {
|
|
2406
2820
|
background: var(--color-warning-soft);
|
|
2407
|
-
color:
|
|
2821
|
+
color: var(--color-warning-active);
|
|
2408
2822
|
}
|
|
2409
2823
|
.u-action.u-action-danger:hover {
|
|
2410
2824
|
color: var(--color-danger);
|
|
@@ -2490,12 +2904,12 @@ textarea.textarea:focus {
|
|
|
2490
2904
|
// deep drop shadow.
|
|
2491
2905
|
box-shadow:
|
|
2492
2906
|
0 0 0 1px color-mix(in srgb, var(--color-text) 10%, transparent),
|
|
2493
|
-
|
|
2907
|
+
var(--shadow-overlay-lg);
|
|
2494
2908
|
background: var(--color-surface);
|
|
2495
2909
|
}
|
|
2496
2910
|
.lx-modal {
|
|
2497
2911
|
background: var(--color-surface);
|
|
2498
|
-
border-radius:
|
|
2912
|
+
border-radius: var(--radius-xl);
|
|
2499
2913
|
corner-shape: squircle;
|
|
2500
2914
|
border: none;
|
|
2501
2915
|
width: 100%;
|
|
@@ -2614,7 +3028,7 @@ textarea.textarea:focus {
|
|
|
2614
3028
|
justify-content: center;
|
|
2615
3029
|
width: 34px;
|
|
2616
3030
|
height: 34px;
|
|
2617
|
-
border-radius:
|
|
3031
|
+
border-radius: var(--radius);
|
|
2618
3032
|
corner-shape: squircle;
|
|
2619
3033
|
flex: none;
|
|
2620
3034
|
background: var(--color-primary-soft);
|
|
@@ -2760,9 +3174,9 @@ textarea.textarea:focus {
|
|
|
2760
3174
|
}
|
|
2761
3175
|
.lx-confirm-actions .btn {
|
|
2762
3176
|
flex: 1;
|
|
2763
|
-
min-height:
|
|
2764
|
-
height:
|
|
2765
|
-
border-radius:
|
|
3177
|
+
min-height: var(--wui-control-height);
|
|
3178
|
+
height: var(--wui-control-height);
|
|
3179
|
+
border-radius: var(--radius);
|
|
2766
3180
|
corner-shape: squircle;
|
|
2767
3181
|
padding-left: 16px;
|
|
2768
3182
|
padding-right: 16px;
|
|
@@ -2859,8 +3273,8 @@ textarea.textarea:focus {
|
|
|
2859
3273
|
padding: 12px 14px;
|
|
2860
3274
|
background: var(--color-surface);
|
|
2861
3275
|
border: none;
|
|
2862
|
-
border-radius:
|
|
2863
|
-
box-shadow:
|
|
3276
|
+
border-radius: var(--radius-lg);
|
|
3277
|
+
box-shadow: var(--shadow-overlay);
|
|
2864
3278
|
text-align: left;
|
|
2865
3279
|
white-space: normal;
|
|
2866
3280
|
cursor: default;
|
|
@@ -2926,6 +3340,37 @@ textarea.textarea:focus {
|
|
|
2926
3340
|
align-items: center;
|
|
2927
3341
|
}
|
|
2928
3342
|
|
|
3343
|
+
// Tablet and below: the masthead stops being a single row — the title block and
|
|
3344
|
+
// its actions stack, and the actions stretch to the full width rather than
|
|
3345
|
+
// crowding the title. Bootstrap's lg tier (991.98px), not an invented breakpoint.
|
|
3346
|
+
//
|
|
3347
|
+
// Selectors are DOUBLED (`.page-header.page-header`) on purpose. A media query
|
|
3348
|
+
// adds no specificity, so a consumer's own single-class rule — written later in
|
|
3349
|
+
// the cascade, as a consuming app's stylesheet always is — silently beats the
|
|
3350
|
+
// responsive rule and the layout never collapses. Doubling puts these at (0,2,0)
|
|
3351
|
+
// so they hold, while still leaving a consumer free to override deliberately.
|
|
3352
|
+
@media (max-width: 991.98px) {
|
|
3353
|
+
.page-header.page-header {
|
|
3354
|
+
flex-direction: column;
|
|
3355
|
+
align-items: stretch;
|
|
3356
|
+
gap: 12px;
|
|
3357
|
+
}
|
|
3358
|
+
.page-actions.page-actions {
|
|
3359
|
+
flex-wrap: wrap;
|
|
3360
|
+
width: 100%;
|
|
3361
|
+
}
|
|
3362
|
+
// A two-up field row is too tight for a tablet column.
|
|
3363
|
+
.field-row.field-row,
|
|
3364
|
+
.field-row-3.field-row-3 {
|
|
3365
|
+
grid-template-columns: 1fr;
|
|
3366
|
+
}
|
|
3367
|
+
// A fixed 420px drawer leaves too little of the page visible on a tablet.
|
|
3368
|
+
.drawer.drawer {
|
|
3369
|
+
width: 100vw;
|
|
3370
|
+
max-width: 420px;
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
|
|
2929
3374
|
// ── Row-action / icon buttons ──
|
|
2930
3375
|
.btn-icon {
|
|
2931
3376
|
min-height: 0;
|
|
@@ -3002,7 +3447,7 @@ textarea.textarea:focus {
|
|
|
3002
3447
|
overflow: hidden;
|
|
3003
3448
|
display: block;
|
|
3004
3449
|
background: var(--color-bg-muted);
|
|
3005
|
-
border-radius:
|
|
3450
|
+
border-radius: var(--radius-sm);
|
|
3006
3451
|
}
|
|
3007
3452
|
.skeleton::after {
|
|
3008
3453
|
content: "";
|