@webority/theme 0.127.0 → 0.129.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.
@@ -36,6 +36,7 @@
36
36
  @import "wui/navigation";
37
37
  @import "wui/nav-list";
38
38
  @import "wui/card";
39
+ @import "wui/card-options";
39
40
  @import "wui/empty-state";
40
41
  @import "wui/data";
41
42
  @import "wui/pagination";
@@ -89,6 +90,7 @@
89
90
  @import "wui/filter-popover";
90
91
  @import "wui/metric-strip";
91
92
  @import "wui/due-date";
93
+ @import "wui/cell";
92
94
 
93
95
  // Country flags are NOT part of this aggregate. The 245 `.wui-flag-*` rules are about 187KB raw
94
96
  // (38KB Brotli) of inlined SVG, and most sites never render a phone field or country picker.
@@ -96,15 +98,16 @@
96
98
  // <wui-phone> and <wui-country-select> show an empty grey box where the flag should be.
97
99
  @import "wui/shell";
98
100
  @import "wui/shell-parts";
101
+ @import "wui/account-switcher";
99
102
  @import "wui/dock";
100
103
  @import "wui/consent-banner";
101
104
  @import "wui/file-viewer";
102
105
  @import "wui/lightbox";
103
106
  @import "wui/toolbar";
104
107
  @import "wui/theme-toggle";
108
+ @import "wui/auth-layout";
105
109
  @import "wui/tree";
106
110
  @import "wui/floating";
107
- @import "wui/chat";
108
111
  @import "wui/code-block";
109
112
  // After `overlays`: anchors the shared modal shell to the top when it holds the palette card.
110
113
  @import "wui/command-palette";
@@ -114,6 +117,9 @@
114
117
  @import "wui/hover-card";
115
118
  @import "wui/notification-center";
116
119
  @import "wui/whats-new";
120
+ // Before field-floor, which restates the font sizes the size steps set.
121
+ @import "wui/field-busy";
122
+ @import "wui/field-sizes";
117
123
  // After every component partial: restates each field's own font size with a 16px floor on touch screens.
118
124
  @import "wui/field-floor";
119
125
  // After every component partial: the shared focus ring and pressed wash settle ties with their hover rules.
@@ -28,6 +28,7 @@
28
28
  @import "wui/navigation";
29
29
  @import "wui/nav-list";
30
30
  @import "wui/card";
31
+ @import "wui/card-options";
31
32
  @import "wui/empty-state";
32
33
  @import "wui/data";
33
34
  @import "wui/pagination";
@@ -75,6 +76,7 @@
75
76
  @import "wui/filter-popover";
76
77
  @import "wui/metric-strip";
77
78
  @import "wui/due-date";
79
+ @import "wui/cell";
78
80
 
79
81
  // Country flags are NOT part of this layer. The 245 `.wui-flag-*` rules are about 187KB raw
80
82
  // (38KB Brotli) of inlined SVG, and most sites never render a phone field or country picker.
@@ -82,10 +84,12 @@
82
84
  // <wui-phone> and <wui-country-select> show an empty grey box where the flag should be.
83
85
  @import "wui/shell";
84
86
  @import "wui/shell-parts";
87
+ @import "wui/account-switcher";
85
88
  @import "wui/consent-banner";
86
89
  @import "wui/lightbox";
87
90
  @import "wui/toolbar";
88
91
  @import "wui/theme-toggle";
92
+ @import "wui/auth-layout";
89
93
  @import "wui/floating";
90
94
  @import "wui/code-block";
91
95
  @import "wui/context-menu";
@@ -93,6 +97,9 @@
93
97
  @import "wui/hover-card";
94
98
  @import "wui/notification-center";
95
99
  @import "wui/whats-new";
100
+ // Before field-floor, which restates the font sizes the size steps set.
101
+ @import "wui/field-busy";
102
+ @import "wui/field-sizes";
96
103
  // After every component partial: restates each field's own font size with a 16px floor on touch screens.
97
104
  @import "wui/field-floor";
98
105
  // After every component partial: the shared focus ring and pressed wash settle ties with their hover rules.
