@webority/theme 0.50.1 → 0.51.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,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/theme",
3
- "version": "0.50.1",
3
+ "version": "0.51.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": {
@@ -20,7 +20,7 @@
20
20
  // Tactile press feedback — every variant AND every size (sm/md/lg). Kept
21
21
  // separate from the height rule above so sm/lg (which opt out of the shared
22
22
  // control height) still get the nudge. A signal, not a depth cue.
23
- .btn:not(.btn-pill):not(.btn-pill-sm):not(.u-action):not(.btn-icon):active:not(:disabled) {
23
+ .btn:not(.btn-pill):not(.btn-pill-sm):not(.u-action):not(.btn-icon):not(.btn-ghost):active:not(:disabled) {
24
24
  transform: translateY(1px);
25
25
  }
26
26
 
@@ -303,8 +303,8 @@
303
303
  }
304
304
 
305
305
  // --- Sheen (default + .btn-anim-sheen): slim angled glossy band sweeps across.
306
- .btn:not(.btn-icon):not(.u-action):not([class*="btn-anim-"])::before,
307
- .btn-anim-sheen::before {
306
+ .btn:not(.btn-icon):not(.u-action):not(.btn-ghost):not([class*="btn-anim-"])::before,
307
+ .btn-anim-sheen:not(.btn-ghost)::before {
308
308
  content: "";
309
309
  position: absolute;
310
310
  top: 0;
@@ -322,12 +322,12 @@
322
322
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
323
323
  pointer-events: none;
324
324
  }
325
- .btn:not(.btn-icon):not(.u-action):not([class*="btn-anim-"]):hover:not(:disabled)::before,
326
- .btn-anim-sheen:hover:not(:disabled)::before {
325
+ .btn:not(.btn-icon):not(.u-action):not(.btn-ghost):not([class*="btn-anim-"]):hover:not(:disabled)::before,
326
+ .btn-anim-sheen:not(.btn-ghost):hover:not(:disabled)::before {
327
327
  transform: translateX(320%) skewX(-18deg);
328
328
  }
329
- .btn:not(.btn-icon):not(.u-action):not([class*="btn-anim-"]):hover:not(:disabled),
330
- .btn-anim-sheen:hover:not(:disabled) {
329
+ .btn:not(.btn-icon):not(.u-action):not(.btn-ghost):not([class*="btn-anim-"]):hover:not(:disabled),
330
+ .btn-anim-sheen:not(.btn-ghost):hover:not(:disabled) {
331
331
  transform: translateY(-1px);
332
332
  }
333
333
 
@@ -489,27 +489,44 @@
489
489
  }
490
490
  }
491
491
 
492
- // ---- Ghost button (transparent, brand-tinted hover) ------------------
493
- // A real ghost button: transparent at rest (no fill, no border), current text colour;
494
- // only the background lights up — a soft brand tint, the same recipe as a
495
- // .dropdown-item's hover — on hover/active. AppButton's `ghost` variant emits this class
496
- // on both surfaces; distinct from Bootstrap's .btn-link, which reads as a hyperlink
497
- // (underlined, link-coloured text) rather than a quiet button. Marketing anchors reach
498
- // for it directly for the same look on a plain <a>.
492
+ // ---- Ghost button: the tertiary action --------------------------------
493
+ // Brand-coloured, underlined text with no box in any state. It has no visible container,
494
+ // so nothing may draw one: no hover fill, no sheen sweep, no lift or press nudge, and no
495
+ // grey box when disabled or loading. Hover darkens the colour and thickens the underline.
496
+ // AppButton's `ghost` variant emits this class on both surfaces.
499
497
  //
500
- // No .btn-ghost-sm/-lg rungs: unlike .btn-pill (a bespoke marketing sizing system), this
501
- // is an ordinary .btn variant, so it sizes the same way .btn-primary/.btn-danger do —
502
- // Bootstrap's own .btn-sm/.btn-lg.
498
+ // No .btn-ghost-sm/-lg rungs: it is an ordinary .btn variant and sizes with Bootstrap's
499
+ // own .btn-sm/.btn-lg, like .btn-primary.
503
500
  .btn-ghost {
504
- --bs-btn-color: var(--color-text);
501
+ --bs-btn-color: var(--color-primary-ink);
505
502
  --bs-btn-bg: transparent;
506
503
  --bs-btn-border-color: transparent;
507
- --bs-btn-hover-color: var(--color-text);
508
- --bs-btn-hover-bg: var(--color-primary-soft);
504
+ --bs-btn-hover-color: color-mix(in srgb, var(--color-primary-ink) 80%, var(--color-text));
505
+ --bs-btn-hover-bg: transparent;
509
506
  --bs-btn-hover-border-color: transparent;
510
- --bs-btn-active-color: var(--color-text);
511
- --bs-btn-active-bg: var(--color-primary-soft);
507
+ --bs-btn-active-color: color-mix(in srgb, var(--color-primary-ink) 70%, var(--color-text));
508
+ --bs-btn-active-bg: transparent;
512
509
  --bs-btn-active-border-color: transparent;
510
+ --bs-btn-disabled-color: var(--color-primary-ink);
511
+ --bs-btn-disabled-bg: transparent;
512
+ --bs-btn-disabled-border-color: transparent;
513
+ text-decoration: underline;
514
+ text-decoration-thickness: 1px;
515
+ text-underline-offset: 3px;
516
+ }
517
+ .btn-ghost:hover:not(:disabled) {
518
+ text-decoration-thickness: 2px;
519
+ }
520
+ .btn.btn-ghost.btn-ghost:disabled,
521
+ .btn.btn-ghost.btn-ghost.disabled {
522
+ background-color: transparent;
523
+ border-color: transparent;
524
+ color: var(--color-primary-ink);
525
+ opacity: 0.55;
526
+ }
527
+ .btn.btn-ghost.btn-ghost.is-loading:disabled,
528
+ .btn.btn-ghost.btn-ghost.is-loading.disabled {
529
+ opacity: 1;
513
530
  }
514
531
 
515
532
  // ---- Card hover-lift (name is historical) -----------------------------
@@ -1074,6 +1074,14 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1074
1074
  background: var(--color-surface);
1075
1075
  border-color: color-mix(in srgb, var(--color-danger) 30%, transparent);
1076
1076
  }
1077
+ .app-phone-number-input {
1078
+ // One size for the tel input, its placeholder and the dial code; the dial is not
1079
+ // inside the input, so it cannot inherit the input's font-size.
1080
+ --wui-phone-font-size: 14px;
1081
+ }
1082
+ .app-phone-number-input .phone-input-wrap > .input {
1083
+ font-size: var(--wui-phone-font-size);
1084
+ }
1077
1085
  // The inner control drops its own chrome — the shell carries it.
1078
1086
  .app-phone-number-input .phone-input-wrap > .input,
1079
1087
  .app-phone-number-input .phone-input-wrap > .input:hover,
@@ -1148,8 +1156,8 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1148
1156
  transform: rotate(180deg);
1149
1157
  }
