@webority/theme 0.18.1 → 0.20.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/README.md +1 -1
- package/dist/purge-safelist.json +3 -0
- package/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_wui-components.scss +1 -0
- package/scss/wui/_controls.scss +17 -4
- package/scss/wui/_data.scss +16 -0
- package/scss/wui/_display.scss +1 -0
- package/scss/wui/_navigation.scss +8 -0
- package/scss/wui/_theme-toggle.scss +61 -0
- package/scss/wui/_typography.scss +2 -3
- package/scss/wui/_utilities.scss +4 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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": {
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
@import "wui/dock";
|
|
52
52
|
@import "wui/consent-banner";
|
|
53
53
|
@import "wui/toolbar";
|
|
54
|
+
@import "wui/theme-toggle";
|
|
54
55
|
// After `overlays`: anchors the shared modal shell to the top when it holds the palette card.
|
|
55
56
|
@import "wui/command-palette";
|
|
56
57
|
// Last: restates the settled state of menus other partials style, so it must win.
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -993,11 +993,15 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
|
|
|
993
993
|
// flush against the label.
|
|
994
994
|
margin-inline-start: 0;
|
|
995
995
|
}
|
|
996
|
+
// min-width: 0 lets a field shrink to its column in any flex or grid parent; left at auto, a
|
|
997
|
+
// control sized by its content (chips, a date range, OTP boxes) pushes the field, and the page,
|
|
998
|
+
// wider than a phone screen.
|
|
996
999
|
.field {
|
|
997
1000
|
display: flex;
|
|
998
1001
|
flex-direction: column;
|
|
999
1002
|
align-items: flex-start;
|
|
1000
1003
|
gap: 4px;
|
|
1004
|
+
min-width: 0;
|
|
1001
1005
|
}
|
|
1002
1006
|
.field > .label,
|
|
1003
1007
|
.field label {
|
|
@@ -1012,12 +1016,8 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
|
|
|
1012
1016
|
.field-row-3 {
|
|
1013
1017
|
grid-template-columns: 1fr 1fr 1fr;
|
|
1014
1018
|
}
|
|
1015
|
-
// min-width:0 — grid/flex items default to min-width:auto, which lets a long
|
|
1016
|
-
// control (e.g. a search-select label) push its track past 1fr and defeat
|
|
1017
|
-
// text truncation. Reset to 0 so the ellipsis engages instead of overflowing.
|
|
1018
1019
|
.field-row .field {
|
|
1019
1020
|
margin-bottom: 0;
|
|
1020
|
-
min-width: 0;
|
|
1021
1021
|
}
|
|
1022
1022
|
// Controls fill their field even though .field aligns children flex-start.
|
|
1023
1023
|
//
|
|
@@ -1050,6 +1050,7 @@ wui-date-range {
|
|
|
1050
1050
|
wui-select[compact] {
|
|
1051
1051
|
display: inline-block;
|
|
1052
1052
|
width: auto;
|
|
1053
|
+
max-width: 100%;
|
|
1053
1054
|
}
|
|
1054
1055
|
.field > wui-select,
|
|
1055
1056
|
.field > wui-country-select,
|
|
@@ -1496,10 +1497,12 @@ wui-select[compact] {
|
|
|
1496
1497
|
// letting the segments do the drawing.
|
|
1497
1498
|
wui-otp {
|
|
1498
1499
|
display: block;
|
|
1500
|
+
max-width: 100%;
|
|
1499
1501
|
}
|
|
1500
1502
|
.otp {
|
|
1501
1503
|
position: relative;
|
|
1502
1504
|
display: inline-flex;
|
|
1505
|
+
max-width: 100%;
|
|
1503
1506
|
}
|
|
1504
1507
|
.otp-field {
|
|
1505
1508
|
position: absolute;
|
|
@@ -1522,15 +1525,25 @@ wui-otp {
|
|
|
1522
1525
|
.otp-field:disabled {
|
|
1523
1526
|
cursor: not-allowed;
|
|
1524
1527
|
}
|
|
1528
|
+
// The boxes narrow together (to 22px, still room for an 18px digit) rather than push past a
|
|
1529
|
+
// phone screen; 8 boxes at 44px need 408px.
|
|
1525
1530
|
.otp-slots {
|
|
1526
1531
|
display: inline-flex;
|
|
1527
1532
|
gap: 8px;
|
|
1533
|
+
min-width: 0;
|
|
1534
|
+
}
|
|
1535
|
+
@include media-breakpoint-down(sm) {
|
|
1536
|
+
.otp-slots {
|
|
1537
|
+
gap: 4px;
|
|
1538
|
+
}
|
|
1528
1539
|
}
|
|
1529
1540
|
.otp-slot {
|
|
1530
1541
|
display: inline-flex;
|
|
1531
1542
|
align-items: center;
|
|
1532
1543
|
justify-content: center;
|
|
1533
1544
|
width: 44px;
|
|
1545
|
+
min-width: 22px;
|
|
1546
|
+
flex: 0 1 auto;
|
|
1534
1547
|
height: var(--wui-control-height);
|
|
1535
1548
|
// Colour model matches AppInput exactly: muted resting fill + soft border, same
|
|
1536
1549
|
// field radius. State rules below mirror AppInput's hover/focus/invalid/disabled.
|
package/scss/wui/_data.scss
CHANGED
|
@@ -542,6 +542,8 @@
|
|
|
542
542
|
}
|
|
543
543
|
.tpg-nav {
|
|
544
544
|
display: inline-flex;
|
|
545
|
+
flex-wrap: wrap;
|
|
546
|
+
justify-content: flex-end;
|
|
545
547
|
align-items: center;
|
|
546
548
|
gap: 4px;
|
|
547
549
|
}
|
|
@@ -624,6 +626,13 @@
|
|
|
624
626
|
display: flex;
|
|
625
627
|
align-items: center;
|
|
626
628
|
justify-content: center;
|
|
629
|
+
max-width: 100%;
|
|
630
|
+
}
|
|
631
|
+
// Many pages wrap onto a second row rather than push past a phone screen.
|
|
632
|
+
.pagination-nav .pagination {
|
|
633
|
+
flex-wrap: wrap;
|
|
634
|
+
justify-content: center;
|
|
635
|
+
row-gap: 4px;
|
|
627
636
|
}
|
|
628
637
|
.pagination-md {
|
|
629
638
|
// Default Bootstrap pagination size (no-op, but explicit for parallelism with sm)
|
|
@@ -654,6 +663,13 @@
|
|
|
654
663
|
scrollbar-width: thin;
|
|
655
664
|
scrollbar-color: var(--color-border-strong) transparent;
|
|
656
665
|
}
|
|
666
|
+
// Visually hidden text (a header's screen-reader label) is absolutely positioned at its static
|
|
667
|
+
// place. Its containing block sits outside the scroll box, so on a wide table it escaped the
|
|
668
|
+
// clipping and widened the page. Pin it to the corner of that containing block instead.
|
|
669
|
+
.table-wrap .visually-hidden:not(.visually-hidden-focusable) {
|
|
670
|
+
top: 0;
|
|
671
|
+
left: 0;
|
|
672
|
+
}
|
|
657
673
|
.table-wrap::-webkit-scrollbar {
|
|
658
674
|
height: 6px;
|
|
659
675
|
}
|
package/scss/wui/_display.scss
CHANGED
|
@@ -146,6 +146,9 @@
|
|
|
146
146
|
display: flex;
|
|
147
147
|
align-items: center;
|
|
148
148
|
gap: 4px;
|
|
149
|
+
// Fits its parent so the tab strip scrolls inside it rather than widening the page.
|
|
150
|
+
min-width: 0;
|
|
151
|
+
max-width: 100%;
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
.app-tabs {
|
|
@@ -394,6 +397,11 @@
|
|
|
394
397
|
|
|
395
398
|
// Wrapper for a self-contained tabs widget (strip + panels), as <app-tabs>
|
|
396
399
|
// renders server-side. React composes the strip and its panels itself.
|
|
400
|
+
// The Razor root: fits its parent so the tab strip inside scrolls instead of widening the page.
|
|
401
|
+
.app-tabs-group {
|
|
402
|
+
min-width: 0;
|
|
403
|
+
max-width: 100%;
|
|
404
|
+
}
|
|
397
405
|
.app-tabs-group > .tab-content {
|
|
398
406
|
padding-top: 16px;
|
|
399
407
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// AppThemeToggle / <app-theme-toggle>: the light/dark switch and its page-wide transition.
|
|
2
|
+
//
|
|
3
|
+
// The button is the .u-action disc (see _data.scss). While a switch runs, the page is a View
|
|
4
|
+
// Transition: the old theme is a frozen snapshot and the new theme is clipped to the effect's
|
|
5
|
+
// moving shape (transitionTheme in @webority/ui-elements writes the keyframes). The rules below
|
|
6
|
+
// are scoped to the class the engine sets on <html> for the length of one switch, so an
|
|
7
|
+
// ordinary page transition elsewhere keeps the browser's defaults.
|
|
8
|
+
html.wui-theme-switching::view-transition-old(root),
|
|
9
|
+
html.wui-theme-switching::view-transition-new(root) {
|
|
10
|
+
// No default cross-fade: the old snapshot stays whole above the effect's edge and the new
|
|
11
|
+
// page whole below it, which is what makes the edge read as a wave or a wipe.
|
|
12
|
+
animation: none;
|
|
13
|
+
mix-blend-mode: normal;
|
|
14
|
+
}
|
|
15
|
+
html.wui-theme-switching::view-transition-new(root) {
|
|
16
|
+
// Hidden until the effect's first frame is attached (a zero-area clip), so a dark page never
|
|
17
|
+
// flashes in full for the frame or two between the snapshot and the animation start.
|
|
18
|
+
clip-path: inset(50%);
|
|
19
|
+
}
|
|
20
|
+
// The pressed control carries its own view-transition-name for the switch (the engine sets it),
|
|
21
|
+
// so it is rendered as its own live layer above the sweep: no old image, no cross-fade, just
|
|
22
|
+
// the control as it now is, with its ring sliding or its glyph swapped the moment it is pressed.
|
|
23
|
+
html.wui-theme-switching::view-transition-group(wui-theme-toggle),
|
|
24
|
+
html.wui-theme-switching::view-transition-new(wui-theme-toggle) {
|
|
25
|
+
animation: none;
|
|
26
|
+
}
|
|
27
|
+
html.wui-theme-switching::view-transition-old(wui-theme-toggle) {
|
|
28
|
+
animation: none;
|
|
29
|
+
opacity: 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The new page is rendered live under the moving clip, so anything still changing on it repaints
|
|
33
|
+
// the whole page every frame. A theme flip starts a colour transition on hundreds of elements at
|
|
34
|
+
// once (Bootstrap's buttons, inputs and links all transition color and background), which is
|
|
35
|
+
// enough to stutter the sweep. For the length of the switch those transitions are off: the page
|
|
36
|
+
// jumps to its new colours under the old snapshot, and only the clip moves.
|
|
37
|
+
// The segmented toggle's own thumb and glyph are the one exception: a press on it is what
|
|
38
|
+
// started the switch, and its ring sliding to the pressed option is part of that press.
|
|
39
|
+
html.wui-theme-switching,
|
|
40
|
+
html.wui-theme-switching *:not(.segmented-thumb, .segmented-option > .wui-icon),
|
|
41
|
+
html.wui-theme-switching *::before,
|
|
42
|
+
html.wui-theme-switching *::after {
|
|
43
|
+
transition: none !important;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The disc's glyph turns a touch on hover, the one cue that it changes something page-wide. The
|
|
47
|
+
// segmented variant (one option per mode) keeps AppSegmented's own look.
|
|
48
|
+
.u-action.wui-theme-toggle .wui-icon {
|
|
49
|
+
transition: transform 0.3s var(--wui-ease-out-quart, ease-out);
|
|
50
|
+
}
|
|
51
|
+
.u-action.wui-theme-toggle:hover .wui-icon {
|
|
52
|
+
transform: rotate(24deg);
|
|
53
|
+
}
|
|
54
|
+
@media (prefers-reduced-motion: reduce) {
|
|
55
|
+
.u-action.wui-theme-toggle .wui-icon {
|
|
56
|
+
transition: none;
|
|
57
|
+
}
|
|
58
|
+
.u-action.wui-theme-toggle:hover .wui-icon {
|
|
59
|
+
transform: none;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -73,9 +73,8 @@
|
|
|
73
73
|
line-height: 0;
|
|
74
74
|
color: var(--color-text-subtle);
|
|
75
75
|
}
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
// Reset it so a library-icon leading glyph lines up with the label.
|
|
76
|
+
// An icon's baseline nudge is meant for icons set in running text; inside the chip's flex row
|
|
77
|
+
// it drops the glyph below the label's centre. Reset it so a leading icon lines up with the label.
|
|
79
78
|
.chip-ic .wui-icon {
|
|
80
79
|
vertical-align: middle;
|
|
81
80
|
}
|
package/scss/wui/_utilities.scss
CHANGED
|
@@ -273,6 +273,8 @@
|
|
|
273
273
|
// one line while it fits — then `.page-header-bar`'s flex-wrap drops the WHOLE actions
|
|
274
274
|
// block onto its own line only once the content genuinely can't share the row.
|
|
275
275
|
flex: none;
|
|
276
|
+
// Never wider than the bar, so on a phone the buttons wrap among themselves.
|
|
277
|
+
max-width: 100%;
|
|
276
278
|
}
|
|
277
279
|
|
|
278
280
|
// ── Size scale ── (`size`): the title and the block's rhythm scale together.
|
|
@@ -885,10 +887,10 @@
|
|
|
885
887
|
height: 24px;
|
|
886
888
|
}
|
|
887
889
|
|
|
888
|
-
// The leading glyph is a solid circular badge
|
|
890
|
+
// The leading glyph is a solid circular badge: variant fill, white glyph centred, with
|
|
889
891
|
// a soft 3px same-colour halo ring around it. Base size is the SINGLE-line badge (20px,
|
|
890
892
|
// 20px glyph); the .alert-stack override above enlarges it when a title and body appear
|
|
891
|
-
// together. (The glyph itself is a
|
|
893
|
+
// together. (The glyph itself is a library icon, an inline SVG sized by the component.)
|
|
892
894
|
// Custom image in the badge (icon replacement): the image fills the circle, so drop the
|
|
893
895
|
// variant fill and the halo ring and clip it round. Compound selector so it beats the
|
|
894
896
|
// .alert-icon rule below, which sets both.
|