@webority/theme 0.6.6 → 0.6.7

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.6.6",
3
+ "version": "0.6.7",
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": {
@@ -189,6 +189,44 @@
189
189
  color: var(--color-text-subtle);
190
190
  }
191
191
 
192
+ // Sizes — mirror AppInput's .input-sm / .input-lg so a select lines up with the
193
+ // text fields beside it. md is the base .search-select-control height.
194
+ .search-select-sm .search-select-control {
195
+ height: var(--wui-control-height-sm);
196
+ min-height: var(--wui-control-height-sm);
197
+ font-size: 13px;
198
+ }
199
+ .search-select-lg .search-select-control {
200
+ height: var(--wui-control-height-lg);
201
+ min-height: var(--wui-control-height-lg);
202
+ font-size: 16px;
203
+ }
204
+
205
+ // Clear (✕) — sits between the value and the caret, shown only once a value is
206
+ // chosen. It is a <span role="button"> inside the trigger <button> (a nested
207
+ // <button> is invalid); the JS stops its click from toggling the menu.
208
+ .search-select-control > span[data-label] {
209
+ flex: 1;
210
+ }
211
+ .search-select-reset {
212
+ flex-shrink: 0;
213
+ display: grid;
214
+ place-items: center;
215
+ width: 20px;
216
+ height: 20px;
217
+ border-radius: 50%;
218
+ font-size: 12px;
219
+ color: var(--color-text-subtle);
220
+ cursor: pointer;
221
+ transition:
222
+ background 0.12s ease,
223
+ color 0.12s ease;
224
+ }
225
+ .search-select-reset:hover {
226
+ background: var(--color-bg-muted);
227
+ color: var(--color-text);
228
+ }
229
+
192
230
  // AppSelectMini — the compact single-purpose picker (page size, a range switch). It shares
193
231
  // every behaviour with AppSelect and differs only in metrics.
194
232
  //
@@ -265,11 +303,18 @@
265
303
  .search-select-clear {
266
304
  display: grid;
267
305
  place-items: center;
268
- width: 24px;
269
- height: 24px;
306
+ width: 26px;
307
+ height: 26px;
270
308
  border: none;
271
309
  background: transparent;
272
310
  padding: 0;
311
+ // Sits at the box's 12px right padding; a small negative margin nudges it
312
+ // closer to the edge so it lines up with the field's own trailing controls.
313
+ margin-right: -4px;
314
+ font-size: 20px;
315
+ // Tight line box: at normal line-height the icon's box is taller than the
316
+ // button, so bootstrap-icons' baseline offset drifts the glyph low.
317
+ line-height: 1;
273
318
  cursor: pointer;
274
319
  color: var(--color-text-subtle);
275
320
  }
@@ -288,11 +333,10 @@
288
333
  .search-select-options {
289
334
  max-height: 300px;
290
335
  overflow-y: auto;
291
- padding: 4px 4px 4px;
336
+ padding: 4px;
292
337
  display: flex;
293
338
  flex-direction: column;
294
339
  gap: 4px;
295
- scrollbar-gutter: stable;
296
340
  }