1150
1158
  .app-phone-number-input .phone-dial {
1151
- font-size: 13px;
1152
- font-weight: 500;
1159
+ font-size: var(--wui-phone-font-size);
1160
+ font-weight: 400;
1153
1161
  color: var(--color-text);
1154
1162
  }
1155
1163
  // Country-adornment toggles (AppPhoneNumberInput showFlag / showDialCode). Driven
@@ -1176,11 +1184,8 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1176
1184
  height: var(--wui-control-height-sm);
1177
1185
  border-radius: var(--wui-field-radius-sm);
1178
1186
  }
1179
- .app-phone-number-input.phone-sm .input {
1180
- font-size: 13px;
1181
- }
1182
- .app-phone-number-input.phone-sm .phone-dial {
1183
- font-size: 12px;
1187
+ .app-phone-number-input.phone-sm {
1188
+ --wui-phone-font-size: 13px;
1184
1189
  }
1185
1190
  .app-phone-number-input.phone-sm .phone-country-adornment {
1186
1191
  padding-left: 10px;
@@ -1189,11 +1194,8 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1189
1194
  height: var(--wui-control-height-lg);
1190
1195
  border-radius: var(--wui-field-radius-lg);
1191
1196
  }
1192
- .app-phone-number-input.phone-lg .input {
1193
- font-size: 16px;
1194
- }
1195
- .app-phone-number-input.phone-lg .phone-dial {
1196
- font-size: 14px;
1197
+ .app-phone-number-input.phone-lg {
1198
+ --wui-phone-font-size: 16px;
1197
1199
  }
1198
1200
  .app-phone-number-input.phone-lg .phone-country-adornment {
1199
1201
  padding-left: 14px;
@@ -193,9 +193,16 @@
193
193
  .wui-radio:disabled {
194
194
  cursor: not-allowed;
195
195
  opacity: 0.55;
196
+ box-shadow: none;
197
+ }
198
+ // A disabled box that is ticked keeps its primary fill, only dimmed; the muted fill is for an
199
+ // empty one. :where keeps this at the :disabled specificity so the switch track still wins.
200
+ // A radio in a group with nothing chosen also matches :indeterminate, hence the type test.
201
+ .form-check-input:disabled:where(:not(:checked, [type="checkbox"]:indeterminate)),
202
+ .wui-check:disabled:where(:not(:checked, :indeterminate)),
203
+ .wui-radio:disabled:where(:not(:checked)) {
196
204
  background-color: var(--color-bg-muted);
197
205
  border-color: var(--color-border);
198
- box-shadow: none;
199
206
  }
200
207
  .form-check-input:disabled ~ .form-check-label,
201
208
  .wui-control:has(:disabled) {
@@ -523,6 +523,13 @@
523
523
  color: var(--wui-success-active);
524
524
  --pw-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Zm4.03-11.28a.9.9 0 0 0-1.32-1.22l-4.02 4.35-1.4-1.5a.9.9 0 1 0-1.32 1.22l2.06 2.22a.9.9 0 0 0 1.32 0l4.68-5.07Z' clip-rule='evenodd'/%3E%3C/svg%3E");
525
525
  }
526
+ // requirementsPlacement="inline": the same checklist, but in the field under the input and always
527
+ // visible, in place of the hint. `.field` is align-items:flex-start, so it is stretched to the
528
+ // control's width; the rows keep the popover's type and the same met/unmet marks.
529
+ .wui-pw-inline {
530
+ align-self: stretch;
531
+ margin-top: 8px;
532
+ }
526
533
  // The checklist and the field help tip open with a PLAIN FADE only — no drop / zoom / scale. It opts out of the shared
527
534
  // menu-open motion (_menu-motion.scss animates every `.popover.show`) and lets Bootstrap's own
528
535
  // `.fade` opacity transition carry the fade in and out. Both surfaces render `.popover.pw-popover`