@webority/theme 0.7.8 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/theme",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "Webority shared design system — Bootstrap 5.3 SCSS overrides, --wui-* tokens, and shared component CSS. The single source of brand chrome for React portals and Razor sites.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": {
@@ -18,6 +18,10 @@
18
18
  // This file is now an INDEX. The rules live in ./wui/*.scss, split by domain
19
19
  // because one 5,000-line stylesheet made finding a component a grep.
20
20
  //
21
+ // Shared tone-pill mixin — used by .badge, .status-label, and .chip.
22
+ // Must be imported before components that use it.
23
+ @import "wui/tone-pill";
24
+
21
25
  // The order below is LOAD-BEARING. A later partial may deliberately override an
22
26
  // earlier one, so reordering these imports silently changes the cascade — and
23
27
  // nothing in the build will complain. `pnpm check:theme` measures the rules that
@@ -47,7 +51,7 @@
47
51
  // and nothing failing anywhere to point at the cause. The component shipped; its styling did
48
52
  // not. That is the same escape as th.sortable and .u-name, and the fix is the same principle:
49
53
  // a component and the CSS it requires travel together.
50
- //
54
+
51
55
  // It is not free — roughly 183KB raw, 45KB gzipped, mostly inlined SVG. A product that never
52
56
  // renders a phone field can skip it by importing the `wui/*` partials above individually
53
57
  // instead of this aggregate; that is the deliberate opt-out, and it is a choice rather than
@@ -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,
@@ -1050,30 +1068,21 @@ wui-select[compact] {
1050
1068
  border: 1px solid var(--color-border);
1051
1069
  }
1052
1070
  .badge-primary {
1053
- background: var(--color-primary-soft);
1054
- color: var(--color-primary);
1071
+ @include wui-tone-pill-colors(primary, var(--color-primary));
1055
1072
  border-color: color-mix(in srgb, var(--color-primary) 25%, transparent);
1056
1073
  }
1057
1074
  .badge-success {
1058
- background: var(--color-success-soft);
1059
- color: var(--color-success-text);
1060
- border-color: transparent;
1075
+ @include wui-tone-pill-colors(success);
1061
1076
  }
1062
1077
  .badge-warning,
1063
1078
  .badge-warn {
1064
- background: var(--color-warning-soft);
1065
- color: var(--color-warning-text);
1066
- border-color: transparent;
1079
+ @include wui-tone-pill-colors(warning);
1067
1080
  }
1068
1081
  .badge-danger {
1069
- background: var(--color-danger-soft);
1070
- color: var(--color-danger-text);
1071
- border-color: transparent;
1082
+ @include wui-tone-pill-colors(danger);
1072
1083
  }
1073
1084
  .badge-info {
1074
- background: var(--color-info-soft);
1075
- color: var(--color-info-text);
1076
- border-color: transparent;
1085
+ @include wui-tone-pill-colors(info);
1077
1086
  }
