@webority/theme 0.36.0 → 0.38.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/package.json CHANGED
@@ -1,22 +1,20 @@
1
1
  {
2
2
  "name": "@webority/theme",
3
- "version": "0.36.0",
3
+ "version": "0.38.0",
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": {
7
7
  "access": "public",
8
8
  "registry": "https://registry.npmjs.org/"
9
9
  },
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/Webority/webority-ui.git",
13
- "directory": "packages/theme"
14
- },
15
10
  "files": [
16
11
  "index.scss",
17
12
  "scss",
18
13
  "fonts",
19
- "dist"
14
+ "dist",
15
+ "!dist/**/__tests__",
16
+ "!dist/**/*.test.*",
17
+ "!dist/**/*.map"
20
18
  ],
21
19
  "exports": {
22
20
  ".": {
@@ -82,7 +82,7 @@
82
82
 
83
83
  .btn-danger {
84
84
  // White-on-red destructive look via the --wui-danger-strong token: white fails AA on the
85
- // brighter danger fills (#F8285A → 3.86:1, #E82646 → 4.38:1), so the button uses the
85
+ // brighter danger fill (#E82646 → 4.38:1), so the button uses the
86
86
  // deepened fill (5.80:1) that carries white cleanly. Token-driven so a rebrand tracks it;
87
87
  // the brighter --wui-danger stays for badges/dots that never carry white text.
88
88
  --bs-btn-color: #fff;
package/scss/_tokens.scss CHANGED
@@ -41,6 +41,10 @@
41
41
  // combobox/phone/date/number wrappers) — a touch lighter than --wui-bg-muted so fields read
42
42
  // as their own surface. Change here to restyle every field's background at once.
43
43
  --wui-field-bg: #F9FBFC;
44
+ // Resting and hover border of every input-like control. They clear WCAG 1.4.11's 3:1
45
+ // against the field fill and the surface, so a field's edge is visible without focus.
46
+ --wui-field-border: #80899F;
47
+ --wui-field-border-hover: #687088;
44
48
  --wui-border-light: #EBEDF3;
45
49
  --wui-border-soft: #F4F6F8;
46
50
  // The unfilled part of a progress bar / meter. Darker than every surface it can sit on
@@ -144,16 +148,16 @@
144
148
  // Shared by the portal (aliased to --color-*) and marketing alerts/badges.
145
149
  --wui-info-text: #1460ba;
146
150
  --wui-info-active: #056EE9;
147
- --wui-info: #1B84FF;
151
+ --wui-info: #056EE9;
148
152
  --wui-info-50: #8DC2FF;
149
153
  --wui-info-30: #BBDBFF;
150
154
  --wui-info-20: #D1E6FF;
151
155
  --wui-info-10: #E9F3FF;
152
156
  --wui-info-5: #F3F9FF;
153
157
 
154
- // `-text` variants exist because the status colours are FILLS. #F8285A and #04B440
158
+ // `-text` variants exist because the status colours are FILLS. #E82646 and #04B440
155
159
  // are correct on a button or a badge, and both fail AA as text on a light surface
156
- // (3.6:1 and 2.6:1) — which is how a validation error message, of all things, came
160
+ // (4.4:1 and 2.8:1) — which is how a validation error message, of all things, came
157
161
  // to be unreadable. Darkening the base tokens would change every red button and
158
162
  // green badge in the system, so text gets its own value and the fills keep theirs.
159
163
  --wui-success-text: #0e7531;
@@ -163,7 +167,7 @@
163
167
  // the other tokens so buttons/badges keep the vibrant fill.
164
168
  --wui-success-strong: #0e7531;
165
169
  --wui-success-active: #04B440;
166
- --wui-success: #17C653;
170
+ --wui-success: #04B440;
167
171
  --wui-success-50: #8BE3A9;
168
172
  --wui-success-30: #BAEECC;
169
173
  --wui-success-20: #D1F4DD;
@@ -172,7 +176,7 @@
172
176
 
173
177
  --wui-warning-text: #876100;
174
178
  --wui-warning-active: #DFA000;
175
- --wui-warning: #F6B100;
179
+ --wui-warning: #DFA000;
176
180
  --wui-warning-50: #FBD880;
177
181
  --wui-warning-30: #FDE8B3;
178
182
  --wui-warning-20: #FDEFCC;
@@ -192,7 +196,7 @@
192
196
  --wui-danger-strong: #C81340;
193
197
  --wui-danger-strong-hover: #AB1036;
194
198
  --wui-danger-active: #E82646;
195
- --wui-danger: #F8285A;
199
+ --wui-danger: #E82646;
196
200
  --wui-danger-50: #FC94AD;
197
201
  --wui-danger-30: #FDBFCE;
198
202
  --wui-danger-20: #FED4DE;
@@ -253,6 +257,9 @@
253
257
  --wui-dark-bg-surface: #181b23;
254
258
  --wui-dark-border: #2a2f3a;
255
259
  --wui-dark-border-strong:#3a414f;
260
+ // Field borders on the dark fills: 3:1 or better against the dark field fill and surface.
261
+ --wui-dark-field-border: #6E7586;
262
+ --wui-dark-field-border-hover: #8a90a0;
256
263
  // Lighter than the dark page, surface and muted fill, so the empty track shows on all three.
257
264
  --wui-dark-progress-track: #323845;
258
265
  --wui-dark-text: #e6e8ee;
@@ -336,6 +343,11 @@
336
343
  --wui-z-popover: 1600; // same rung as --wui-z-menu (above modal)
337
344
  --wui-z-tooltip: 1650;
338
345
  --wui-z-toast: 1700;
346
+
347
+ // The weekday every calendar's week starts on: the date picker, the date range and the event
348
+ // calendar. 0 = Sunday, 1 = Monday, through 6 = Saturday. A product sets it once here, a page or
349
+ // a section can set it on an ancestor, and a control's own first-day attribute wins over both.
350
+ --wui-first-day: 1;
339
351
  }
340
352
 
341
353
  // Bootstrap md / xl (not invented px)
@@ -20,6 +20,8 @@ $primary: #4F46E5 !default;
20
20
  // second brand colour should use a custom utility or token — do not repurpose
21
21
  // $secondary without a coordinated migration.
22
22
  $secondary: #4B5675 !default;
23
+ // The status colours match the base --wui-success/-info/-warning/-danger tokens, so a
24
+ // Bootstrap utility (.bg-success, .btn-info, --bs-danger) paints the same fill as the library.
23
25
  $success: #04B440 !default;
24
26
  $info: #056EE9 !default;
25
27
  $warning: #DFA000 !default;
@@ -18,12 +18,20 @@
18
18
  --color-border: var(--wui-border-light);
19
19
  --color-border-light: var(--wui-border-soft);
20
20
  --color-border-strong: var(--wui-text-5);
21
+ // The resting and hover border of an input-like control (3:1 against the field and surface).
22
+ // Decorative card and table borders stay on --color-border.
23
+ --color-field-border: var(--wui-field-border);
24
+ --color-field-border-hover: var(--wui-field-border-hover);
21
25
  --color-progress-track: var(--wui-progress-track);
22
26
  // Label text inside a progress bar fill.
23
27
  --color-progress-label: var(--wui-bg-light);
24
28
  // Ink on a solid status fill (success, warning, danger, info): near-black in both modes, since
25
29
  // white fails AA on the bright fills.
26
30
  --color-on-status-fill: #0F0F0F;
31
+ // Ink on a DEEP status fill (the -text tokens, used as a fill the way primary is): white in
32
+ // light mode, where those shades measure 5.5 to 6.2:1; near-black in dark mode, where -text is
33
+ // the bright fill.
34
+ --color-on-status-strong: #FFFFFF;
27
35
 
28
36
  // Text ramp
29
37
  --color-text: var(--wui-text-heading);
@@ -32,7 +40,9 @@
32
40
  // A dark-ink glyph such as Bootstrap's .btn-close needs no filter on a light surface.
33
41
  --color-close-filter: none;
34
42
  // The border of a focused / open / active control (input, select, OTP slot, dropzone).
35
- --color-focus-border: var(--wui-primary-30);
43
+ // It is the only focus cue on a field, so it must clear 3:1 against the field and surface
44
+ // (WCAG 1.4.11): the full primary, not a tint.
45
+ --color-focus-border: var(--wui-primary);
36
46
  --color-text-muted: var(--wui-text-2);
37
47
  --color-text-subtle: var(--wui-text-3);
38
48
 
@@ -153,6 +163,8 @@
153
163
  --color-border: var(--wui-dark-border);
154
164
  --color-border-light: color-mix(in srgb, var(--wui-dark-border) 50%, transparent);
155
165
  --color-border-strong: var(--wui-dark-border-strong);
166
+ --color-field-border: var(--wui-dark-field-border);
167
+ --color-field-border-hover: var(--wui-dark-field-border-hover);
156
168
  --color-progress-track: var(--wui-dark-progress-track);
157
169
  // Same near-black as --color-primary-text below: 4.97:1 or better on every bright fill.
158
170
  --color-progress-label: #0F0F0F;
@@ -199,29 +211,33 @@
199
211
  // Dark mode primary text: #fff on #6B83EC measures 3.5:1, below WCAG AA (4.5:1).
200
212
  // Map to a near-black for safe contrast (6.9:1 on --wui-dark-primary) when text sits on primary fill.
201
213
  --color-primary-text: #0F0F0F;
214
+ --color-on-status-strong: #0F0F0F;
202
215
 
203
- // On a dark surface the FILL colours are already the readable ones — the darkened
204
- // `-text` values would sink into the background. So dark mode maps text back to the
205
- // bright tokens rather than inheriting light mode's.
206
- --color-success-text: var(--wui-success);
207
- --color-danger-text: var(--wui-danger);
208
- --color-warning-text: var(--wui-warning);
209
- --color-info-text: var(--wui-info);
216
+ // On a dark surface the darkened `-text` values would sink into the background, so dark
217
+ // mode builds text from the fill instead, lifted with 25% white: the info and danger fills
218
+ // alone reach only 3.1:1 and 3.4:1 on the muted fill, and the lift clears 4.5:1 on every
219
+ // dark surface for all four tones.
220
+ --color-success-text: color-mix(in srgb, var(--wui-success) 75%, white);
221
+ --color-danger-text: color-mix(in srgb, var(--wui-danger) 75%, white);
222
+ --color-warning-text: color-mix(in srgb, var(--wui-warning) 75%, white);
223
+ --color-info-text: color-mix(in srgb, var(--wui-info) 75%, white);
210
224
 
211
225
  // Text ON a soft tint (an alert heading). The bright tone alone falls under WCAG AA on its own
212
226
  // 18% tint over the dark page (info and danger reach only ~3.8:1), so it is lifted with 30%
213
227
  // white: every tone then clears 4.5:1 on the dark page, surface and muted fill.
214
- --color-success-text-on-soft: color-mix(in srgb, var(--wui-success) 70%, white);
215
- --color-danger-text-on-soft: color-mix(in srgb, var(--wui-danger) 70%, white);
216
- --color-warning-text-on-soft: color-mix(in srgb, var(--wui-warning) 70%, white);
217
- --color-info-text-on-soft: color-mix(in srgb, var(--wui-info) 70%, white);
228
+ --color-success-text-on-soft: color-mix(in srgb, var(--wui-success) 65%, white);
229
+ --color-danger-text-on-soft: color-mix(in srgb, var(--wui-danger) 65%, white);
230
+ --color-warning-text-on-soft: color-mix(in srgb, var(--wui-warning) 65%, white);
231
+ --color-info-text-on-soft: color-mix(in srgb, var(--wui-info) 65%, white);
218
232
 
219
233
  // Light mode maps -strong to deep AA fills; on dark surfaces those sink into the page.
220
- // Remap to the bright fills so timeline dots / strong accents stay visible (same as -text).
221
- --color-success-strong: var(--wui-success);
222
- --color-danger-strong: var(--wui-danger);
223
- --color-info-strong: var(--wui-info);
224
- --color-warning-strong: var(--wui-warning);
234
+ // Dark mode uses the lifted -text tone, since -strong is a fill that carries near-black text
235
+ // (progress label, bell count): the bare danger and info fills reach only 4.4:1 and 4.0:1
236
+ // under that ink, the lifted ones 5.7:1 or better.
237
+ --color-success-strong: var(--color-success-text);
238
+ --color-danger-strong: var(--color-danger-text);
239
+ --color-info-strong: var(--color-info-text);
240
+ --color-warning-strong: var(--color-warning-text);
225
241
 
226
242
  --color-success-soft: color-mix(in srgb, var(--wui-success) 18%, transparent);
227
243
  --color-warning-soft: color-mix(in srgb, var(--wui-warning) 18%, transparent);
@@ -244,7 +244,7 @@
244
244
  // Not while open — an open control keeps its active (primary) border, not the grey hover one.
245
245
  // Not while invalid — an errored control (aria-invalid mirrored from the host) holds its red
246
246
  // border on hover rather than reverting to the grey hover one.
247
- border-color: var(--color-border-strong);
247
+ border-color: var(--color-field-border-hover);
248
248
  }
249
249
  .search-select-control.open {
250
250
  // White fill + the active/focus border, matching a focused text input (which lifts off the
@@ -414,7 +414,7 @@
414
414
  margin: 4px;
415
415
  padding: 0 12px;
416
416
  height: 40px;
417
- border: 1px solid var(--color-border);
417
+ border: 1px solid var(--color-field-border);
418
418
  border-radius: var(--radius);
419
419
  // AppInput's field model: muted resting fill that lifts to a clean surface on focus.
420
420
  background: var(--color-field-bg);
@@ -426,7 +426,7 @@
426
426
  // Hover and focus follow AppInput, as the phone country search does: a stronger border on hover,
427
427
  // then a clean surface fill + soft primary border on focus.
428
428
  .search-select-search:hover:not(:focus-within) {
429
- border-color: var(--color-border-strong);
429
+ border-color: var(--color-field-border-hover);
430
430
  }
431
431
  .search-select-search:focus-within {
432
432
  background: var(--color-surface);
@@ -581,7 +581,7 @@
581
581
  wui-datepicker:not([aria-invalid="true"]) .datepicker:not(.open) .datepicker-trigger:focus:not(:focus-visible),
582
582
  wui-date-range:not([aria-invalid="true"]) .daterange:not(.open) .daterange-trigger:focus:not(:focus-visible) {
583
583
  background-color: var(--color-field-bg);
584
- border-color: var(--color-border);
584
+ border-color: var(--color-field-border);
585
585
  }
586
586
  // Portalled to <body> and fixed-positioned from JS so a modal's
587
587
  // overflow:hidden can't clip it; top/left come from inline styles. z-index
@@ -1003,7 +1003,7 @@ button.datepicker-month:hover {
1003
1003
  align-items: center;
1004
1004
  width: 100%;
1005
1005
  height: var(--wui-control-height);
1006
- border: 1px solid var(--color-border);
1006
+ border: 1px solid var(--color-field-border);
1007
1007
  // Match AppInput's field-radius ladder exactly (18px md), not the generic --radius (10px).
1008
1008
  border-radius: var(--wui-field-radius);
1009
1009
  // Match AppInput exactly: muted resting fill that lifts to a clean surface on focus.
@@ -1024,7 +1024,7 @@ button.datepicker-month:hover {
1024
1024
  wui-phone:not([aria-invalid="true"])
1025
1025
  .phone-input-wrap:hover:not(:focus-within):not(:has(> .input.error)):not(:has(> .input.is-invalid)):not(:has(> .input[aria-invalid="true"])) {
1026
1026
  background: var(--color-field-bg);
1027
- border-color: var(--color-border-strong);
1027
+ border-color: var(--color-field-border-hover);
1028
1028
  }
1029
1029
  // Match AppInput's focus: clean surface fill + a soft primary border.
1030
1030
  .app-phone-number-input .phone-input-wrap:focus-within {
@@ -1238,7 +1238,7 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1238
1238
  width: 100%;
1239
1239
  height: 40px;
1240
1240
  padding: 0 12px 0 36px;
1241
- border: 1px solid var(--color-border);
1241
+ border: 1px solid var(--color-field-border);
1242
1242
  border-radius: var(--radius);
1243
1243
  font-size: 14px;
1244
1244
  color: var(--color-text);
@@ -1248,7 +1248,7 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1248
1248
  }
1249
1249
  .dropdown-search-input:hover:not(:focus) {
1250
1250
  background: var(--color-field-bg);
1251
- border-color: var(--color-border-strong);
1251
+ border-color: var(--color-field-border-hover);
1252
1252
  }
1253
1253
  // Border is the focus cue everywhere else in the system — no glow ring here.
1254
1254
  // AppInput's exact focus: clean surface fill + its soft primary border token.
@@ -301,7 +301,7 @@
301
301
  height: var(--wui-control-height);
302
302
  padding: 12px;
303
303
  border-radius: var(--wui-field-radius);
304
- border: 1px solid var(--color-border);
304
+ border: 1px solid var(--color-field-border);
305
305
  background: var(--color-field-bg);
306
306
  font-size: 14px;
307
307
  line-height: 1.5;
@@ -359,7 +359,7 @@ textarea.form-control.autosize {
359
359
  .select:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error),
360
360
  .textarea:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]):not(.is-invalid):not(.error) {
361
361
  background-color: var(--color-field-bg);
362
- border-color: var(--color-border-strong);
362
+ border-color: var(--color-field-border-hover);
363
363
  }
364
364
  .form-control:focus,
365
365
  .form-select:focus,
@@ -462,7 +462,10 @@ textarea.form-control.autosize {
462
462
  .input[readonly],
463
463
  .select[readonly],
464
464
  .textarea[readonly] {
465
- background: var(--color-bg-subtle);
465
+ // The light decorative border and the muted fill, never the field border, so a read-only
466
+ // value never looks typeable.
467
+ background: var(--color-bg-muted);
468
+ border-color: var(--color-border);
466
469
  color: var(--color-text-muted);
467
470
  cursor: default;
468
471
  }
@@ -810,7 +813,7 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
810
813
  display: flex;
811
814
  align-items: stretch;
812
815
  width: 100%;
813
- border: 1px solid var(--color-border);
816
+ border: 1px solid var(--color-field-border);
814
817
  border-radius: var(--wui-field-radius);
815
818
  // Soft-filled at rest, white on focus — one shared fill for the whole control so the
816
819
  // prefix/suffix and the input body are always the SAME colour combination as the field.
@@ -820,7 +823,7 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
820
823
  background-color 0.12s ease;
821
824
  }
822
825
  .input-affixed:hover:not(:focus-within) {
823
- border-color: var(--color-border-strong);
826
+ border-color: var(--color-field-border-hover);
824
827
  }
825
828
  .input-affixed:focus-within {
826
829
  background: var(--color-surface);
@@ -1098,7 +1101,7 @@ wui-select[compact] {
1098
1101
  .input-with-unit {
1099
1102
  display: flex;
1100
1103
  align-items: stretch;
1101
- border: 1px solid var(--color-border);
1104
+ border: 1px solid var(--color-field-border);
1102
1105
  border-radius: var(--wui-field-radius);
1103
1106
  // Soft-filled at rest, white on focus — the numeric input and its unit pill share the
1104
1107
  // one wrapper fill, same colour combination as every other field.
@@ -1109,7 +1112,7 @@ wui-select[compact] {
1109
1112
  overflow: hidden;
1110
1113
  }
1111
1114
  .input-with-unit:hover:not(:focus-within) {
1112
- border-color: var(--color-border-strong);
1115
+ border-color: var(--color-field-border-hover);
1113
1116
  }
1114
1117
  .input-with-unit:focus-within {
1115
1118
  background: var(--color-surface);
@@ -1168,7 +1171,7 @@ wui-select[compact] {
1168
1171
  .num-wrap {
1169
1172
  display: flex;
1170
1173
  align-items: stretch;
1171
- border: 1px solid var(--color-border);
1174
+ border: 1px solid var(--color-field-border);
1172
1175
  // Field radius ladder (matches AppInput): md 18px; sm/lg step below.
1173
1176
  border-radius: var(--wui-field-radius);
1174
1177
  // Match AppInput exactly: muted resting fill that lifts to a clean surface on focus.
@@ -1182,7 +1185,7 @@ wui-select[compact] {
1182
1185
  // reverting to the grey hover one (matches AppInput, whose hover rule excludes the error state).
1183
1186
  .num-wrap:hover:not(:focus-within):not(:has(.num-input.error)):not(:has(.num-input.is-invalid)):not(:has(.num-input[aria-invalid="true"])) {
1184
1187
  background: var(--color-field-bg);
1185
- border-color: var(--color-border-strong);
1188
+ border-color: var(--color-field-border-hover);
1186
1189
  }
1187
1190
  .num-wrap:focus-within {
1188
1191
  background: var(--color-surface);
@@ -1547,7 +1550,7 @@ wui-otp {
1547
1550
  height: var(--wui-control-height);
1548
1551
  // Colour model matches AppInput exactly: muted resting fill + soft border, same
1549
1552
  // field radius. State rules below mirror AppInput's hover/focus/invalid/disabled.
1550
- border: 1px solid var(--color-border);
1553
+ border: 1px solid var(--color-field-border);
1551
1554
  border-radius: var(--wui-field-radius);
1552
1555
  background: var(--color-field-bg);
1553
1556
  font-size: 18px;
@@ -1561,7 +1564,7 @@ wui-otp {
1561
1564
  // Hover — the whole field lifts its border to strong (AppInput hover), but not while a
1562
1565
  // slot is active (focus wins) or the field is disabled.
1563
1566
  .otp:hover:not(:focus-within):not(:has(.otp-field:disabled)) .otp-slot {
1564
- border-color: var(--color-border-strong);
1567
+ border-color: var(--color-field-border-hover);
1565
1568
  }
1566
1569
  // The caret cannot be seen through a transparent input, so the ACTIVE box carries the
1567
1570
  // affordance instead — derived from selectionStart. Matches AppInput's focus: clean
@@ -701,6 +701,45 @@
701
701
  border-radius: 999px;
702
702
  }
703
703
 
704
+ // A table wider than its scroller fades out at each edge it continues past, so a cut-off column
705
+ // reads as "more this way". scrollEdges.js (ui-elements) keeps the two data-wui-overflow-*
706
+ // attributes current on scroll and resize. The fade is a mask on the scroller, because a shadow
707
+ // paints under the opaque cells. A mask also hides what a descendant floats outside the box, so
708
+ // it stands aside while a menu or picker inside the table is open (an expanded row, which has no
709
+ // popup, keeps it). A pinned first column never
710
+ // fades at the start: it is the part that stays put.
711
+ :is(.table-wrap, .table-responsive, [class*="table-responsive-"]) {
712
+ --wui-overflow-fade-start: 0px;
713
+ --wui-overflow-fade-end: 0px;
714
+ }
715
+ :is(.table-wrap, .table-responsive, [class*="table-responsive-"])[data-wui-overflow-start] {
716
+ --wui-overflow-fade-start: 32px;
717
+ }
718
+ :is(.table-wrap, .table-responsive, [class*="table-responsive-"])[data-wui-overflow-end] {
719
+ --wui-overflow-fade-end: 32px;
720
+ }
721
+ .table-wrap[data-wui-overflow-start]:has(> .wui-table-sticky-first) {
722
+ --wui-overflow-fade-start: 0px;
723
+ }
724
+ :is(.table-wrap, .table-responsive, [class*="table-responsive-"]):is([data-wui-overflow-start], [data-wui-overflow-end]):not(:has([aria-haspopup][aria-expanded="true"], .dropdown-menu.show)) {
725
+ mask-image: linear-gradient(
726
+ 90deg,
727
+ transparent,
728
+ #000 var(--wui-overflow-fade-start),
729
+ #000 calc(100% - var(--wui-overflow-fade-end)),
730
+ transparent
731
+ );
732
+ }
733
+ :is(.table-wrap, .table-responsive, [class*="table-responsive-"]):dir(rtl):is([data-wui-overflow-start], [data-wui-overflow-end]):not(:has([aria-haspopup][aria-expanded="true"], .dropdown-menu.show)) {
734
+ mask-image: linear-gradient(
735
+ 270deg,
736
+ transparent,
737
+ #000 var(--wui-overflow-fade-start),
738
+ #000 calc(100% - var(--wui-overflow-fade-end)),
739
+ transparent
740
+ );
741
+ }
742
+
704
743
  // The identity/name column — a STABLE hook, because position is not one.
705
744
  //
706
745
  // The library owns .wui-select-cell, so a ledger's first cell is the 44px checkbox when
@@ -25,6 +25,10 @@
25
25
  // eyebrow, filled rail). Unset, completed steps follow the accent.
26
26
  // --wui-step-size compact/vertical badge diameter (default 28px)
27
27
  // --wui-step-badge-size icons-variant badge diameter (default 56px)
28
+ // --wui-step-journey-size journey badge diameter (default 40px)
29
+ // --wui-step-number-font the step number in a badge (default 12px; journey 13px, icons 22px)
30
+ // --wui-step-label-font the step label (default 13px; journey 16px, icons 15px)
31
+ // The `.wui-step-size-sm` / `.wui-step-size-lg` classes (end of this block) set these together.
28
32
  // Every variant defaults to the brand primary — active, done and rail share one accent
29
33
  // (no multi-colour). Colour variety is opt-in: a `.wui-step-tone-*` class or a var override.
30
34
  // ============================================================================
@@ -76,6 +80,15 @@
76
80
  .step.done + .step::before {
77
81
  background: var(--wui-step-done-accent, var(--wui-step-accent, var(--color-primary)));
78
82
  }
83
+ // The active circle's 4px ring would swallow the 4px gap, so both connectors touching the
84
+ // active step start 4px further out and keep the same gap from the ring's edge.
85
+ .step.active + .step::before {
86
+ left: calc(-50% + var(--wui-step-size, 28px) / 2 + 8px);
87
+ }
88
+ .step.active + .step::before,
89
+ .step.active::before {
90
+ right: calc(50% + var(--wui-step-size, 28px) / 2 + 8px);
91
+ }
79
92
  .step-circle {
80
93
  position: relative;
81
94
  z-index: 1;
@@ -87,7 +100,7 @@
87
100
  border: 1.5px solid var(--color-border);
88
101
  border-radius: 999px;
89
102
  background: var(--color-surface);
90
- font-size: 12px;
103
+ font-size: var(--wui-step-number-font, 12px);
91
104
  font-weight: 600;
92
105
  color: var(--color-text-subtle);
93
106
  }
@@ -106,7 +119,7 @@
106
119
  }
107
120
  .step-label {
108
121
  max-width: 130px;
109
- font-size: 13px;
122
+ font-size: var(--wui-step-label-font, 13px);
110
123
  font-weight: 500;
111
124
  line-height: 1.25;
112
125
  color: var(--color-text-subtle);
@@ -210,11 +223,11 @@ button.step.done:focus-visible .step-circle {
210
223
  content: none;
211
224
  }
212
225
  .stepper-journey .step-circle {
213
- width: 40px;
214
- height: 40px;
226
+ width: var(--wui-step-journey-size, 40px);
227
+ height: var(--wui-step-journey-size, 40px);
215
228
  border: none;
216
229
  background: var(--color-bg-muted);
217
- font-size: 13px;
230
+ font-size: var(--wui-step-number-font, 13px);
218
231
  font-weight: 700;
219
232
  transition:
220
233
  background 0.2s ease,
@@ -265,7 +278,7 @@ button.step.done:focus-visible .step-circle {
265
278
  .stepper-journey .step-label {
266
279
  max-width: none;
267
280
  margin-top: 8px;
268
- font-size: 16px;
281
+ font-size: var(--wui-step-label-font, 16px);
269
282
  line-height: 1.3;
270
283
  white-space: nowrap;
271
284
  overflow: hidden;
@@ -406,7 +419,7 @@ button.step.done:focus-visible .step-circle {
406
419
  border: none;
407
420
  background: var(--color-bg-muted);
408
421
  color: var(--color-text-subtle);
409
- font-size: 22px;
422
+ font-size: var(--wui-step-number-font, 22px);
410
423
  transition:
411
424
  background 0.2s ease,
412
425
  color 0.2s ease,
@@ -457,7 +470,7 @@ button.step.done:focus-visible .step-circle {
457
470
  }
458
471
  .stepper-icons .step-label {
459
472
  max-width: 150px;
460
- font-size: 15px;
473
+ font-size: var(--wui-step-label-font, 15px);
461
474
  font-weight: 600;
462
475
  color: var(--color-text-subtle);
463
476
  }
@@ -534,28 +547,29 @@ button.step.done:focus-visible .step-circle {
534
547
  --wui-step-soft: var(--color-primary-soft);
535
548
  --wui-step-soft-fg: var(--color-primary);
536
549
  }
537
- // Text on a bright status fill: a near-black ink in both modes, where white fails AA.
550
+ // Like primary, each tone fills with its deep shade (the -text token) under white ink in light mode,
551
+ // and with the bright fill under near-black ink in dark mode; both pairs clear AA.
538
552
  .wui-step-tone-success {
539
- --wui-step-accent: var(--color-success);
540
- --wui-step-accent-fg: var(--color-on-status-fill);
553
+ --wui-step-accent: var(--color-success-text);
554
+ --wui-step-accent-fg: var(--color-on-status-strong);
541
555
  --wui-step-soft: var(--color-success-soft);
542
556
  --wui-step-soft-fg: var(--color-success-text);
543
557
  }
544
558
  .wui-step-tone-warning {
545
- --wui-step-accent: var(--color-warning);
546
- --wui-step-accent-fg: var(--color-on-status-fill);
559
+ --wui-step-accent: var(--color-warning-text);
560
+ --wui-step-accent-fg: var(--color-on-status-strong);
547
561
  --wui-step-soft: var(--color-warning-soft);
548
562
  --wui-step-soft-fg: var(--color-warning-text);
549
563
  }
550
564
  .wui-step-tone-danger {
551
- --wui-step-accent: var(--color-danger);
552
- --wui-step-accent-fg: var(--color-on-status-fill);
565
+ --wui-step-accent: var(--color-danger-text);
566
+ --wui-step-accent-fg: var(--color-on-status-strong);
553
567
  --wui-step-soft: var(--color-danger-soft);
554
568
  --wui-step-soft-fg: var(--color-danger-text);
555
569
  }
556
570
  .wui-step-tone-info {
557
- --wui-step-accent: var(--color-info);
558
- --wui-step-accent-fg: var(--color-on-status-fill);
571
+ --wui-step-accent: var(--color-info-text);
572
+ --wui-step-accent-fg: var(--color-on-status-strong);
559
573
  --wui-step-soft: var(--color-info-soft);
560
574
  --wui-step-soft-fg: var(--color-info-text);
561
575
  }
@@ -572,30 +586,70 @@ button.step.done:focus-visible .step-circle {
572
586
  --wui-step-done-soft-fg: var(--color-primary);
573
587
  }
574
588
  .wui-step-done-tone-success {
575
- --wui-step-done-accent: var(--color-success);
576
- --wui-step-done-accent-fg: var(--color-on-status-fill);
589
+ --wui-step-done-accent: var(--color-success-text);
590
+ --wui-step-done-accent-fg: var(--color-on-status-strong);
577
591
  --wui-step-done-soft: var(--color-success-soft);
578
592
  --wui-step-done-soft-fg: var(--color-success-text);
579
593
  }
580
594
  .wui-step-done-tone-warning {
581
- --wui-step-done-accent: var(--color-warning);
582
- --wui-step-done-accent-fg: var(--color-on-status-fill);
595
+ --wui-step-done-accent: var(--color-warning-text);
596
+ --wui-step-done-accent-fg: var(--color-on-status-strong);
583
597
  --wui-step-done-soft: var(--color-warning-soft);
584
598
  --wui-step-done-soft-fg: var(--color-warning-text);
585
599
  }
586
600
  .wui-step-done-tone-danger {
587
- --wui-step-done-accent: var(--color-danger);
588
- --wui-step-done-accent-fg: var(--color-on-status-fill);
601
+ --wui-step-done-accent: var(--color-danger-text);
602
+ --wui-step-done-accent-fg: var(--color-on-status-strong);
589
603
  --wui-step-done-soft: var(--color-danger-soft);
590
604
  --wui-step-done-soft-fg: var(--color-danger-text);
591
605
  }
592
606
  .wui-step-done-tone-info {
593
- --wui-step-done-accent: var(--color-info);
594
- --wui-step-done-accent-fg: var(--color-on-status-fill);
607
+ --wui-step-done-accent: var(--color-info-text);
608
+ --wui-step-done-accent-fg: var(--color-on-status-strong);
595
609
  --wui-step-done-soft: var(--color-info-soft);
596
610
  --wui-step-done-soft-fg: var(--color-info-text);
597
611
  }
598
612
 
613
+ // Size presets: scale the badge, its number and the label together, on every variant. Same
614
+ // placement rules as the tone presets: on the stepper or any ancestor (the wizard host). Medium is
615
+ // the unset default above. The number and label fonts are per-variant here, so these rules name
616
+ // each variant; one class sets them all.
617
+ // ----------------------------------------------------------------------------
618
+ .wui-step-size-sm {
619
+ --wui-step-size: 22px;
620
+ --wui-step-badge-size: 44px;
621
+ --wui-step-journey-size: 32px;
622
+ --wui-step-number-font: 11px;
623
+ --wui-step-label-font: 12px;
624
+ }
625
+ .wui-step-size-sm .stepper-journey,
626
+ .wui-step-size-sm.stepper-journey {
627
+ --wui-step-number-font: 12px;
628
+ --wui-step-label-font: 14px;
629
+ }
630
+ .wui-step-size-sm .stepper-icons,
631
+ .wui-step-size-sm.stepper-icons {
632
+ --wui-step-number-font: 18px;
633
+ --wui-step-label-font: 13px;
634
+ }
635
+ .wui-step-size-lg {
636
+ --wui-step-size: 36px;
637
+ --wui-step-badge-size: 68px;
638
+ --wui-step-journey-size: 48px;
639
+ --wui-step-number-font: 14px;
640
+ --wui-step-label-font: 15px;
641
+ }
642
+ .wui-step-size-lg .stepper-journey,
643
+ .wui-step-size-lg.stepper-journey {
644
+ --wui-step-number-font: 15px;
645
+ --wui-step-label-font: 18px;
646
+ }
647
+ .wui-step-size-lg .stepper-icons,
648
+ .wui-step-size-lg.stepper-icons {
649
+ --wui-step-number-font: 26px;
650
+ --wui-step-label-font: 17px;
651
+ }
652
+
599
653
  // ============================================================================
600
654
  // Pricing card. A tier's name, price, pitch, feature list and CTA.
601
655
  //