@@ -0,0 +1,70 @@
1
+ // Account switcher: shared by React <AppAccountSwitcher> and Razor <app-account-switcher>.
2
+ // Rows are Bootstrap dropdown items inside the shell's user menu (.wui-usermenu-menu, see
3
+ // _shell-parts.scss), so they inherit that menu's padding and hover. Every rule here is scoped
4
+ // under .wui-account-switcher so it wins over Bootstrap's .dropdown-item specificity.
5
+
6
+ .wui-account-switcher {
7
+ padding-block: var(--wui-space-xxs);
8
+ }
9
+
10
+ .wui-account-switcher-label {
11
+ padding: var(--wui-space-sm);
12
+ font-size: var(--wui-fs-sm);
13
+ color: var(--color-text-muted);
14
+ }
15
+
16
+ .wui-account-switcher-list {
17
+ display: flex;
18
+ flex-direction: column;
19
+ max-height: 40vh;
20
+ overflow-y: auto;
21
+ }
22
+
23
+ .wui-account-switcher .wui-account-switcher-option {
24
+ display: flex;
25
+ align-items: center;
26
+ gap: var(--wui-space-sm);
27
+ }
28
+
29
+ .wui-account-switcher-option-text {
30
+ flex: 1 1 auto;
31
+ min-width: 0;
32
+ }
33
+
34
+ .wui-account-switcher-option-name {
35
+ display: block;
36
+ overflow: hidden;
37
+ text-overflow: ellipsis;
38
+ white-space: nowrap;
39
+ }
40
+
41
+ .wui-account-switcher-option-note {
42
+ display: block;
43
+ overflow: hidden;
44
+ font-size: var(--wui-fs-xs);
45
+ color: var(--color-text-muted);
46
+ text-overflow: ellipsis;
47
+ white-space: nowrap;
48
+ }
49
+
50
+ .wui-account-switcher-badge {
51
+ flex: none;
52
+ padding: var(--wui-space-xxs) var(--wui-space-sm);
53
+ border-radius: var(--wui-radius-pill);
54
+ background: var(--color-bg-subtle);
55
+ color: var(--color-text-muted);
56
+ font-size: var(--wui-fs-xs);
57
+ font-weight: var(--wui-fw-semibold);
58
+ }
59
+
60
+ // The current account keeps its tint through hover, so the tick is never the only cue.
61
+ .wui-account-switcher .wui-account-switcher-option.is-active,
62
+ .wui-account-switcher .wui-account-switcher-option.is-active:hover {
63
+ background: var(--color-primary-soft);
64
+ color: var(--color-primary-text);
65
+ }
66
+
67
+ .wui-account-switcher-tick {
68
+ flex: none;
69
+ color: var(--color-primary-text);
70
+ }
@@ -0,0 +1,77 @@
1
+ // ── Auth layout ──
2
+ // AppAuthLayout / <app-auth-layout>: a sign-in page, centred by default. The form panel sits in
3
+ // the middle of the page; `.wui-auth-split` (variant="split") adds an aside on the right from the
4
+ // lg tier up, and below lg the aside is hidden so the form is alone and centred.
5
+ // <div class="wui-auth wui-auth-centred|wui-auth-split">
6
+ // <div class="wui-auth-main"><div class="wui-auth-panel">logo, body, footer</div></div>
7
+ // <aside class="wui-auth-aside">…</aside>
8
+ // The aside follows the form in the DOM and sits on the right; nothing reorders it.
9
+ //
10
+ // Part of the wui-components layer, see _wui-components.scss for load order.
11
+
12
+ .wui-auth {
13
+ display: grid;
14
+ grid-template-columns: minmax(0, 1fr);
15
+ min-height: var(--wui-auth-min-height, 100dvh);
16
+ background: var(--color-bg);
17
+ color: var(--color-text);
18
+ }
19
+
20
+ .wui-auth-main {
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ min-width: 0;
25
+ padding: var(--wui-space-xl) var(--wui-space-lg);
26
+ }
27
+
28
+ .wui-auth-panel {
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: var(--wui-space-xl);
32
+ width: 100%;
33
+ max-width: var(--wui-auth-width, 420px);
34
+ }
35
+
36
+ .wui-auth-logo {
37
+ display: flex;
38
+ justify-content: center;
39
+ }
40
+ .wui-auth-logo img,
41
+ .wui-auth-logo svg {
42
+ max-width: 100%;
43
+ max-height: 48px;
44
+ height: auto;
45
+ }
46
+
47
+ .wui-auth-body {
48
+ padding: var(--wui-space-xl);
49
+ border: 1px solid var(--color-border);
50
+ border-radius: var(--wui-radius-lg);
51
+ background: var(--color-surface);
52
+ }
53
+
54
+ .wui-auth-footer {
55
+ color: var(--color-text-muted);
56
+ font-size: var(--wui-fs-sm);
57
+ text-align: center;
58
+ }
59
+
60
+ .wui-auth-aside {
61
+ display: none;
62
+ }
63
+
64
+ @include media-breakpoint-up(lg) {
65
+ .wui-auth-split {
66
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
67
+ }
68
+ .wui-auth-split .wui-auth-aside {
69
+ display: flex;
70
+ flex-direction: column;
71
+ justify-content: center;
72
+ min-width: 0;
73
+ padding: var(--wui-space-3xl);
74
+ background: var(--color-primary-soft);
75
+ color: var(--color-text);
76
+ }
77
+ }
@@ -0,0 +1,45 @@
1
+ // Card options: `flush` (content runs to the card's rounded corners) and `divided`
2
+ // (a rule under the header).
3
+ //
4
+ // Part of the wui-components layer, see _wui-components.scss for the load
5
+ // order. This partial loads directly after `wui/card`, because its padding
6
+ // rules must win over the header rule in _card.scss.
7
+
8
+ // A consumer class can set --wui-card-divider-color to retune the rule.
9
+ .card {
10
+ --wui-card-divider-color: var(--color-border);
11
+ }
12
+
13
+ // The card's own border-radius clips the content, so a table or image keeps the corners.
14
+ .card-flush {
15
+ overflow: hidden;
16
+ }
17
+
18
+ // A 1px hairline stays raw: it is a rule, not a spacing or type value.
19
+ .card-divided > .card-header {
20
+ border-bottom: 1px solid var(--wui-card-divider-color);
21
+ }
22
+ // _card.scss removes the body's top padding under any header. A divided card keeps the
23
+ // body's padding step, so each padding class restores its own value here.
24
+ .card-divided > .card-header + .card-body {
25
+ padding-top: var(--bs-card-spacer-y);
26
+ }
27
+ .card-pad-sm.card-divided > .card-header + .card-body {
28
+ padding-top: var(--wui-space-md);
29
+ }
30
+ .card-pad-lg.card-divided > .card-header + .card-body {
31
+ padding-top: var(--wui-space-xl);
32
+ }
33
+ .card-pad-none.card-divided > .card-header + .card-body {
34
+ padding-top: 0;
35
+ }
36
+ .card-compact.card-divided > .card-header + .card-body {
37
+ padding-top: var(--wui-space-lg);
38
+ }
39
+
40
+ // Last, so it wins every tie: flush clears the body only, as `.card-pad-none` does.
41
+ // The header and footer keep their padding.
42
+ .card.card-flush > .card-body,
43
+ .card.card-flush > .card-header + .card-body {
44
+ padding: 0;
45
+ }
@@ -46,11 +46,13 @@
46
46
  }