297
341
  .search-select-option {
298
342
  display: flex;
@@ -634,6 +634,20 @@ textarea.textarea:focus {
634
634
  .field-float .input.error ~ .float-label {
635
635
  color: var(--color-danger-text);
636
636
  }
637
+ // Floating label for AppSelect — the trigger is a <button>, so there is no
638
+ // :placeholder-shown to key off. :has() raises the label when the control is
639
+ // focused/open, or once a value is chosen (the label span drops
640
+ // .search-select-placeholder the moment something is selected).
641
+ .field-float:has(.search-select-control:focus) .float-label,
642
+ .field-float:has(.search-select-control.open) .float-label,
643
+ .field-float:has([data-label]:not(.search-select-placeholder)) .float-label {
644
+ top: 0;
645
+ transform: translateY(-50%) scale(0.86);
646
+ }
647
+ .field-float:has(.search-select-control:focus) .float-label,
648
+ .field-float:has(.search-select-control.open) .float-label {
649
+ color: var(--color-primary);
650
+ }
637
651
 
638
652
  .hint,
639
653
  .field-hint {
@@ -998,3 +1012,85 @@ wui-select[compact] {
998
1012
  .fld-section:first-child {
999
1013
  margin-top: 0;
1000
1014
  }
1015
+
1016
+ // ── OTP / code entry (<wui-otp>) ──
1017
+ //
1018
+ // One real input, transparent, sitting on top of the segment boxes. The input keeps focus,
1019
+ // caret, selection, IME and — the reason for the whole design — OS autofill, which delivers
1020
+ // an SMS code to the focused field whole. The boxes are presentational and aria-hidden; the
1021
+ // input is the single labelled field a screen reader sees.
1022
+ //
1023
+ // The input must stay REAL: not opacity:0, not visibility:hidden, not off-screen. Any of
1024
+ // those stop iOS offering the code, which is the failure this component exists to avoid.
1025
+ // Transparent text with a transparent caret is the version that keeps the affordance while
1026
+ // letting the segments do the drawing.
1027
+ wui-otp {
1028
+ display: block;
1029
+ }
1030
+ .otp {
1031
+ position: relative;
1032
+ display: inline-flex;
1033
+ }
1034
+ .otp-field {
1035
+ position: absolute;
1036
+ inset: 0;
1037
+ width: 100%;
1038
+ height: 100%;
1039
+ padding: 0;
1040
+ border: 0;
1041
+ background: transparent;
1042
+ color: transparent;
1043
+ caret-color: transparent;
1044
+ font-size: 16px; // < 16px makes iOS zoom the page on focus
1045
+ text-align: center;
1046
+ letter-spacing: 0;
1047
+ z-index: 1;
1048
+ }
1049
+ .otp-field:focus {
1050
+ outline: none;
1051
+ }
1052
+ .otp-field:disabled {
1053
+ cursor: not-allowed;
1054
+ }
1055
+ .otp-slots {
1056
+ display: inline-flex;
1057
+ gap: 8px;
1058
+ }
1059
+ .otp-slot {
1060
+ display: inline-flex;
1061
+ align-items: center;
1062
+ justify-content: center;
1063
+ width: 44px;
1064
+ height: var(--wui-control-height);
1065
+ border: 1px solid var(--color-border);
1066
+ border-radius: var(--radius);
1067
+ background: var(--color-surface);
1068
+ font-size: 18px;
1069
+ font-weight: 600;
1070
+ font-variant-numeric: tabular-nums;
1071
+ color: var(--color-text);
1072
+ transition:
1073
+ border-color 0.12s,
1074
+ box-shadow 0.12s;
1075
+ }
1076
+ // The caret cannot be seen through a transparent input, so the ACTIVE box carries the
1077
+ // affordance instead — derived from selectionStart, since only one element is ever focused.
1078
+ .otp-slot.active {
1079
+ border-color: color-mix(in srgb, var(--color-primary) 45%, transparent);
1080
+ box-shadow: 0 0 0 3px var(--color-primary-soft);
1081
+ }
1082
+ .otp-field:disabled ~ .otp-slots .otp-slot {
1083
+ background: var(--color-bg-muted);
1084
+ border-color: var(--color-border);
1085
+ color: var(--color-text-subtle);
1086
+ }
1087
+ .field-error ~ .otp .otp-slot,
1088
+ .otp.error .otp-slot {
1089
+ border-color: var(--color-danger);
1090
+ }
1091
+ @media (max-width: 575.98px) {
1092
+ // Six 44px boxes plus gaps overflow a 360px screen; shrink the box, never the font.
1093
+ .otp-slot {
1094
+ width: 38px;
1095
+ }
1096
+ }