1078
1087
  .badge-dot::before {
1079
1088
  content: "";
@@ -1109,29 +1118,24 @@ wui-select[compact] {
1109
1118
  .status-label.success,
1110
1119
  .status-label.tone-ok,
1111
1120
  .status-label.tone-green {
1112
- background: color-mix(in srgb, var(--color-success) 16%, transparent);
1113
- color: var(--color-success-text);
1121
+ @include wui-tone-pill-colors(success);
1114
1122
  }
1115
1123
  .status-label.warning,
1116
1124
  .status-label.tone-warn,
1117
1125
  .status-label.tone-amber {
1118
- background: color-mix(in srgb, var(--color-warning) 16%, transparent);
1119
- color: var(--color-warning-text);
1126
+ @include wui-tone-pill-colors(warning);
1120
1127
  }
1121
1128
  .status-label.danger,
1122
1129
  .status-label.tone-bad,
1123
1130
  .status-label.tone-red {
1124
- background: color-mix(in srgb, var(--color-danger) 16%, transparent);
1125
- color: var(--color-danger-text);
1131
+ @include wui-tone-pill-colors(danger);
1126
1132
  }
1127
1133
  .status-label.info,
1128
1134
  .status-label.tone-blue {
1129
- background: color-mix(in srgb, var(--color-info) 16%, transparent);
1130
- color: var(--color-info-text);
1135
+ @include wui-tone-pill-colors(info);
1131
1136
  }
1132
1137
  .status-label.primary {
1133
- background: var(--color-primary-soft);
1134
- color: var(--color-primary);
1138
+ @include wui-tone-pill-colors(primary, var(--color-primary));
1135
1139
  }
1136
1140
  // Default/neutral — also the bare `.status-label` look, so an unknown or omitted
1137
1141
  // tone still renders as a proper pill instead of transparent text.
@@ -5,9 +5,24 @@
5
5
  // partials are a split of one stylesheet, not independent modules, so a rule
6
6
  // here may be overridden by a later partial. Never reorder the imports.
7
7
 
8
- // Wizard stepper. Two variants of one component:
8
+ // Wizard stepper. Three variants of one component:
9
9
  // .stepper — compact: numbered circle above a label, line behind.
10
10
  // .stepper-journey — wide: 40px dot beside a label + title, for a page masthead.
11
+ // .stepper-icons — large connected icon badges with a "STEP n" eyebrow.
12
+ //
13
+ // Fully themable through a small set of `--wui-step-*` custom properties — set them on
14
+ // the stepper (or the wizard host, or any ancestor: they inherit), or flip the whole
15
+ // accent with a `.wui-step-tone-*` class. Every colour/size below reads a var with the
16
+ // historical value as the fallback, so an UNSET wizard looks exactly as before while a
17
+ // product can recolour or resize it without overriding a single selector:
18
+ // --wui-step-accent solid state colour (active fill, done fill/rail, active eyebrow)
19
+ // --wui-step-accent-fg text/icon on the accent fill
20
+ // --wui-step-soft soft tint (active ring; the journey/icons "done" badge bg)
21
+ // --wui-step-soft-fg text/icon on the soft tint (the "done" badge check)
22
+ // --wui-step-connector the incomplete-rail colour
23
+ // --wui-step-size compact/vertical badge diameter (default 28px)
24
+ // --wui-step-badge-size icons-variant badge diameter (default 56px)
25
+ // The icons variant defaults its accent to success (green); the others to primary.
11
26
  // ============================================================================
12
27
  .stepper {
13
28
  // Full width so the flex:1 steps spread evenly across the container. Without it,
@@ -34,23 +49,23 @@
34
49
  .step + .step::before {
35
50
  content: "";
36
51
  position: absolute;
37
- // 13px = half the 28px circle, minus 1 for the 2px line's own height.
38
- top: 13px;
39
- left: calc(-50% + 18px);
40
- right: calc(50% + 18px);
52
+ // Centred on the badge: half the diameter, minus 1 for the 2px line's own height.
53
+ top: calc(var(--wui-step-size, 28px) / 2 - 1px);
54
+ left: calc(-50% + var(--wui-step-size, 28px) / 2 + 4px);
55
+ right: calc(50% + var(--wui-step-size, 28px) / 2 + 4px);
41
56
  height: 2px;
42
- background: var(--color-border);
57
+ background: var(--wui-step-connector, var(--color-border));
43
58
  z-index: 0;
44
59
  }
45
60
  .step.done + .step::before {
46
- background: var(--color-primary);
61
+ background: var(--wui-step-accent, var(--color-primary));
47
62
  }
48
63
  .step-circle {
49
64
  position: relative;
50
65
  z-index: 1;
51
66
  flex: none;
52
- width: 28px;
53
- height: 28px;
67
+ width: var(--wui-step-size, 28px);
68
+ height: var(--wui-step-size, 28px);
54
69
  display: inline-grid;
55
70
  place-items: center;
56
71
  border: 1.5px solid var(--color-border);
@@ -61,15 +76,15 @@
61
76
  color: var(--color-text-subtle);
62
77
  }
63
78
  .step.active .step-circle {
64
- border-color: var(--color-primary);
65
- background: var(--color-primary);
66
- color: var(--color-on-scrim);
67
- box-shadow: 0 0 0 4px var(--color-primary-soft);
79
+ border-color: var(--wui-step-accent, var(--color-primary));
80
+ background: var(--wui-step-accent, var(--color-primary));
81
+ color: var(--wui-step-accent-fg, var(--color-on-scrim));
82
+ box-shadow: 0 0 0 4px var(--wui-step-soft, var(--color-primary-soft));
68
83
  }
69
84
  .step.done .step-circle {
70
- border-color: var(--color-primary);
71
- background: var(--color-primary);
72
- color: var(--color-on-scrim);
85
+ border-color: var(--wui-step-accent, var(--color-primary));
86
+ background: var(--wui-step-accent, var(--color-primary));
87
+ color: var(--wui-step-accent-fg, var(--color-on-scrim));
73
88
  }
74
89
  .step-label {
75
90
  max-width: 130px;
@@ -101,12 +116,12 @@ button.step {
101
116
  }
102
117
  a.step:hover .step-circle,
103
118
  button.step:hover .step-circle {
104
- box-shadow: 0 0 0 4px var(--color-primary-soft);
119
+ box-shadow: 0 0 0 4px var(--wui-step-soft, var(--color-primary-soft));
105
120
  transform: scale(1.04);
106
121
  }
107
122
  a.step:hover .step-label,
108
123
  button.step:hover .step-label {
109
- color: var(--color-primary);
124
+ color: var(--wui-step-accent, var(--color-primary));
110
125
  }
111
126
  a.step:focus-visible,
112
127
  button.step:focus-visible {
@@ -116,8 +131,8 @@ button.step:focus-visible {
116
131
  a.step:focus-visible .step-circle,
117
132
  button.step:focus-visible .step-circle {
118
133
  box-shadow:
119
- 0 0 0 4px var(--color-primary-soft),
120
- 0 0 0 6px var(--color-primary);
134
+ 0 0 0 4px var(--wui-step-soft, var(--color-primary-soft)),
135
+ 0 0 0 6px var(--wui-step-accent, var(--color-primary));
121
136
  }
122
137
  // A step that failed its stage — a red circle with an alert, so an invalid step
123
138
  // reads as wrong at a glance rather than merely "not done".
@@ -173,12 +188,12 @@ button.step:focus-visible .step-circle {
173
188
  transform 0.2s ease;
174
189
  }
175
190
  .stepper-journey .step.done .step-circle {
176
- background: var(--color-success-soft);
177
- color: var(--color-success-text);
191
+ background: var(--wui-step-soft, var(--color-success-soft));
192
+ color: var(--wui-step-soft-fg, var(--color-success-text));
178
193
  }
179
194
  .stepper-journey .step.active .step-circle {
180
- background: var(--color-primary);
181
- color: var(--color-on-scrim);
195
+ background: var(--wui-step-accent, var(--color-primary));
196
+ color: var(--wui-step-accent-fg, var(--color-on-scrim));
182
197
  box-shadow: none;
183
198
  transform: scale(1.06);
184
199
  }
@@ -199,10 +214,10 @@ button.step:focus-visible .step-circle {
199
214
  color: var(--color-text-subtle);
200
215
  }
201
216
  .stepper-journey .step.done .step-index {
202
- color: var(--color-success-text);
217
+ color: var(--wui-step-soft-fg, var(--color-success-text));
203
218
  }
204
219
  .stepper-journey .step.active .step-index {
205
- color: var(--color-primary);
220
+ color: var(--wui-step-accent, var(--color-primary));
206
221
  }
207
222
  .stepper-journey .step.error .step-circle {
208
223
  background: var(--color-danger-soft);
@@ -240,7 +255,7 @@ button.step:focus-visible .step-circle {
240
255
  background: var(--color-border-strong);
241
256
  }
242
257
  .stepper-journey .step-line.done {
243
- background: var(--color-success);
258
+ background: var(--wui-step-accent, var(--color-success));
244
259
  }
245
260
  // Below xl the four label+title pairs no longer fit side by side, so the bar
246
261
  // becomes a vertical list and the connectors go away.
@@ -270,7 +285,7 @@ button.step:focus-visible .step-circle {
270
285
  // left, the label and its description stacked on the right.
271
286
  .stepper-vertical .step {
272
287
  display: grid;
273
- grid-template-columns: 28px 1fr;
288
+ grid-template-columns: var(--wui-step-size, 28px) 1fr;
274
289
  column-gap: 12px;
275
290
  // No gap between the label and its description — they read as one block; the
276
291
  // base .step gap of 10px is for the horizontal circle→label spacing.
@@ -307,18 +322,18 @@ button.step:focus-visible .step-circle {
307
322
  .stepper-vertical .step:not(:last-child)::after {
308
323
  content: "";
309
324
  position: absolute;
310
- // 13px = half the 28px circle minus 1 for the 2px line's own width.
311
- left: 13px;
312
- top: 28px;
325
+ // Half the badge minus 1 for the 2px line's own width; runs from below the circle.
326
+ left: calc(var(--wui-step-size, 28px) / 2 - 1px);
327
+ top: var(--wui-step-size, 28px);
313
328
  // Reaches through the 18px inter-step gap to meet the next circle's top; keep
314
329
  // this in sync with the .stepper-vertical `gap`.
315
330
  bottom: -18px;
316
331
  width: 2px;
317
- background: var(--color-border);
332
+ background: var(--wui-step-connector, var(--color-border));
318
333
  z-index: 0;
319
334
  }
320
335
  .stepper-vertical .step.done:not(:last-child)::after {
321
- background: var(--color-primary);
336
+ background: var(--wui-step-accent, var(--color-primary));
322
337
  }
323
338
  // The journey bar stacked vertically reuses its own narrow-width treatment: a plain
324
339
  // column with the horizontal connectors dropped (a real vertical rail here would
@@ -332,15 +347,169 @@ button.step:focus-visible .step-circle {
332
347
  display: none;
333
348
  }
334
349
 
350
+ // .stepper-icons — large icon badges joined by a rail, with a "STEP n" eyebrow above
351
+ // each title. Done tints green with a filled connector; active is a solid primary
352
+ // badge; pending is a muted badge. Built for icon-per-step flows — each step sets `icon`
353
+ // (a step with no icon falls back to its number).
354
+ // ----------------------------------------------------------------------------
355
+ .stepper-icons {
356
+ align-items: flex-start;
357
+ padding: 8px 8px 0;
358
+ }
359
+ .stepper-icons .step {
360
+ gap: 6px;
361
+ }
362
+ .stepper-icons .step-circle {
363
+ width: var(--wui-step-badge-size, 56px);
364
+ height: var(--wui-step-badge-size, 56px);
365
+ border: none;
366
+ background: var(--color-bg-muted);
367
+ color: var(--color-text-subtle);
368
+ font-size: 22px;
369
+ transition:
370
+ background 0.2s ease,
371
+ color 0.2s ease,
372
+ transform 0.2s ease;
373
+ }
374
+ // The icons variant defaults its accent to success (green), so an untoned icons wizard
375
+ // reads green — solid-green active badge, soft-green done badge, green rail. Every colour
376
+ // still routes through the shared --wui-step-* vars, so a tone class or an override recolours it.
377
+ .stepper-icons .step.active .step-circle {
378
+ background: var(--wui-step-accent, var(--color-success));
379
+ color: var(--wui-step-accent-fg, var(--color-on-scrim));
380
+ // The compact variant rings the active circle; here the solid fill carries it, so
381
+ // drop the ring to match the flat badge look.
382
+ box-shadow: none;
383
+ }
384
+ .stepper-icons .step.done .step-circle {
385
+ border: none;
386
+ background: var(--wui-step-soft, var(--color-success-soft));
387
+ color: var(--wui-step-soft-fg, var(--color-success-text));
388
+ }
389
+ .stepper-icons .step.error .step-circle {
390
+ background: var(--color-danger-soft);
391
+ color: var(--color-danger-text);
392
+ }
393
+ .stepper-icons .step.skipped .step-circle {
394
+ background: var(--color-bg-muted);
395
+ border: 1px dashed var(--color-border-strong);
396
+ color: var(--color-text-muted);
397
+ }
398
+ // The "STEP n" eyebrow sits between the badge and the title, colour-matched to the state.
399
+ .stepper-icons .step-index {
400
+ margin-top: 4px;
401
+ font-size: 12px;
402
+ font-weight: 600;
403
+ line-height: 1.2;
404
+ text-transform: uppercase;
405
+ letter-spacing: 0.06em;
406
+ color: var(--color-text-subtle);
407
+ }
408
+ .stepper-icons .step.active .step-index {
409
+ color: var(--wui-step-accent, var(--color-success));
410
+ }
411
+ .stepper-icons .step.done .step-index {
412
+ color: var(--wui-step-soft-fg, var(--color-success-text));
413
+ }
414
+ .stepper-icons .step.error .step-index {
415
+ color: var(--color-danger-text);
416
+ }
417
+ .stepper-icons .step-label {
418
+ max-width: 150px;
419
+ font-size: 15px;
420
+ font-weight: 600;
421
+ color: var(--color-text-subtle);
422
+ }
423
+ .stepper-icons .step.active .step-label,
424
+ .stepper-icons .step.done .step-label {
425
+ color: var(--color-text);
426
+ }
427
+ // The rail: thicker and rounded, recentred on the badge (half the diameter − 2 for the
428
+ // 4px line's own height), and filled with the accent once the step to its left is done.
429
+ .stepper-icons .step + .step::before {
430
+ top: calc(var(--wui-step-badge-size, 56px) / 2 - 2px);
431
+ left: calc(-50% + var(--wui-step-badge-size, 56px) / 2 + 8px);
432
+ right: calc(50% + var(--wui-step-badge-size, 56px) / 2 + 8px);
433
+ height: 4px;
434
+ border-radius: 999px;
435
+ background: var(--wui-step-connector, var(--color-border));
436
+ }
437
+ .stepper-icons .step.done + .step::before {
438
+ background: var(--wui-step-accent, var(--color-success));
439
+ }
440
+ // The compact hover ring is calibrated to the 28px badge; the 56px icon badge just
441
+ // lifts on hover instead.
442
+ .stepper-icons a.step:hover .step-circle,
443
+ .stepper-icons button.step:hover .step-circle {
444
+ box-shadow: none;
445
+ transform: scale(1.04);
446
+ }
447
+ // Below md the six badges no longer fit across, so the icons stepper stacks into a
448
+ // vertical list — the shared .stepper-vertical rules take over from here.
449
+ @include media-breakpoint-down(md) {
450
+ .stepper-icons.stepper-icons {
451
+ flex-direction: column;
452
+ align-items: stretch;
453
+ gap: 20px;
454
+ }
455
+ .stepper-icons.stepper-icons .step {
456
+ flex-direction: row;
457
+ align-items: center;
458
+ gap: 14px;
459
+ text-align: left;
460
+ }
461
+ .stepper-icons.stepper-icons .step + .step::before {
462
+ content: none;
463
+ }
464
+ }
465
+
335
466
  @media (prefers-reduced-motion: reduce) {
336
467
  a.step:hover .step-circle,
337
468
  button.step:hover .step-circle,
338
- .stepper-journey .step-circle {
469
+ .stepper-journey .step-circle,
470
+ .stepper-icons .step-circle {
339
471
  transition: none;
340
472
  transform: none;
341
473
  }
342
474
  }
343
475
 
476
+ // Tone presets — flip the whole accent (active fill, done rail, ring, journey/icons
477
+ // done tint) to a semantic ramp in one class. Because custom properties inherit, the
478
+ // class can sit on the stepper itself OR on an ancestor (the wizard host), and an inline
479
+ // `style="--wui-step-accent: …"` on a closer element still wins over it. Placing the
480
+ // class recolours every variant consistently; error steps stay red regardless (semantic).
481
+ // ----------------------------------------------------------------------------
482
+ .wui-step-tone-primary {
483
+ --wui-step-accent: var(--color-primary);
484
+ --wui-step-accent-fg: var(--color-on-scrim);
485
+ --wui-step-soft: var(--color-primary-soft);
486
+ --wui-step-soft-fg: var(--color-primary);
487
+ }
488
+ .wui-step-tone-success {
489
+ --wui-step-accent: var(--color-success);
490
+ --wui-step-accent-fg: var(--color-on-scrim);
491
+ --wui-step-soft: var(--color-success-soft);
492
+ --wui-step-soft-fg: var(--color-success-text);
493
+ }
494
+ .wui-step-tone-warning {
495
+ --wui-step-accent: var(--color-warning);
496
+ --wui-step-accent-fg: var(--color-on-scrim);
497
+ --wui-step-soft: var(--color-warning-soft);
498
+ --wui-step-soft-fg: var(--color-warning-text);
499
+ }
500
+ .wui-step-tone-danger {
501
+ --wui-step-accent: var(--color-danger);
502
+ --wui-step-accent-fg: var(--color-on-scrim);
503
+ --wui-step-soft: var(--color-danger-soft);
504
+ --wui-step-soft-fg: var(--color-danger-text);
505
+ }
506
+ .wui-step-tone-info {
507
+ --wui-step-accent: var(--color-info);
508
+ --wui-step-accent-fg: var(--color-on-scrim);
509
+ --wui-step-soft: var(--color-info-soft);
510
+ --wui-step-soft-fg: var(--color-info-text);
511
+ }
512
+
344
513
  // ============================================================================
345
514
  // Pricing card. A tier's name, price, pitch, feature list and CTA.
346
515
  //
@@ -616,4 +785,51 @@ button.step:focus-visible .step-circle {
616
785
  .form-actions-sticky {
617
786
  transition: none;
618
787
  }
788
+ }
789
+
790
+ // ============================================================================
791
+ // AppWizard (<wui-wizard>) — stepper header + one visible step panel + a sticky
792
+ // Back/Skip/Next/Finish footer. Composes .stepper and .form-actions above.
793
+ // ============================================================================
794
+ wui-wizard {
795
+ display: block;
796
+ }
797
+ // Custom elements don't inherit the UA `[hidden] { display: none }` once we give the
798
+ // tag its own `display`, so the inactive panels need it stated explicitly.
799
+ wui-wizard-step {
800
+ display: block;
801
+ }
802
+ wui-wizard-step[hidden] {
803
+ display: none;
804
+ }
805
+ .wizard-stepper {
806
+ margin-bottom: 24px;
807
+ }
808
+ // A skipped step reads as "passed over", not "achieved": a dashed, muted circle so it
809
+ // is visibly distinct from a ticked (done) step.
810
+ .step.skipped .step-circle {
811
+ background: var(--color-bg-muted);
812
+ border: 1px dashed var(--color-border-strong);
813
+ color: var(--color-text-muted);
814
+ }
815
+ .wizard-panel {
816
+ // A resting min-height so the footer doesn't jump as steps of different length
817
+ // swap in; content taller than this simply grows the panel.
818
+ min-height: 96px;
819
+ }
820
+ .wizard-footer {
821
+ // The sticky Back/Skip/Next/Finish bar — pinned to the bottom of a scrolling flow
822
+ // so the actions (and the Skip on an optional step) are always in reach.
823
+ position: sticky;
824
+ bottom: 0;
825
+ margin-top: 24px;
826
+ padding: 16px 0;
827
+ border-top: 1px solid var(--color-border);
828
+ background: var(--color-surface);
829
+ z-index: 1;
830
+ }
831
+ .wizard-footer-actions {
832
+ display: flex;
833
+ align-items: center;
834
+ gap: 12px;
619
835
  }
@@ -0,0 +1,23 @@
1
+ // ── Shared tone-pill color mixin ──
2
+ // Consolidates the tone→color mapping used by .badge, .status-label, and .chip:
3
+ // background: var(--color-{tone}-soft)
4
+ // color: var(--color-{tone}-text) (override via $text — see below)
5
+ // border-color: transparent
6
+ //
7
+ // $text exists because the status tones and `primary` name their on-tint color
8
+ // differently: --color-{status}-text IS the on-tint hue, but --color-primary-text
9
+ // is the text-on-SOLID-primary color (near-black in dark mode) and fails AA on the
10
+ // soft tint — primary pills pass var(--color-primary) instead.
11
+ //
12
+ // Component-specific overrides (e.g. .badge-primary's colored border) are applied
13
+ // after the mixin call.
14
+
15
+ @mixin wui-tone-pill-colors($tone, $text: null) {
16
+ background: var(--color-#{$tone}-soft);
17
+ @if $text {
18
+ color: $text;
19
+ } @else {
20
+ color: var(--color-#{$tone}-text);
21
+ }
22
+ border-color: transparent;
23
+ }
@@ -121,29 +121,19 @@
121
121
 
122
122
  /* Tones — tinted surface + on-tint text; icon/remove inherit the tone colour. */
123
123
  .chip.primary {
124
- border-color: transparent;
125
- background: var(--color-primary-soft);
126
- color: var(--color-primary);
124
+ @include wui-tone-pill-colors(primary, var(--color-primary));
127
125
  }
128
126
  .chip.success {
129
- border-color: transparent;
130
- background: var(--color-success-soft);
131
- color: var(--color-success-text);
127
+ @include wui-tone-pill-colors(success);
132
128
  }
133
129
  .chip.warning {
134
- border-color: transparent;
135
- background: var(--color-warning-soft);
136
- color: var(--color-warning-text);
130
+ @include wui-tone-pill-colors(warning);
137
131
  }
138
132
  .chip.danger {
139
- border-color: transparent;
140
- background: var(--color-danger-soft);
141
- color: var(--color-danger-text);
133
+ @include wui-tone-pill-colors(danger);
142
134
  }
143
135
  .chip.info {
144
- border-color: transparent;
145
- background: var(--color-info-soft);
146
- color: var(--color-info-text);
136
+ @include wui-tone-pill-colors(info);
147
137
  }
148
138
  .chip.primary .chip-ic,
149
139
  .chip.success .chip-ic,
@@ -238,6 +228,25 @@
238
228
  min-width: 0;
239
229
  align-items: center;
240
230
  }
231
+ // Chips scale with the control size — the taller the field, the larger its tokens
232
+ // (the element always emits `.chip.chip-sm`; these override it per size tier). The
233
+ // two-class `.ms-chips .chip` outranks the single `.chip-sm`, so md is the base and
234
+ // the sm/lg tiers layer over it.
235
+ .ms-chips .chip {
236
+ gap: 5px;
237
+ padding: 3px 9px;
238
+ font-size: 0.8125rem;
239
+ }
240
+ .search-select-sm .ms-chips .chip {
241
+ gap: 4px;
242
+ padding: 2px 8px;
243
+ font-size: 0.75rem;
244
+ }
245
+ .search-select-lg .ms-chips .chip {
246
+ gap: 6px;
247
+ padding: 5px 12px;
248
+ font-size: 0.875rem;
249
+ }
241
250
  // Collapse mode (collapse-chips): chips stay on one row; the JS folds the overflow into
242
251
  // a "+N" counter (see WuiMultiSelect._collapseChips).
243
252
  wui-multiselect[collapse-chips] .ms-chips {
@@ -295,6 +304,27 @@ wui-multiselect[collapse-chips] .ms-chips {
295
304
  .ms-input:focus {
296
305
  outline: none;
297
306
  }
307
+ // AppTagInput lays the input on the LEFT with the chips filling and wrapping to its
308
+ // right (its template renders the input before the chips). Multiselect keeps its
309
+ // chips-first layout, so these overrides are scoped to `wui-taginput`.
310
+ wui-taginput .ms-input {
311
+ // Don't grow to fill — hold a modest resting width on the left; the chips take
312
+ // everything to the right. (align-items:center on the control centres the input
313
+ // against the chip row, so it needs no min-height of its own.)
314
+ flex: 0 1 auto;
315
+ flex-basis: 120px;
316
+ min-width: 96px;
317
+ }
318
+ wui-taginput .ms-chips {
319
+ flex: 1 1 auto;
320
+ }
321
+ // Match AppInput's per-size heights: the shared `.search-select-multi` floors md at
322
+ // 40px, but AppInput's md is the 44px control-height token — raise the floor so the
323
+ // sm/md/lg progression lines up with a text field beside it. (sm 36 / lg 52 already
324
+ // come from the size-specific `.search-select-{sm,lg} .search-select-control` rules.)
325
+ wui-taginput .search-select-multi {
326
+ min-height: var(--wui-control-height);
327
+ }
298
328
 
299
329
  /* ── Choice card: image / custom-template variant ── */
300
330
  .struct-card-img-card {
@@ -73,7 +73,6 @@
73
73
  background: transparent;
74
74
  color: var(--color-text-subtle);
75
75
  cursor: help;
76
- transform: scaleY(-1);
77
76
  transition: color 0.15s;
78
77
  }
79
78
  .infotip-btn:hover {
@@ -598,6 +597,35 @@
598
597
  color: var(--color-primary);
599
598
  }
600
599
 
600
+ // AppAlert extras — a leading icon and/or a bold heading line. Bootstrap already
601
+ // styles `.alert`/`.alert-{variant}`; these rules only lay out the icon column and
602
+ // give the plain `.alert-heading` div its type treatment (Bootstrap's own rule is
603
+ // just `color: inherit`, meant for an <h4>).
604
+ .alert-has-icon {
605
+ display: flex;
606
+ align-items: flex-start;
607
+ gap: 12px;
608
+ }
609
+ .alert-icon {
610
+ flex: none;
611
+ display: inline-flex;
612
+ // Nudge onto the first line's optical centre.
613
+ margin-top: 1px;
614
+ }
615
+ .alert-content {
616
+ flex: 1;
617
+ min-width: 0;
618
+ }
619
+ .alert .alert-heading {
620
+ margin-bottom: 2px;
621
+ font-size: 1rem;
622
+ font-weight: 700;
623
+ }
624
+ // The body's trailing paragraph shouldn't add space below the alert.
625
+ .alert .alert-content > :last-child {
626
+ margin-bottom: 0;
627
+ }
628
+
601
629
  // ============================================================================
602
630
  // Inline help glyph — a hint attached to a word rather than to a field label.
603
631
  // (For a field label use `.infotip`, which is a real popover button.)