@webority/theme 0.6.6 → 0.6.8
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/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_wui-components.scss +15 -0
- package/scss/wui/_choice.scss +58 -9
- package/scss/wui/_controls.scss +96 -0
- package/scss/wui/_data.scss +10 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
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": {
|
|
@@ -34,4 +34,19 @@
|
|
|
34
34
|
@import "wui/disclosure";
|
|
35
35
|
@import "wui/display";
|
|
36
36
|
@import "wui/flow";
|
|
37
|
+
|
|
38
|
+
// Flags belong here because <wui-phone> cannot render without them.
|
|
39
|
+
//
|
|
40
|
+
// The 245 `.wui-flag-*` rules live in _flags.scss, and this entry did not pull it in — so a
|
|
41
|
+
// product following the documented chain (variables -> bootstrap -> tokens -> components ->
|
|
42
|
+
// wui-components) got AppPhoneNumberInput with an empty grey box where every flag should be,
|
|
43
|
+
// and nothing failing anywhere to point at the cause. The component shipped; its styling did
|
|
44
|
+
// not. That is the same escape as th.sortable and .u-name, and the fix is the same principle:
|
|
45
|
+
// a component and the CSS it requires travel together.
|
|
46
|
+
//
|
|
47
|
+
// It is not free — roughly 183KB raw, 45KB gzipped, mostly inlined SVG. A product that never
|
|
48
|
+
// renders a phone field can skip it by importing the `wui/*` partials above individually
|
|
49
|
+
// instead of this aggregate; that is the deliberate opt-out, and it is a choice rather than
|
|
50
|
+
// an accident.
|
|
51
|
+
@import "flags";
|
|
37
52
|
@import "wui/shell";
|
package/scss/wui/_choice.scss
CHANGED
|
@@ -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:
|
|
269
|
-
height:
|
|
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
|
|
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;
|
|
@@ -431,12 +475,17 @@
|
|
|
431
475
|
gap: 4px;
|
|
432
476
|
margin-bottom: 6px;
|
|
433
477
|
}
|
|
478
|
+
// The seven-column calendar. This is the grid the day cells actually live in.
|
|
479
|
+
//
|
|
480
|
+
// It was a COLUMN FLEX wrapping a `.datepicker-row` that nothing ever emitted: the element
|
|
481
|
+
// writes its day buttons flat into this container. So each day became a full-width flex item
|
|
482
|
+
// — `width: 100%` of 274px, then `aspect-ratio: 1` squared it — giving 274x274 cells and a
|
|
483
|
+
// 9,622px popup. Every date picker in the library, not just the one inside a modal where a
|
|
484
|
+
// tester happened to notice it.
|
|
485
|
+
//
|
|
486
|
+
// It survived because the interaction tests only ever asserted that the popup OPENS. Opening
|
|
487
|
+
// a calendar you cannot click a day in is not opening it.
|
|
434
488
|
.datepicker-grid {
|
|
435
|
-
display: flex;
|
|
436
|
-
flex-direction: column;
|
|
437
|
-
gap: 4px;
|
|
438
|
-
}
|
|
439
|
-
.datepicker-row {
|
|
440
489
|
display: grid;
|
|
441
490
|
grid-template-columns: repeat(7, 1fr);
|
|
442
491
|
gap: 4px;
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -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
|
+
}
|
package/scss/wui/_data.scss
CHANGED
|
@@ -285,10 +285,16 @@
|
|
|
285
285
|
// 360px column to 854px and pushed the table into horizontal scroll. It trades row height for
|
|
286
286
|
// column geometry. A line clamp leaves the text wrappable, so min-content stays one word.
|
|
287
287
|
//
|
|
288
|
-
//
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
//
|
|
288
|
+
// DEFAULT inside a ledger, opt-in class elsewhere.
|
|
289
|
+
//
|
|
290
|
+
// It shipped opt-in because `-webkit-box` stacks element children, and the library styles
|
|
291
|
+
// `.u-name` without ever emitting it — so a product composing a name with an inline badge
|
|
292
|
+
// would have got the badge under the name. A consumer then audited all 13 of their usages:
|
|
293
|
+
// every one has a bare text expression as its only child, and the badges and sublines are
|
|
294
|
+
// SIBLINGS inside `.u-meta`, outside `.u-name` entirely. The caution was right to have and
|
|
295
|
+
// does not apply, so the ledger case — where a wrapped name breaks row rhythm down the whole
|
|
296
|
+
// table — gets it without anyone opting in.
|
|
297
|
+
.table-ledger .u-name,
|
|
292
298
|
.u-name-clamp {
|
|
293
299
|
display: -webkit-box;
|
|
294
300
|
-webkit-line-clamp: 1;
|