@webority/theme 0.7.7 → 0.7.9
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 +50 -0
- package/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_components.scss +184 -14
- package/scss/_tokens.scss +11 -3
- package/scss/_wui-aliases.scss +35 -5
- package/scss/_wui-components.scss +8 -1
- package/scss/wui/_card.scss +2 -2
- package/scss/wui/_choice.scss +11 -4
- package/scss/wui/_controls.scss +93 -48
- package/scss/wui/_data.scss +20 -0
- package/scss/wui/_flow.scss +250 -34
- package/scss/wui/_media.scss +1 -1
- package/scss/wui/_overlays.scss +254 -25
- package/scss/wui/_shell.scss +37 -3
- package/scss/wui/_tone-pill.scss +23 -0
- package/scss/wui/_typography.scss +45 -15
- package/scss/wui/_utilities.scss +29 -1
- package/scss/wui/_workspace.scss +239 -0
package/scss/wui/_controls.scss
CHANGED
|
@@ -176,6 +176,24 @@
|
|
|
176
176
|
// Re-assert the knob — the checkbox :checked tick has equal specificity.
|
|
177
177
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='10' cy='10' r='7' fill='white'/%3E%3C/svg%3E");
|
|
178
178
|
}
|
|
179
|
+
|
|
180
|
+
// Circular variant (shape="circle") — a compact round on/off button instead of a sliding
|
|
181
|
+
// pill. The track is a square-sized circle; the knob dot stays CENTRED in both states (no
|
|
182
|
+
// left/right slide) and the fill flips to primary when on. Placed after the base switch
|
|
183
|
+
// block so the equal-specificity :checked position override wins on source order.
|
|
184
|
+
.form-switch-circle .form-check-input {
|
|
185
|
+
width: 24px;
|
|
186
|
+
height: 24px;
|
|
187
|
+
// The switch is a type="checkbox", which carries `corner-shape: squircle` — on a square
|
|
188
|
+
// that renders a rounded square, not a circle. Reset to a true round corner.
|
|
189
|
+
corner-shape: round;
|
|
190
|
+
border-radius: 999px;
|
|
191
|
+
background-position: center;
|
|
192
|
+
background-size: 10px 10px;
|
|
193
|
+
}
|
|
194
|
+
.form-switch-circle .form-check-input:checked {
|
|
195
|
+
background-position: center;
|
|
196
|
+
}
|
|
179
197
|
@media (prefers-reduced-motion: reduce) {
|
|
180
198
|
.form-check-input,
|
|
181
199
|
.wui-check,
|
|
@@ -191,6 +209,11 @@
|
|
|
191
209
|
// deltas on top of Bootstrap's $input-* vars: shared control height, hover
|
|
192
210
|
// fill, brand-border focus (no glow), soft invalid border.
|
|
193
211
|
//
|
|
212
|
+
// Soft-filled resting look (per the field design reference): the field rests on a
|
|
213
|
+
// muted fill with a barely-there border, then lifts to a white surface + brand
|
|
214
|
+
// border + a soft focus ring on focus. "Filled" is the same resting look with a
|
|
215
|
+
// value; the state ladder is default/filled → hover → focus → disabled → error.
|
|
216
|
+
//
|
|
194
217
|
// Keep shell utilities (.input-wrap, .input-icon, .input-sm, …) — those are
|
|
195
218
|
// structure, not a parallel visual system.
|
|
196
219
|
.form-control,
|
|
@@ -204,7 +227,7 @@
|
|
|
204
227
|
padding: 12px;
|
|
205
228
|
border-radius: var(--radius);
|
|
206
229
|
border: 1px solid var(--color-border);
|
|
207
|
-
background: var(--color-
|
|
230
|
+
background: var(--color-bg-muted);
|
|
208
231
|
font-size: 14px;
|
|
209
232
|
line-height: 1.5;
|
|
210
233
|
color: var(--color-text);
|
|
@@ -246,7 +269,7 @@ textarea.form-control.autosize {
|
|
|
246
269
|
// Hover/error guards: without them the hover fill repaints over an invalid border.
|
|
247
270
|
.form-control:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
248
271
|
.form-select:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
249
|
-
.input:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
272
|
+
.input:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]):not(.is-invalid):not(.error):not(.open),
|
|
250
273
|
.select:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
251
274
|
.textarea:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]):not(.is-invalid):not(.error) {
|
|
252
275
|
background-color: var(--color-bg-muted);
|
|
@@ -258,10 +281,11 @@ textarea.form-control.autosize {
|
|
|
258
281
|
.select:focus,
|
|
259
282
|
.textarea:focus {
|
|
260
283
|
outline: none;
|
|
261
|
-
|
|
284
|
+
// Lift off the muted resting fill onto a clean white surface while active.
|
|
285
|
+
background-color: var(--color-surface);
|
|
262
286
|
color: var(--color-text);
|
|
263
|
-
//
|
|
264
|
-
border-color: var(--
|
|
287
|
+
// Soft brand border is the focus cue (per the reference) — no ring/shadow.
|
|
288
|
+
border-color: var(--wui-primary-30);
|
|
265
289
|
box-shadow: none;
|
|
266
290
|
}
|
|
267
291
|
.form-control::placeholder,
|
|
@@ -297,8 +321,10 @@ textarea.form-control.autosize {
|
|
|
297
321
|
.input:disabled,
|
|
298
322
|
.select:disabled,
|
|
299
323
|
.textarea:disabled {
|
|
300
|
-
|
|
301
|
-
|
|
324
|
+
// A touch deeper than the resting fill so a disabled field stays distinct now that
|
|
325
|
+
// the resting state is itself filled (per the reference's disabled field).
|
|
326
|
+
background: color-mix(in srgb, var(--color-bg-muted) 72%, var(--color-border));
|
|
327
|
+
border-color: transparent;
|
|
302
328
|
color: var(--color-text-subtle);
|
|
303
329
|
cursor: not-allowed;
|
|
304
330
|
}
|
|
@@ -350,7 +376,9 @@ textarea.form-control.autosize {
|
|
|
350
376
|
.input.error:focus,
|
|
351
377
|
.textarea.error:focus {
|
|
352
378
|
background: var(--color-surface);
|
|
353
|
-
|
|
379
|
+
// Soft danger border (danger at 30%, mirroring the primary-30 focus border) — no ring,
|
|
380
|
+
// so the field reads as a quiet error, not a solid alarm box.
|
|
381
|
+
border-color: color-mix(in srgb, var(--color-danger) 30%, transparent);
|
|
354
382
|
box-shadow: none;
|
|
355
383
|
// Kill Bootstrap's default invalid SVG + extra padding when .is-invalid is used.
|
|
356
384
|
background-image: none;
|
|
@@ -618,17 +646,23 @@ textarea.form-control.autosize {
|
|
|
618
646
|
width: 100%;
|
|
619
647
|
border: 1px solid var(--color-border);
|
|
620
648
|
border-radius: var(--radius);
|
|
621
|
-
|
|
622
|
-
|
|
649
|
+
// Soft-filled at rest, white on focus — one shared fill for the whole control so the
|
|
650
|
+
// prefix/suffix and the input body are always the SAME colour combination as the field.
|
|
651
|
+
background: var(--color-bg-muted);
|
|
652
|
+
transition:
|
|
653
|
+
border-color 0.12s ease,
|
|
654
|
+
background-color 0.12s ease;
|
|
623
655
|
}
|
|
624
656
|
.input-affixed:hover:not(:focus-within) {
|
|
625
657
|
border-color: var(--color-border-strong);
|
|
626
658
|
}
|
|
627
659
|
.input-affixed:focus-within {
|
|
628
|
-
|
|
660
|
+
background: var(--color-surface);
|
|
661
|
+
border-color: var(--wui-primary-30);
|
|
629
662
|
}
|
|
630
663
|
.input-affixed:has(.input.error) {
|
|
631
|
-
|
|
664
|
+
background: var(--color-surface);
|
|
665
|
+
border-color: color-mix(in srgb, var(--color-danger) 30%, transparent);
|
|
632
666
|
}
|
|
633
667
|
.input-affixed .input {
|
|
634
668
|
flex: 1;
|
|
@@ -641,7 +675,9 @@ textarea.form-control.autosize {
|
|
|
641
675
|
display: flex;
|
|
642
676
|
align-items: center;
|
|
643
677
|
padding: 0 12px;
|
|
644
|
-
|
|
678
|
+
// Transparent so the affix inherits the wrapper fill — the addon and the input read as
|
|
679
|
+
// one continuous surface (only the separator border divides them).
|
|
680
|
+
background: transparent;
|
|
645
681
|
color: var(--color-text-subtle);
|
|
646
682
|
font-size: 14px;
|
|
647
683
|
white-space: nowrap;
|
|
@@ -669,19 +705,27 @@ textarea.form-control.autosize {
|
|
|
669
705
|
transform: translateY(-50%);
|
|
670
706
|
transform-origin: left center;
|
|
671
707
|
margin: 0;
|
|
672
|
-
padding: 0
|
|
708
|
+
padding: 0 6px;
|
|
709
|
+
// Rounded so the background mask reads as a soft pill sitting on the border, not a sharp
|
|
710
|
+
// rectangle. Invisible at rest (label fill == field fill); only shows once it floats up.
|
|
711
|
+
border-radius: var(--radius-sm);
|
|
673
712
|
max-width: calc(100% - 24px);
|
|
674
713
|
overflow: hidden;
|
|
675
714
|
text-overflow: ellipsis;
|
|
676
715
|
white-space: nowrap;
|
|
677
716
|
font-size: 14px;
|
|
678
717
|
color: var(--color-text-subtle);
|
|
679
|
-
background
|
|
718
|
+
// The floated label sits ON the top border, so its background masks the border under the
|
|
719
|
+
// text. It MUST match the field's own fill, or the mask reads as a separate patch. The
|
|
720
|
+
// resting/filled fill is --color-bg-muted (a white notch on the grey fill was the tell); it
|
|
721
|
+
// only lifts to --color-surface on focus/invalid, matched below.
|
|
722
|
+
background: var(--color-bg-muted);
|
|
680
723
|
pointer-events: none;
|
|
681
724
|
transition:
|
|
682
725
|
top 0.15s var(--wui-ease-ui),
|
|
683
726
|
transform 0.15s var(--wui-ease-ui),
|
|
684
|
-
color 0.15s var(--wui-ease-ui)
|
|
727
|
+
color 0.15s var(--wui-ease-ui),
|
|
728
|
+
background-color 0.12s;
|
|
685
729
|
}
|
|
686
730
|
.field-float .input:focus ~ .float-label,
|
|
687
731
|
.field-float .input:not(:placeholder-shown) ~ .float-label {
|
|
@@ -694,6 +738,14 @@ textarea.form-control.autosize {
|
|
|
694
738
|
.field-float .input.error ~ .float-label {
|
|
695
739
|
color: var(--color-danger-text);
|
|
696
740
|
}
|
|
741
|
+
// Track the field's fill: it lifts to --color-surface while focused or invalid, so the label
|
|
742
|
+
// mask follows to stay seamless in those states too.
|
|
743
|
+
.field-float .input:focus ~ .float-label,
|
|
744
|
+
.field-float .input.error ~ .float-label,
|
|
745
|
+
.field-float .input.is-invalid ~ .float-label,
|
|
746
|
+
.field-float .input[aria-invalid="true"] ~ .float-label {
|
|
747
|
+
background: var(--color-surface);
|
|
748
|
+
}
|
|
697
749
|
// Floating label for AppSelect — the trigger is a <button>, so there is no
|
|
698
750
|
// :placeholder-shown to key off. :has() raises the label when the control is
|
|
699
751
|
// focused/open, or once a value is chosen (the label span drops
|
|
@@ -707,6 +759,9 @@ textarea.form-control.autosize {
|
|
|
707
759
|
.field-float:has(.search-select-control:focus) .float-label,
|
|
708
760
|
.field-float:has(.search-select-control.open) .float-label {
|
|
709
761
|
color: var(--color-primary);
|
|
762
|
+
// The control turns white while focused/open, so the label mask follows (a filled-but-closed
|
|
763
|
+
// select keeps the default muted mask, matching its muted resting fill).
|
|
764
|
+
background: var(--color-surface);
|
|
710
765
|
}
|
|
711
766
|
|
|
712
767
|
.hint,
|
|
@@ -859,22 +914,24 @@ wui-select[compact] {
|
|
|
859
914
|
border: 1px solid var(--color-border);
|
|
860
915
|
border-radius: var(--radius-lg);
|
|
861
916
|
corner-shape: squircle;
|
|
862
|
-
|
|
917
|
+
// Soft-filled at rest, white on focus — the numeric input and its unit pill share the
|
|
918
|
+
// one wrapper fill, same colour combination as every other field.
|
|
919
|
+
background: var(--color-bg-muted);
|
|
863
920
|
transition:
|
|
864
921
|
border-color 0.12s,
|
|
865
922
|
background-color 0.12s;
|
|
866
923
|
overflow: hidden;
|
|
867
924
|
}
|
|
868
925
|
.input-with-unit:hover:not(:focus-within) {
|
|
869
|
-
background: var(--color-bg-muted);
|
|
870
926
|
border-color: var(--color-border-strong);
|
|
871
927
|
}
|
|
872
928
|
.input-with-unit:focus-within {
|
|
873
|
-
background: var(--color-
|
|
874
|
-
border-color:
|
|
929
|
+
background: var(--color-surface);
|
|
930
|
+
border-color: var(--wui-primary-30);
|
|
875
931
|
}
|
|
876
932
|
.input-with-unit:has(.num-input.error) {
|
|
877
|
-
|
|
933
|
+
background: var(--color-surface);
|
|
934
|
+
border-color: color-mix(in srgb, var(--color-danger) 30%, transparent);
|
|
878
935
|
}
|
|
879
936
|
|
|
880
937
|
// Numeric inputs are plain text fields — no native spinner, no stepper. The
|
|
@@ -889,7 +946,8 @@ wui-select[compact] {
|
|
|
889
946
|
box-shadow: none;
|
|
890
947
|
}
|
|
891
948
|
.input-with-unit .unit {
|
|
892
|
-
|
|
949
|
+
// Transparent so the pill inherits the wrapper fill — one continuous surface.
|
|
950
|
+
background: transparent;
|
|
893
951
|
border: none;
|
|
894
952
|
border-left: 1px solid var(--color-border);
|
|
895
953
|
padding: 9px 14px;
|
|
@@ -1010,30 +1068,21 @@ wui-select[compact] {
|
|
|
1010
1068
|
border: 1px solid var(--color-border);
|
|
1011
1069
|
}
|
|
1012
1070
|
.badge-primary {
|
|
1013
|
-
|
|
1014
|
-
color: var(--color-primary);
|
|
1071
|
+
@include wui-tone-pill-colors(primary, var(--color-primary));
|
|
1015
1072
|
border-color: color-mix(in srgb, var(--color-primary) 25%, transparent);
|
|
1016
1073
|
}
|
|
1017
1074
|
.badge-success {
|
|
1018
|
-
|
|
1019
|
-
color: var(--color-success-text);
|
|
1020
|
-
border-color: transparent;
|
|
1075
|
+
@include wui-tone-pill-colors(success);
|
|
1021
1076
|
}
|
|
1022
1077
|
.badge-warning,
|
|
1023
1078
|
.badge-warn {
|
|
1024
|
-
|
|
1025
|
-
color: var(--color-warning-text);
|
|
1026
|
-
border-color: transparent;
|
|
1079
|
+
@include wui-tone-pill-colors(warning);
|
|
1027
1080
|
}
|
|
1028
1081
|
.badge-danger {
|
|
1029
|
-
|
|
1030
|
-
color: var(--color-danger-text);
|
|
1031
|
-
border-color: transparent;
|
|
1082
|
+
@include wui-tone-pill-colors(danger);
|
|
1032
1083
|
}
|
|
1033
1084
|
.badge-info {
|
|
1034
|
-
|
|
1035
|
-
color: var(--color-info-text);
|
|
1036
|
-
border-color: transparent;
|
|
1085
|
+
@include wui-tone-pill-colors(info);
|
|
1037
1086
|
}
|
|
1038
1087
|
.badge-dot::before {
|
|
1039
1088
|
content: "";
|
|
@@ -1069,29 +1118,24 @@ wui-select[compact] {
|
|
|
1069
1118
|
.status-label.success,
|
|
1070
1119
|
.status-label.tone-ok,
|
|
1071
1120
|
.status-label.tone-green {
|
|
1072
|
-
|
|
1073
|
-
color: var(--color-success-text);
|
|
1121
|
+
@include wui-tone-pill-colors(success);
|
|
1074
1122
|
}
|
|
1075
1123
|
.status-label.warning,
|
|
1076
1124
|
.status-label.tone-warn,
|
|
1077
1125
|
.status-label.tone-amber {
|
|
1078
|
-
|
|
1079
|
-
color: var(--color-warning-text);
|
|
1126
|
+
@include wui-tone-pill-colors(warning);
|
|
1080
1127
|
}
|
|
1081
1128
|
.status-label.danger,
|
|
1082
1129
|
.status-label.tone-bad,
|
|
1083
1130
|
.status-label.tone-red {
|
|
1084
|
-
|
|
1085
|
-
color: var(--color-danger-text);
|
|
1131
|
+
@include wui-tone-pill-colors(danger);
|
|
1086
1132
|
}
|
|
1087
1133
|
.status-label.info,
|
|
1088
1134
|
.status-label.tone-blue {
|
|
1089
|
-
|
|
1090
|
-
color: var(--color-info-text);
|
|
1135
|
+
@include wui-tone-pill-colors(info);
|
|
1091
1136
|
}
|
|
1092
1137
|
.status-label.primary {
|
|
1093
|
-
|
|
1094
|
-
color: var(--color-primary);
|
|
1138
|
+
@include wui-tone-pill-colors(primary, var(--color-primary));
|
|
1095
1139
|
}
|
|
1096
1140
|
// Default/neutral — also the bare `.status-label` look, so an unknown or omitted
|
|
1097
1141
|
// tone still renders as a proper pill instead of transparent text.
|
|
@@ -1311,7 +1355,7 @@ wui-otp {
|
|
|
1311
1355
|
background-color: var(--color-primary);
|
|
1312
1356
|
border: 2px solid var(--color-surface);
|
|
1313
1357
|
border-radius: 50%;
|
|
1314
|
-
box-shadow:
|
|
1358
|
+
box-shadow: var(--wui-shadow-thumb);
|
|
1315
1359
|
cursor: pointer;
|
|
1316
1360
|
transition:
|
|
1317
1361
|
background-color 0.15s ease,
|
|
@@ -1319,7 +1363,7 @@ wui-otp {
|
|
|
1319
1363
|
border-color 0.15s ease;
|
|
1320
1364
|
}
|
|
1321
1365
|
.slider-thumb:hover:not(:disabled) {
|
|
1322
|
-
box-shadow:
|
|
1366
|
+
box-shadow: var(--wui-shadow-thumb-hover);
|
|
1323
1367
|
}
|
|
1324
1368
|
.slider-thumb:focus-visible {
|
|
1325
1369
|
outline: 2px solid var(--color-primary);
|
|
@@ -1342,6 +1386,7 @@ wui-otp {
|
|
|
1342
1386
|
white-space: nowrap;
|
|
1343
1387
|
}
|
|
1344
1388
|
|
|
1389
|
+
// Dark mode: slider shadows need light ink for visibility.
|
|
1345
1390
|
// Size variants
|
|
1346
1391
|
.slider-sm {
|
|
1347
1392
|
height: 28px;
|
package/scss/wui/_data.scss
CHANGED
|
@@ -230,6 +230,10 @@
|
|
|
230
230
|
background: var(--color-bg-muted);
|
|
231
231
|
color: var(--color-text);
|
|
232
232
|
}
|
|
233
|
+
.wui-expand-toggle:focus-visible {
|
|
234
|
+
outline: 2px solid var(--color-primary);
|
|
235
|
+
outline-offset: 2px;
|
|
236
|
+
}
|
|
233
237
|
.wui-expand-toggle .bi {
|
|
234
238
|
transition: transform 0.18s var(--wui-ease-out-quart, ease);
|
|
235
239
|
}
|
|
@@ -429,6 +433,10 @@
|
|
|
429
433
|
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
430
434
|
color: var(--color-primary);
|
|
431
435
|
}
|
|
436
|
+
.tpg-arrow:focus-visible {
|
|
437
|
+
outline: 2px solid var(--color-primary);
|
|
438
|
+
outline-offset: 2px;
|
|
439
|
+
}
|
|
432
440
|
// Text-5 already reads as "unavailable" — no opacity dimming on top of it.
|
|
433
441
|
.tpg-arrow:disabled {
|
|
434
442
|
color: var(--color-border-strong);
|
|
@@ -460,6 +468,10 @@
|
|
|
460
468
|
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
461
469
|
color: var(--color-primary);
|
|
462
470
|
}
|
|
471
|
+
.tpg-page:focus-visible {
|
|
472
|
+
outline: 2px solid var(--color-primary);
|
|
473
|
+
outline-offset: 2px;
|
|
474
|
+
}
|
|
463
475
|
.tpg-page.is-active {
|
|
464
476
|
background: var(--color-primary);
|
|
465
477
|
color: var(--color-primary-text);
|
|
@@ -627,6 +639,10 @@
|
|
|
627
639
|
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
628
640
|
color: var(--color-primary);
|
|
629
641
|
}
|
|
642
|
+
.u-action:focus-visible {
|
|
643
|
+
outline: 2px solid var(--color-primary);
|
|
644
|
+
outline-offset: 2px;
|
|
645
|
+
}
|
|
630
646
|
.u-action:disabled {
|
|
631
647
|
opacity: 0.4;
|
|
632
648
|
cursor: not-allowed;
|
|
@@ -729,6 +745,10 @@
|
|
|
729
745
|
cursor: pointer;
|
|
730
746
|
user-select: none;
|
|
731
747
|
}
|
|
748
|
+
.table th.sortable:focus-visible {
|
|
749
|
+
outline: 2px solid var(--color-primary);
|
|
750
|
+
outline-offset: -2px;
|
|
751
|
+
}
|
|
732
752
|
.table th.sortable .sort-icon {
|
|
733
753
|
// Reserved space so the header does not jump width when the arrow appears.
|
|
734
754
|
display: inline-block;
|