47
47
  // Bootstrap has its own `.card-title` (a bare margin-bottom) — these override it,
48
48
  // so they must stay in the layer that loads after Bootstrap.
49
- // A product sets its title type with --wui-card-title-fs and --wui-card-title-lh.
50
- .card-title {
49
+ // A product sets its title type with --wui-card-title-font-family, -fs, -fw and -lh.
50
+ .card-title,
51
+ .result-title {
51
52
  margin: 0;
53
+ font-family: var(--wui-card-title-font-family, inherit);
52
54
  font-size: var(--wui-card-title-fs, 16px);
53
- font-weight: 600;
55
+ font-weight: var(--wui-card-title-fw, 600);
54
56
  line-height: var(--wui-card-title-lh, 1.3);
55
57
  color: var(--color-text);
56
58
  }
@@ -0,0 +1,56 @@
1
+ // ── Cell ──
2
+ // AppCell / <app-cell>: one table or list cell with a primary line and an optional secondary line.
3
+ // Markup contract, shared by both surfaces and the rules below:
4
+ //
5
+ // Component: React AppCell Razor <app-cell>
6
+ // Props: primary string|node Razor: attribute `primary`, or the tag child content when `primary` is absent
7
+ // secondary string|node Razor: attribute `secondary`; absent or empty draws no second line
8
+ // href string when set the primary is an <a>, else a <span>
9
+ // mono bool, false monospace on the primary line only
10
+ // numeric bool, false right-aligned, tabular digits, both lines
11
+ // className string React; Razor uses plain `class`
12
+ // Markup (identical on both surfaces, class order fixed):
13
+ // <div class="wui-cell [wui-cell-numeric] [wui-cell-mono] [extra]" data-wui="AppCell">
14
+ // <a|span class="wui-cell-primary" [href="..."]>PRIMARY</a|span>
15
+ // <span class="wui-cell-secondary">SECONDARY</span> (omitted when no secondary)
16
+ // </div>
17
+ // Empty primary (null, undefined, "" and no child content) draws the placeholder "-" (React EMPTY_PLACEHOLDER from ../lib/emptyValue, Razor EmptyValue.Placeholder).
18
+ // 0 is real data and draws as "0".
19
+ // Classes (all new, all wui-*): wui-cell, wui-cell-primary, wui-cell-secondary, wui-cell-numeric, wui-cell-mono
20
+ //
21
+ // Part of the wui-components layer, see _wui-components.scss for load order.
22
+
23
+ .wui-cell {
24
+ display: block;
25
+ min-width: 0;
26
+ line-height: var(--wui-lh-heading);
27
+ }
28
+
29
+ .wui-cell-primary {
30
+ display: block;
31
+ font-weight: var(--wui-fw-medium);
32
+ color: var(--color-text);
33
+ }
34
+
35
+ a.wui-cell-primary {
36
+ text-decoration: none;
37
+
38
+ &:hover {
39
+ text-decoration: underline;
40
+ }
41
+ }
42
+
43
+ .wui-cell-secondary {
44
+ display: block;
45
+ font-size: var(--wui-fs-sm);
46
+ color: var(--color-text-muted);
47
+ }
48
+
49
+ .wui-cell-numeric {
50
+ text-align: right;
51
+ font-variant-numeric: tabular-nums;
52
+ }
53
+
54
+ .wui-cell-mono > .wui-cell-primary {
55
+ font-family: var(--wui-font-mono);
56
+ }
@@ -336,13 +336,6 @@
336
336
  font-size: 16px;
337
337
  border-radius: var(--wui-field-lg-radius);
338
338
  }
339
- // xl: the 52px select, the same height as an AppInput at size="xl". A literal height, as on the input.
340
- .wui-search-select-xl .search-select-control {
341
- height: 52px;
342
- min-height: 52px;
343
- font-size: 16px;
344
- border-radius: var(--wui-field-lg-radius);
345
- }
346
339
  // Multi-select grows with its chips, so a size sets the FLOOR (min-height from the rule
347
340
  // above) but never a fixed height — reset height:auto so a second row of chips can appear.
348
341
  .search-select-sm .search-select-multi,
@@ -1238,7 +1231,8 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1238
1231
  // NOT scoped under `.country-dropdown` — <wui-phone> renders the toggle inside
1239
1232
  // `.phone-country-adornment`, with no such wrapper. Scoping it there left the
1240
1233
  // button on raw UA styling (2px outset border + grey fill) inside the field.
1241
- .country-toggle {
1234
+ .country-toggle,
1235
+ .wui-phone-prefix {
1242
1236
  display: flex;
1243
1237
  align-items: center;
1244
1238
  gap: 6px;
@@ -1276,6 +1270,10 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1276
1270
  cursor: not-allowed;
1277
1271
  opacity: var(--wui-disabled-opacity);
1278
1272
  }
1273
+ // A locked country: the dial code is a static prefix, laid out like the toggle but not a control.
1274
+ .wui-phone-prefix {
1275
+ cursor: default;
1276
+ }
1279
1277
  // Flag glyphs come from the generated .wui-flag-* classes (_flags.scss).
1280
1278
  .app-phone-number-input .phone-chevron {
1281
1279
  color: var(--color-text-muted);
@@ -479,15 +479,6 @@
479
479
  font-size: 16px;
480
480
  border-radius: var(--wui-field-lg-radius);
481
481
  }
482
- // xl (size="xl"): the 52px control for large forms. A literal height, because the control-height
483
- // ladder stops at 44px and its tokens live in the theme's token file. A floating label needs no
484
- // rule of its own: it centres on the field's box, so it stays inside at 52px.
485
- .wui-input-xl {
486
- height: 52px;
487
- padding: 9px 16px;
488
- font-size: 16px;
489
- border-radius: var(--wui-field-lg-radius);
490
- }
491
482
  .textarea,
492
483
  textarea.form-control {
493
484
  height: auto;
@@ -676,6 +667,8 @@ textarea.form-control.autosize {
676
667
  // Tighten the space above the message: .field's 10px gap → effective 8px, without
677
668
  // touching the label→control gap.
678
669
  margin-top: -2px;
670
+ // Start-aligned, so a message under a centred container sits under the field's edge, not its middle.
671
+ text-align: start;
679
672
  }
680
673
 
681
674
  // The message line under a field: the hint and the error share one grid cell, so the error takes
@@ -915,8 +908,7 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
915
908
  --wui-input-icon-size: 14px;
916
909
  --wui-input-icon-gap: 10px;
917
910
  }
918
- .input-icon:has(> .input-lg),
919
- .input-icon:has(> .wui-input-xl) {
911
+ .input-icon:has(> .input-lg) {
920
912
  --wui-input-icon-slot: 48px;
921
913
  --wui-input-icon-size: 18px;
922
914
  --wui-input-icon-gap: 14px;
@@ -1069,6 +1061,8 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
1069
1061
  // Soft-filled at rest, white on focus — one shared fill for the whole control so the
1070
1062
  // prefix/suffix and the input body are always the SAME colour combination as the field.
1071
1063
  background: var(--color-field-bg);
1064
+ // Clip the square inner input to the wrapper's rounded corners on hover and focus.
1065
+ overflow: clip;
1072
1066
  transition:
1073
1067
  border-color 0.12s ease,
1074
1068
  background-color 0.12s ease;
@@ -1091,6 +1085,8 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
1091
1085
  border: 0;
1092
1086
  border-radius: 0;
1093
1087
  background: transparent;
1088
+ // The wrapper's focus border is the cue (.input-affixed:focus-within above), so the inner outline would only poke past the corners.
1089
+ outline: none;
1094
1090
  }
1095
1091
  // A select in the affixed shell (AppSelect prefix/suffix): the custom element is the flex
1096
1092
  // item, and its inner .input control already loses its own border through the rule above.
@@ -1250,6 +1246,7 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
1250
1246
  margin-top: 2px;
1251
1247
  font-size: 12px;
1252
1248
  color: var(--color-text-subtle);
1249
+ text-align: start;
1253
1250
  }
1254
1251
  // Character counter — a right-aligned count under the field; turns danger at the limit.
1255
1252
  .field-count {
@@ -1470,9 +1467,6 @@ wui-select[compact] {
1470
1467
  .input-with-unit .num-input.input-lg {
1471
1468
  height: calc(var(--wui-control-lg) - 2px);
1472
1469
  }
1473
- .input-with-unit .num-input.wui-input-xl {
1474
- height: calc(52px - 2px);
1475
- }
1476
1470
  .input-with-unit .num-input:focus {
1477
1471
  box-shadow: none;
1478
1472
  }
@@ -97,10 +97,11 @@
97
97
  }
98
98
 
99
99
  // ── Table ──
100
- // DataTable cell helpers
100
+ // DataTable cell helpers. End-aligned cells use tabular digits so the figures in a column line up.
101
101
  .table-ledger .wui-cell-end,
102
102
  .table-ledger thead th.wui-cell-end {
103
103
  text-align: right;
104
+ font-variant-numeric: tabular-nums;
104
105
  }
105
106
 
106
107
  // Row expansion (renderExpanded) — a narrow leading toggle column, a chevron that rotates when
@@ -226,8 +227,12 @@
226
227
  .table-ledger tbody tr:focus-within {
227
228
  --bs-table-bg-state: var(--color-bg-muted);
228
229
  }
230
+ // The identity block is a flex child of its cell: without a zero floor it keeps a long email's full
231
+ // width inside a phone card and the subline's truncation never starts. Column sizing is unaffected,
232
+ // because a min-width of zero lowers no min-content contribution.
229
233
  .table-ledger .u-meta {
230
234
  gap: 2px;
235
+ min-width: 0;
231
236
  }
232
237
  .table-ledger .u-name {
233
238
  min-width: 0;
@@ -10,6 +10,7 @@
10
10
  // so the slide never runs underneath it. Close stays Bootstrap's own quick fade of the modal.
11
11
  // Side panels (offcanvas, drawers) keep their slide: they come from an edge by nature.
12
12
  //
13
+ // The status icon in a dialog header springs in with the dialog (rules below).
13
14
  // Imported after menu-motion, whose wui-menu-zoom keyframes it reuses.
14
15
 
15
16
  :root {
@@ -27,8 +28,97 @@
27
28
  animation: var(--wui-dialog-anim) var(--wui-dialog-anim-duration) var(--wui-dialog-anim-ease);
28
29
  }
29
30
 
31
+ // The three beats of an SVG tone (error) run 600ms end to end and overlap, so the icon reads as
32
+ // one movement. The rules key on position, so a new SVG tone inherits the same beats.
33
+ // The origin is the icon's centre, not the viewBox corner, so every scale grows in place.
34
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show wui-status-icon {
35
+ display: inline-flex;
36
+ }
37
+
38
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show wui-status-icon > svg > rect,
39
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show wui-status-icon > svg > g {
40
+ transform-box: view-box;
41
+ transform-origin: 87px 87px;
42
+ }
43
+
44
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show wui-status-icon > svg > rect:first-child {
45
+ animation: wui-dialog-icon-halo 260ms var(--wui-ease-out) 0ms both;
46
+ }
47
+
48
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show wui-status-icon > svg > rect:nth-child(2) {
49
+ animation: wui-dialog-icon-ring 300ms var(--wui-ease-out) 140ms both;
50
+ }
51
+
52
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show wui-status-icon > svg > g {
53
+ animation: wui-dialog-icon-core 340ms var(--wui-ease-elastic) 260ms both;
54
+ }
55
+
56
+ // Raster tones get one beat because they have no parts to stage.
57
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show wui-status-icon > img {
58
+ animation: wui-dialog-icon-raster 340ms var(--wui-ease-elastic) 0ms both;
59
+ }
60
+
61
+ @keyframes wui-dialog-icon-halo {
62
+ from {
63
+ opacity: 0;
64
+ transform: scale(0.7);
65
+ }
66
+ to {
67
+ opacity: 1;
68
+ transform: scale(1);
69
+ }
70
+ }
71
+
72
+ @keyframes wui-dialog-icon-ring {
73
+ from {
74
+ opacity: 0;
75
+ transform: scale(0.8);
76
+ }
77
+ to {
78
+ opacity: 1;
79
+ transform: scale(1);
80
+ }
81
+ }
82
+
83
+ @keyframes wui-dialog-icon-core {
84
+ from {
85
+ opacity: 0;
86
+ transform: scale(0.55);
87
+ }
88
+ 60% {
89
+ opacity: 1;
90
+ }
91
+ to {
92
+ opacity: 1;
93
+ transform: scale(1);
94
+ }
95
+ }
96
+
97
+ @keyframes wui-dialog-icon-raster {
98
+ from {
99
+ opacity: 0;
100
+ transform: scale(0.55);
101
+ }
102
+ to {
103
+ opacity: 1;
104
+ transform: scale(1);
105
+ }
106
+ }
107
+
30
108
  @media (prefers-reduced-motion: reduce) {
31
109
  :root:not([data-wui-dialog-motion="slide"]) .modal.show .modal-dialog {
32
110
  animation: none;
33
111
  }
112
+
113
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show wui-status-icon > svg > :is(rect, g) {
114
+ animation: none;
115
+ opacity: 1;
116
+ transform: none;
117
+ }
118
+
119
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show wui-status-icon > img {
120
+ animation: none;
121
+ opacity: 1;
122
+ transform: none;
123
+ }
34
124
  }
@@ -63,6 +63,8 @@
63
63
  }
64
64
  .kpi-value {
65
65
  margin-top: 6px;
66
+ // A product sets the figure's face with --wui-kpi-value-font-family; unset, it inherits the page font.
67
+ font-family: var(--wui-kpi-value-font-family, inherit);
66
68
  // The value ("main heading") size flows from one var, so a size tier (sm/md/lg,
67
69
  // spotlight) or a custom `valueSize` (px) can drive it. Default (md) is 24px.
68
70
  font-size: var(--wui-kpi-value-size, 24px);
@@ -188,7 +190,7 @@
188
190
  }
189
191
  .kpi.kpi-spotlight .kpi-value {
190
192
  margin-top: 14px;
191
- // No font-family: the value inherits the product's own font like every other tile.
193
+ // The face comes from --wui-kpi-value-font-family on .kpi-value, as on every other tile.
192
194
  // Size comes from --wui-kpi-value-size (36px default, set on the spotlight root above), so a
193
195
  // custom `valueSize` (px) can tame a too-large headline.
194
196
  line-height: 1;
@@ -1494,6 +1496,15 @@ button.wui-link {
1494
1496
  z-index: 0;
1495
1497
  white-space: var(--wui-link-label-white-space, nowrap);
1496
1498
  }
1499
+ // maxLines: the label wraps and stops at that many lines, the clamp drawing the ellipsis. The
1500
+ // underline sits 1px inside the label's bottom edge, so overflow: hidden does not clip it.
1501
+ .wui-link--clamp .wui-link__label {
1502
+ white-space: normal;
1503
+ display: -webkit-box;
1504
+ -webkit-box-orient: vertical;
1505
+ -webkit-line-clamp: var(--wui-link-max-lines, none);
1506
+ overflow: hidden;
1507
+ }
1497
1508
  // A link in a table cell may wrap: a nowrap label runs under the pinned actions column.
1498
1509
  .table-ledger td .wui-link__label {
1499
1510
  --wui-link-label-white-space: normal;
@@ -0,0 +1,50 @@
1
+ // The busy state of a form field (React loading, Razor loading, <wui-*> loading). Part of the wui-components layer, see _wui-components.scss for load order.
2
+
3
+ @property --wui-busy-angle {
4
+ syntax: "<angle>";
5
+ inherits: false;
6
+ initial-value: 0deg;
7
+ }
8
+ @keyframes wui-field-busy-spin {
9
+ to {
10
+ --wui-busy-angle: 360deg;
11
+ }
12
+ }
13
+
14
+ .wui-field-busy {
15
+ cursor: progress;
16
+ pointer-events: none;
17
+ }
18
+
19
+ .input.wui-field-busy,
20
+ .textarea.wui-field-busy,
21
+ .form-control.wui-field-busy,
22
+ .select.wui-field-busy {
23
+ padding-inline-end: var(--wui-space-2xl);
24
+ background-repeat: no-repeat;
25
+ background-position: right 12px center;
26
+ background-size: 18px 18px;
27
+ background-image:
28
+ radial-gradient(circle, var(--color-surface) 0 5px, transparent 5.5px),
29
+ conic-gradient(from var(--wui-busy-angle), transparent 0 30%, var(--color-primary) 30% 100%);
30
+ animation: wui-field-busy-spin 0.7s linear infinite;
31
+ }
32
+
33
+ @media (prefers-reduced-motion: reduce) {
34
+ .input.wui-field-busy,
35
+ .textarea.wui-field-busy,
36
+ .form-control.wui-field-busy,
37
+ .select.wui-field-busy {
38
+ animation: none;
39
+ }
40
+ }
41
+
42
+ .form-check-input.wui-field-busy,
43
+ .segmented.wui-field-busy,
44
+ .struct-card.wui-field-busy,
45
+ .choice-chip.wui-field-busy,
46
+ .wui-rating.wui-field-busy,
47
+ .slider.wui-field-busy,
48
+ .otp.wui-field-busy {
49
+ opacity: var(--wui-disabled-opacity);
50
+ }
@@ -39,10 +39,6 @@
39
39
  font-size: max(16px, #{$form-select-font-size-lg});
40
40
  }
41
41
 
42
- .wui-input-xl {
43
- font-size: max(16px, 16px);
44
- }
45
-
46
42
  input,
47
43
  textarea,
48
44
  select {