@webority/theme 0.8.0 → 0.8.2
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/_components.scss +6 -6
- package/scss/wui/_choice.scss +19 -4
- package/scss/wui/_controls.scss +40 -8
- package/scss/wui/_display.scss +193 -19
- package/scss/wui/_flow.scss +14 -6
- package/scss/wui/_media.scss +24 -13
- package/scss/wui/_overlays.scss +8 -1
- package/scss/wui/_typography.scss +9 -0
- package/scss/wui/_workspace.scss +61 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
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": {
|
package/scss/_components.scss
CHANGED
|
@@ -170,9 +170,9 @@
|
|
|
170
170
|
// sizes step font-size, horizontal padding and radius together (vertical size
|
|
171
171
|
// comes from --wui-control-height, so a button lines up with an input). Weight
|
|
172
172
|
// is 600 on every rung — buttons read as a deliberate action, not body text.
|
|
173
|
-
// Medium (default): 15px / 600, 24px side padding,
|
|
173
|
+
// Medium (default): 15px / 600, 24px side padding, 22px squircle radius, 45px tall.
|
|
174
174
|
.btn {
|
|
175
|
-
--bs-btn-border-radius:
|
|
175
|
+
--bs-btn-border-radius: 22px;
|
|
176
176
|
--bs-btn-font-size: 0.9375rem;
|
|
177
177
|
--bs-btn-font-weight: 600;
|
|
178
178
|
--bs-btn-padding-x: 1.5rem;
|
|
@@ -180,10 +180,10 @@
|
|
|
180
180
|
// keep a plain border-radius arc — not a cross-browser design contract.
|
|
181
181
|
corner-shape: squircle;
|
|
182
182
|
}
|
|
183
|
-
// Small: 14px, 18px side padding,
|
|
183
|
+
// Small: 14px, 18px side padding, 17px squircle radius (proportional to md), 35px tall.
|
|
184
184
|
.btn-sm:not(.btn-icon),
|
|
185
185
|
.btn-group-sm > .btn:not(.btn-icon) {
|
|
186
|
-
--bs-btn-border-radius:
|
|
186
|
+
--bs-btn-border-radius: 17px;
|
|
187
187
|
--bs-btn-font-size: 0.875rem;
|
|
188
188
|
--bs-btn-padding-x: 1.125rem;
|
|
189
189
|
display: inline-flex;
|
|
@@ -191,10 +191,10 @@
|
|
|
191
191
|
justify-content: center;
|
|
192
192
|
min-height: 35px;
|
|
193
193
|
}
|
|
194
|
-
// Large: 17px, 32px side padding,
|
|
194
|
+
// Large: 17px, 32px side padding, 27px squircle radius (proportional to md), 55px tall — a spacious CTA.
|
|
195
195
|
.btn-lg:not(.btn-icon),
|
|
196
196
|
.btn-group-lg > .btn:not(.btn-icon) {
|
|
197
|
-
--bs-btn-border-radius:
|
|
197
|
+
--bs-btn-border-radius: 27px;
|
|
198
198
|
--bs-btn-font-size: 1.0625rem;
|
|
199
199
|
--bs-btn-padding-x: 2rem;
|
|
200
200
|
display: inline-flex;
|
package/scss/wui/_choice.scss
CHANGED
|
@@ -894,7 +894,8 @@ button.datepicker-month:hover {
|
|
|
894
894
|
border: 1px solid var(--color-border);
|
|
895
895
|
border-radius: var(--radius);
|
|
896
896
|
corner-shape: squircle;
|
|
897
|
-
|
|
897
|
+
// Match AppInput exactly: muted resting fill that lifts to a clean surface on focus.
|
|
898
|
+
background: var(--color-bg-muted);
|
|
898
899
|
// Clip the inner control to the shell's rounded shape — a full-height child was painting
|
|
899
900
|
// over the squircle border at the bottom, which read as a cut-off bottom edge. The dial
|
|
900
901
|
// dropdown is position:fixed, so it is unaffected by this.
|
|
@@ -907,10 +908,10 @@ button.datepicker-month:hover {
|
|
|
907
908
|
background: var(--color-bg-muted);
|
|
908
909
|
border-color: var(--color-border-strong);
|
|
909
910
|
}
|
|
910
|
-
// Match AppInput's focus: a soft primary border
|
|
911
|
+
// Match AppInput's focus: clean surface fill + a soft primary border.
|
|
911
912
|
.app-phone-number-input .phone-input-wrap:focus-within {
|
|
912
|
-
background: var(--color-
|
|
913
|
-
border-color:
|
|
913
|
+
background: var(--color-surface);
|
|
914
|
+
border-color: var(--wui-primary-30);
|
|
914
915
|
}
|
|
915
916
|
.app-phone-number-input .phone-input-wrap:has(> .input:disabled) {
|
|
916
917
|
background: var(--color-bg-muted);
|
|
@@ -998,6 +999,20 @@ button.datepicker-month:hover {
|
|
|
998
999
|
font-weight: 500;
|
|
999
1000
|
color: var(--color-text);
|
|
1000
1001
|
}
|
|
1002
|
+
// Country-adornment toggles (AppPhoneNumberInput showFlag / showDialCode). Driven
|
|
1003
|
+
// by a host attribute so it survives <wui-phone>'s innerHTML re-renders; the flex
|
|
1004
|
+
// gap collapses on its own, leaving no gap where a hidden glyph sat.
|
|
1005
|
+
.app-phone-number-input wui-phone[hide-flag] [data-flag] {
|
|
1006
|
+
display: none;
|
|
1007
|
+
}
|
|
1008
|
+
.app-phone-number-input wui-phone[hide-dial] [data-dial] {
|
|
1009
|
+
display: none;
|
|
1010
|
+
}
|
|
1011
|
+
// Both hidden → the country toggle is just the chevron; drop its leading pad so
|
|
1012
|
+
// the number sits flush like a plain input's.
|
|
1013
|
+
.app-phone-number-input wui-phone[hide-flag][hide-dial] .phone-country-adornment {
|
|
1014
|
+
padding-left: 10px;
|
|
1015
|
+
}
|
|
1001
1016
|
|
|
1002
1017
|
// Menu, search field and rows all share the .search-select language so the
|
|
1003
1018
|
// country picker reads as the same component family as every other picker.
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -999,7 +999,8 @@ wui-select[compact] {
|
|
|
999
999
|
border: 1px solid var(--color-border);
|
|
1000
1000
|
border-radius: var(--radius-lg);
|
|
1001
1001
|
corner-shape: squircle;
|
|
1002
|
-
|
|
1002
|
+
// Match AppInput exactly: muted resting fill that lifts to a clean surface on focus.
|
|
1003
|
+
background: var(--color-bg-muted);
|
|
1003
1004
|
transition:
|
|
1004
1005
|
border-color 0.12s,
|
|
1005
1006
|
background-color 0.12s;
|
|
@@ -1010,8 +1011,8 @@ wui-select[compact] {
|
|
|
1010
1011
|
border-color: var(--color-border-strong);
|
|
1011
1012
|
}
|
|
1012
1013
|
.num-wrap:focus-within {
|
|
1013
|
-
background: var(--color-
|
|
1014
|
-
border-color:
|
|
1014
|
+
background: var(--color-surface);
|
|
1015
|
+
border-color: var(--wui-primary-30);
|
|
1015
1016
|
}
|
|
1016
1017
|
.num-wrap:has(.num-input.error) {
|
|
1017
1018
|
border-color: var(--color-danger);
|
|
@@ -1019,25 +1020,34 @@ wui-select[compact] {
|
|
|
1019
1020
|
|
|
1020
1021
|
// Numeric input inside the stepper — plain text field, no native spinner.
|
|
1021
1022
|
// Flows seamlessly between the stepper buttons; the wrapper carries the border.
|
|
1023
|
+
// The value reads as the segmented centre cell — centred + semibold like the
|
|
1024
|
+
// reference: minus | value | plus, three even cells divided by the button borders.
|
|
1022
1025
|
.num-wrap .num-input {
|
|
1023
1026
|
flex: 1;
|
|
1024
1027
|
border: none;
|
|
1025
1028
|
border-radius: 0;
|
|
1026
1029
|
background: transparent;
|
|
1027
1030
|
min-width: 0;
|
|
1031
|
+
text-align: center;
|
|
1032
|
+
font-weight: 600;
|
|
1033
|
+
color: var(--color-text);
|
|
1028
1034
|
}
|
|
1029
1035
|
.num-wrap .num-input:focus {
|
|
1030
1036
|
box-shadow: none;
|
|
1031
1037
|
}
|
|
1032
1038
|
|
|
1033
1039
|
// Stepper buttons — decrease/increase the value by step.
|
|
1034
|
-
// Positioned left and right
|
|
1035
|
-
// control is disabled.
|
|
1040
|
+
// Positioned left (minus, :first-child) and right (plus, :last-child) of the
|
|
1041
|
+
// input; disabled at min/max or when the control is disabled. Each carries the
|
|
1042
|
+
// divider on its input-facing edge so the value sits in its own segment.
|
|
1036
1043
|
.num-btn {
|
|
1037
1044
|
display: grid;
|
|
1038
1045
|
place-items: center;
|
|
1039
|
-
width:
|
|
1040
|
-
height: 100
|
|
1046
|
+
width: 44px;
|
|
1047
|
+
// Fill the field height and centre the glyph. `height: 100%` resolved to auto
|
|
1048
|
+
// against the wrap's content-based height and left the button icon-tall, pinned
|
|
1049
|
+
// to the top; align-self:stretch fills the full field so place-items centres it.
|
|
1050
|
+
align-self: stretch;
|
|
1041
1051
|
border: none;
|
|
1042
1052
|
background: transparent;
|
|
1043
1053
|
padding: 0;
|
|
@@ -1064,10 +1074,32 @@ wui-select[compact] {
|
|
|
1064
1074
|
cursor: not-allowed;
|
|
1065
1075
|
opacity: 0.5;
|
|
1066
1076
|
}
|
|
1067
|
-
|
|
1077
|
+
// Segment dividers: minus divides on its right, plus on its left — so the value
|
|
1078
|
+
// cell is bounded on both sides. (DOM order is minus, input, [unit], plus on both
|
|
1079
|
+
// surfaces, so :first-child / :last-child land on the two buttons.)
|
|
1080
|
+
.num-btn:first-child {
|
|
1081
|
+
border-right: 1px solid var(--color-border);
|
|
1082
|
+
}
|
|
1083
|
+
.num-btn:last-child {
|
|
1068
1084
|
border-left: 1px solid var(--color-border);
|
|
1069
1085
|
}
|
|
1070
1086
|
|
|
1087
|
+
// ── Size variants ── the inner input carries .input-sm / .input-lg (height +
|
|
1088
|
+
// font), the wrapper carries .num-wrap-sm / .num-wrap-lg to scale the button
|
|
1089
|
+
// cells to match. Default (md) uses the base 44px cell above.
|
|
1090
|
+
.num-wrap-sm .num-btn {
|
|
1091
|
+
width: 36px;
|
|
1092
|
+
}
|
|
1093
|
+
.num-wrap-sm .num-input {
|
|
1094
|
+
font-size: 13px;
|
|
1095
|
+
}
|
|
1096
|
+
.num-wrap-lg .num-btn {
|
|
1097
|
+
width: 52px;
|
|
1098
|
+
}
|
|
1099
|
+
.num-wrap-lg .num-input {
|
|
1100
|
+
font-size: 16px;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1071
1103
|
// Affix (unit) in the num-wrap — a trailing pill after the input.
|
|
1072
1104
|
.num-wrap .affix {
|
|
1073
1105
|
background: var(--color-bg-muted);
|
package/scss/wui/_display.scss
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
display: flex;
|
|
13
13
|
flex-direction: column;
|
|
14
14
|
padding: 18px 20px;
|
|
15
|
-
border: 1px solid var(--color-border);
|
|
16
15
|
border-radius: var(--radius-lg);
|
|
17
16
|
corner-shape: squircle;
|
|
18
17
|
background: var(--color-surface);
|
|
@@ -64,7 +63,9 @@
|
|
|
64
63
|
}
|
|
65
64
|
.kpi-value {
|
|
66
65
|
margin-top: 6px;
|
|
67
|
-
|
|
66
|
+
// The value ("main heading") size flows from one var, so a size tier (sm/md/lg,
|
|
67
|
+
// spotlight) or a custom `valueSize` (px) can drive it. Default (md) is 28px.
|
|
68
|
+
font-size: var(--wui-kpi-value-size, 28px);
|
|
68
69
|
font-weight: 700;
|
|
69
70
|
// Large numerals need negative tracking to stop reading as separate digits;
|
|
70
71
|
// tabular figures stop a live-updating value from shifting width.
|
|
@@ -119,52 +120,109 @@
|
|
|
119
120
|
margin-top: 2px;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
// Surface variants — plain (above) is the
|
|
123
|
-
//
|
|
124
|
-
// drawing its own outline.
|
|
123
|
+
// Surface variants — plain (above) is borderless on the surface fill; these just swap the
|
|
124
|
+
// fill so a dashboard can mix a KPI strip with the surrounding card.
|
|
125
125
|
.kpi.kpi-filled {
|
|
126
|
-
border-color: transparent;
|
|
127
126
|
background: var(--color-bg-subtle);
|
|
128
127
|
}
|
|
129
128
|
.kpi.kpi-accent {
|
|
130
|
-
border-color: transparent;
|
|
131
129
|
background: var(--color-primary-soft);
|
|
132
130
|
}
|
|
133
131
|
// Needs-action state — orthogonal to variant, so it composes with plain/filled/accent
|
|
134
|
-
// rather than replacing the surface.
|
|
132
|
+
// rather than replacing the surface. Draws its own outline since the base tile no longer does.
|
|
135
133
|
.kpi.kpi-attention {
|
|
136
|
-
border
|
|
134
|
+
border: 1px solid var(--color-warning);
|
|
137
135
|
background: var(--color-warning-soft);
|
|
138
136
|
}
|
|
139
137
|
|
|
138
|
+
// Compact size — dense dashboards and tight cards. Shrinks the value + chrome.
|
|
139
|
+
.kpi.kpi-sm {
|
|
140
|
+
padding: 12px 14px;
|
|
141
|
+
--wui-kpi-value-size: 22px;
|
|
142
|
+
}
|
|
143
|
+
.kpi.kpi-sm .kpi-label {
|
|
144
|
+
font-size: 12px;
|
|
145
|
+
}
|
|
146
|
+
.kpi.kpi-sm .kpi-icon {
|
|
147
|
+
width: 32px;
|
|
148
|
+
height: 32px;
|
|
149
|
+
margin-bottom: 8px;
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
// Hero size — the single headline stat above a dashboard. Enlarges the value and its
|
|
141
153
|
// chrome; orthogonal to variant/inline/attention, so it composes with all of them.
|
|
142
154
|
.kpi.kpi-lg {
|
|
143
155
|
padding: 20px;
|
|
156
|
+
--wui-kpi-value-size: 40px;
|
|
144
157
|
}
|
|
145
158
|
.kpi.kpi-lg .kpi-label {
|
|
146
159
|
font-size: 14px;
|
|
147
160
|
}
|
|
148
|
-
.kpi.kpi-lg .kpi-value {
|
|
149
|
-
font-size: 40px;
|
|
150
|
-
}
|
|
151
161
|
.kpi.kpi-lg .kpi-icon {
|
|
152
162
|
width: 44px;
|
|
153
163
|
height: 44px;
|
|
154
164
|
}
|
|
155
165
|
|
|
166
|
+
// Spotlight — an editorial feature tile: the label sits top-left with the icon chip pinned
|
|
167
|
+
// top-right, the value is set in the display face (a product's serif brand face when it
|
|
168
|
+
// overrides --wui-font-display), and an optional caption reads under the number. For the
|
|
169
|
+
// single showcase stat on a marketing/portal surface where the number is the hero.
|
|
170
|
+
.kpi.kpi-spotlight {
|
|
171
|
+
display: grid;
|
|
172
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
173
|
+
column-gap: 16px;
|
|
174
|
+
padding: 22px 24px;
|
|
175
|
+
--wui-kpi-value-size: 44px;
|
|
176
|
+
}
|
|
177
|
+
.kpi.kpi-spotlight .kpi-icon {
|
|
178
|
+
grid-column: 2;
|
|
179
|
+
grid-row: 1;
|
|
180
|
+
align-self: start;
|
|
181
|
+
justify-self: end;
|
|
182
|
+
margin-bottom: 0;
|
|
183
|
+
}
|
|
184
|
+
.kpi.kpi-spotlight .kpi-label,
|
|
185
|
+
.kpi.kpi-spotlight .kpi-value,
|
|
186
|
+
.kpi.kpi-spotlight .kpi-caption,
|
|
187
|
+
.kpi.kpi-spotlight .kpi-delta {
|
|
188
|
+
grid-column: 1;
|
|
189
|
+
}
|
|
190
|
+
.kpi.kpi-spotlight .kpi-label {
|
|
191
|
+
align-self: center;
|
|
192
|
+
text-transform: uppercase;
|
|
193
|
+
letter-spacing: 0.08em;
|
|
194
|
+
font-weight: 600;
|
|
195
|
+
}
|
|
196
|
+
.kpi.kpi-spotlight .kpi-value {
|
|
197
|
+
margin-top: 14px;
|
|
198
|
+
// The editorial serif is this variant's signature (see the reference tile), so the value gets
|
|
199
|
+
// a serif stack directly — not --wui-font-display, which defaults to the body sans and so would
|
|
200
|
+
// never read as serif. A product that wants its own face overrides this one selector.
|
|
201
|
+
// Size comes from --wui-kpi-value-size (44px default, set on the spotlight root above), so a
|
|
202
|
+
// custom `valueSize` (px) can tame a too-large headline.
|
|
203
|
+
font-family: Georgia, 'Times New Roman', 'Noto Serif', serif;
|
|
204
|
+
line-height: 1;
|
|
205
|
+
font-weight: 600;
|
|
206
|
+
}
|
|
207
|
+
// Caption — the sub-line under the value ("Total sent"). Spotlight-only today, but the class
|
|
208
|
+
// is variant-agnostic so a plain tile can carry one too.
|
|
209
|
+
.kpi-caption {
|
|
210
|
+
margin-top: 8px;
|
|
211
|
+
font-size: 14px;
|
|
212
|
+
font-weight: 400;
|
|
213
|
+
color: var(--color-text-muted);
|
|
214
|
+
}
|
|
215
|
+
|
|
156
216
|
// The whole tile as a link (href). Colour/underline reset so the anchor reads as the
|
|
157
217
|
// card, not as text; the hover lift matches .res-card's tint-and-lift treatment.
|
|
158
218
|
a.kpi {
|
|
159
219
|
text-decoration: none;
|
|
160
220
|
color: inherit;
|
|
161
221
|
transition:
|
|
162
|
-
border-color 0.15s ease,
|
|
163
222
|
box-shadow 0.15s ease,
|
|
164
223
|
transform 0.15s ease;
|
|
165
224
|
}
|
|
166
225
|
a.kpi:hover {
|
|
167
|
-
border-color: var(--color-primary);
|
|
168
226
|
box-shadow: var(--shadow-md);
|
|
169
227
|
transform: translateY(-2px);
|
|
170
228
|
}
|
|
@@ -566,6 +624,9 @@ a.info-row:hover .info-value {
|
|
|
566
624
|
// cannot break into segments when an entry wraps to two lines.
|
|
567
625
|
// ============================================================================
|
|
568
626
|
.timeline {
|
|
627
|
+
// One knob for the whole component: the dot diameter. The rail, halo and
|
|
628
|
+
// glyph all derive from it, so `size` (px) scales everything in proportion.
|
|
629
|
+
--wui-timeline-dot: 24px;
|
|
569
630
|
position: relative;
|
|
570
631
|
margin: 0;
|
|
571
632
|
padding: 0 0 0 6px;
|
|
@@ -575,9 +636,9 @@ a.info-row:hover .info-value {
|
|
|
575
636
|
content: "";
|
|
576
637
|
position: absolute;
|
|
577
638
|
// Inset top and bottom so the rail starts and ends inside the first/last dot.
|
|
578
|
-
top:
|
|
579
|
-
bottom:
|
|
580
|
-
left:
|
|
639
|
+
top: calc(var(--wui-timeline-dot) / 2 - 2px);
|
|
640
|
+
bottom: calc(var(--wui-timeline-dot) / 2 - 2px);
|
|
641
|
+
left: calc(6px + var(--wui-timeline-dot) / 2 - 1px);
|
|
581
642
|
width: 2px;
|
|
582
643
|
background: var(--color-border);
|
|
583
644
|
}
|
|
@@ -594,8 +655,8 @@ a.info-row:hover .info-value {
|
|
|
594
655
|
position: relative;
|
|
595
656
|
z-index: 1;
|
|
596
657
|
flex: none;
|
|
597
|
-
width:
|
|
598
|
-
height:
|
|
658
|
+
width: var(--wui-timeline-dot);
|
|
659
|
+
height: var(--wui-timeline-dot);
|
|
599
660
|
display: inline-grid;
|
|
600
661
|
place-items: center;
|
|
601
662
|
border-radius: 999px;
|
|
@@ -659,6 +720,119 @@ a.info-row:hover .info-value {
|
|
|
659
720
|
white-space: pre-wrap;
|
|
660
721
|
overflow-wrap: anywhere;
|
|
661
722
|
}
|
|
723
|
+
// Plain inline subtext under the title — a muted line (distinct from the boxed
|
|
724
|
+
// .timeline-note). Works in both variants; used heavily by the progress variant.
|
|
725
|
+
.timeline-desc {
|
|
726
|
+
margin-top: 2px;
|
|
727
|
+
font-size: 13px;
|
|
728
|
+
line-height: 1.45;
|
|
729
|
+
color: var(--color-text-muted);
|
|
730
|
+
overflow-wrap: anywhere;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// ----------------------------------------------------------------------------
|
|
734
|
+
// Progress variant — a status tracker (Initiated → Submitted → … → Disbursed).
|
|
735
|
+
// Solid status dots (done/current/rejected) or a hollow ring (upcoming); the
|
|
736
|
+
// rail is drawn PER SEGMENT so a completed step's outgoing line reads filled.
|
|
737
|
+
// ----------------------------------------------------------------------------
|
|
738
|
+
.timeline--progress {
|
|
739
|
+
// Halo widths scale with the dot: the coloured ring and the thin blank ring.
|
|
740
|
+
--wui-timeline-halo: calc(var(--wui-timeline-dot) / 4);
|
|
741
|
+
--wui-timeline-halo-thin: calc(var(--wui-timeline-dot) / 12);
|
|
742
|
+
// Room on the left so a status dot's soft halo ring isn't clipped.
|
|
743
|
+
padding-left: calc(var(--wui-timeline-halo) + 1px);
|
|
744
|
+
}
|
|
745
|
+
// No single list rail — each item owns the segment down to the next dot so it
|
|
746
|
+
// can be tinted independently.
|
|
747
|
+
.timeline--progress::before {
|
|
748
|
+
display: none;
|
|
749
|
+
}
|
|
750
|
+
.timeline--progress .timeline-item {
|
|
751
|
+
padding-bottom: 22px;
|
|
752
|
+
}
|
|
753
|
+
// The connector from this dot down into the next one — anchored to the dot's
|
|
754
|
+
// bottom edge and centre, so it stays aligned at any dot size.
|
|
755
|
+
.timeline--progress .timeline-item:not(:last-child)::before {
|
|
756
|
+
content: "";
|
|
757
|
+
position: absolute;
|
|
758
|
+
top: var(--wui-timeline-dot);
|
|
759
|
+
bottom: -2px;
|
|
760
|
+
left: calc(var(--wui-timeline-dot) / 2 - 1px);
|
|
761
|
+
width: 2px;
|
|
762
|
+
background: var(--color-border);
|
|
763
|
+
}
|
|
764
|
+
// The rail fills green only BETWEEN two completed steps, so a lone/last done
|
|
765
|
+
// step doesn't leave a green stub dangling into an incomplete step below it.
|
|
766
|
+
.timeline--progress .timeline-item.is-done:has(+ .timeline-item.is-done)::before {
|
|
767
|
+
background: var(--color-success);
|
|
768
|
+
}
|
|
769
|
+
// Base / upcoming dot: a hollow ring for a not-yet-reached step. Its 2px grey
|
|
770
|
+
// halo is OPAQUE (mixed toward the surface, not transparent) so it masks the
|
|
771
|
+
// rail behind the dot — the connector only shows in the clear gap, never
|
|
772
|
+
// bleeding through the ring's halo.
|
|
773
|
+
.timeline--progress .timeline-dot,
|
|
774
|
+
.timeline--progress .timeline-item.is-upcoming .timeline-dot {
|
|
775
|
+
color: var(--color-text-subtle);
|
|
776
|
+
background: var(--color-surface);
|
|
777
|
+
box-shadow:
|
|
778
|
+
inset 0 0 0 2px var(--color-border),
|
|
779
|
+
0 0 0 var(--wui-timeline-halo-thin) color-mix(in srgb, var(--color-text-subtle) 12%, var(--color-surface));
|
|
780
|
+
}
|
|
781
|
+
// Solid status discs with a white glyph and a soft concentric halo ring in the
|
|
782
|
+
// dot's own hue. The fill is softened a touch (mixed toward the surface) so the
|
|
783
|
+
// states read gentler than the raw brand tones.
|
|
784
|
+
.timeline--progress .timeline-item.is-done .timeline-dot {
|
|
785
|
+
color: var(--color-on-scrim);
|
|
786
|
+
background: color-mix(in srgb, var(--color-success) 82%, var(--color-surface));
|
|
787
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) var(--color-success-soft);
|
|
788
|
+
}
|
|
789
|
+
.timeline--progress .timeline-item.is-current .timeline-dot {
|
|
790
|
+
color: var(--color-on-scrim);
|
|
791
|
+
background: color-mix(in srgb, var(--color-warning) 82%, var(--color-surface));
|
|
792
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) var(--color-warning-soft);
|
|
793
|
+
}
|
|
794
|
+
.timeline--progress .timeline-item.is-rejected .timeline-dot {
|
|
795
|
+
color: var(--color-on-scrim);
|
|
796
|
+
background: color-mix(in srgb, var(--color-danger) 82%, var(--color-surface));
|
|
797
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) var(--color-danger-soft);
|
|
798
|
+
}
|
|
799
|
+
// Blocked / waiting on an external action — informational blue.
|
|
800
|
+
.timeline--progress .timeline-item.is-on-hold .timeline-dot {
|
|
801
|
+
color: var(--color-on-scrim);
|
|
802
|
+
background: color-mix(in srgb, var(--color-info) 82%, var(--color-surface));
|
|
803
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) var(--color-info-soft);
|
|
804
|
+
}
|
|
805
|
+
// Cancelled / skipped — resolved-but-inactive outcomes read as muted slate,
|
|
806
|
+
// with a soft neutral halo, so they sit visually below the live states.
|
|
807
|
+
.timeline--progress .timeline-item.is-cancelled .timeline-dot,
|
|
808
|
+
.timeline--progress .timeline-item.is-skipped .timeline-dot {
|
|
809
|
+
color: var(--color-on-scrim);
|
|
810
|
+
background: color-mix(in srgb, var(--color-text-subtle) 82%, var(--color-surface));
|
|
811
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) color-mix(in srgb, var(--color-text-subtle) 16%, transparent);
|
|
812
|
+
}
|
|
813
|
+
// A cancelled step's title is struck through — the plan changed.
|
|
814
|
+
.timeline--progress .timeline-item.is-cancelled .timeline-title {
|
|
815
|
+
text-decoration: line-through;
|
|
816
|
+
color: var(--color-text-muted);
|
|
817
|
+
}
|
|
818
|
+
// Title row: title on the left, the timestamp pinned to the right.
|
|
819
|
+
.timeline--progress .timeline-head {
|
|
820
|
+
display: flex;
|
|
821
|
+
align-items: baseline;
|
|
822
|
+
justify-content: space-between;
|
|
823
|
+
gap: 12px;
|
|
824
|
+
}
|
|
825
|
+
.timeline--progress .timeline-head .timeline-at {
|
|
826
|
+
flex: none;
|
|
827
|
+
font-size: 12.5px;
|
|
828
|
+
color: var(--color-text-subtle);
|
|
829
|
+
white-space: nowrap;
|
|
830
|
+
}
|
|
831
|
+
// An upcoming step greys down — it hasn't happened yet.
|
|
832
|
+
.timeline--progress .timeline-item.is-upcoming .timeline-title {
|
|
833
|
+
color: var(--color-text-muted);
|
|
834
|
+
font-weight: 500;
|
|
835
|
+
}
|
|
662
836
|
|
|
663
837
|
// ============================================================================
|
|
664
838
|
// Feature-comparison table — us vs them. The highlighted column is tinted so
|
package/scss/wui/_flow.scss
CHANGED
|
@@ -830,16 +830,24 @@ wui-wizard-step[hidden] {
|
|
|
830
830
|
// A resting min-height so the footer doesn't jump as steps of different length
|
|
831
831
|
// swap in; content taller than this simply grows the panel.
|
|
832
832
|
min-height: 96px;
|
|
833
|
+
// A rounded squircle card around the step content. The surface (border + fill +
|
|
834
|
+
// padding) is what makes the radius visible — a radius on a border/background-less
|
|
835
|
+
// box shows nothing.
|
|
836
|
+
padding: 20px 24px;
|
|
837
|
+
border: 1px solid var(--color-border);
|
|
838
|
+
border-radius: 22px;
|
|
839
|
+
corner-shape: squircle;
|
|
840
|
+
background: var(--color-surface);
|
|
833
841
|
}
|
|
834
842
|
.wizard-footer {
|
|
835
|
-
// The
|
|
836
|
-
//
|
|
837
|
-
//
|
|
838
|
-
|
|
839
|
-
|
|
843
|
+
// The Back/Skip/Next/Finish bar sits at the END of the wizard content — NOT
|
|
844
|
+
// viewport-pinned. A transparent sticky bar (the old behaviour) pinned to the
|
|
845
|
+
// viewport bottom collided with a page/app footer below the wizard, burying the
|
|
846
|
+
// buttons behind it. Inline keeps them in the flow, always clear of a page footer.
|
|
847
|
+
// (For a wizard inside its OWN fixed-height scroll panel, a product can opt back
|
|
848
|
+
// into sticky on its container.)
|
|
840
849
|
margin-top: 24px;
|
|
841
850
|
padding: 16px 0;
|
|
842
|
-
z-index: 1;
|
|
843
851
|
}
|
|
844
852
|
.wizard-footer-actions {
|
|
845
853
|
display: flex;
|
package/scss/wui/_media.scss
CHANGED
|
@@ -527,16 +527,23 @@
|
|
|
527
527
|
border-radius: var(--radius-lg);
|
|
528
528
|
corner-shape: squircle;
|
|
529
529
|
border: 1px solid var(--color-border);
|
|
530
|
-
|
|
530
|
+
// Match AppInput: muted resting fill, lifts to a clean surface on focus.
|
|
531
|
+
background: var(--color-bg-muted);
|
|
531
532
|
overflow: hidden;
|
|
532
533
|
cursor: pointer;
|
|
533
534
|
transition:
|
|
534
535
|
border-color 0.12s,
|
|
535
536
|
box-shadow 0.12s;
|
|
536
537
|
}
|
|
537
|
-
.file-input:hover {
|
|
538
|
+
.file-input:hover:not(:focus-within) {
|
|
539
|
+
background: var(--color-bg-muted);
|
|
538
540
|
border-color: var(--color-border-strong);
|
|
539
541
|
}
|
|
542
|
+
.file-input:focus-within {
|
|
543
|
+
background: var(--color-surface);
|
|
544
|
+
border-color: var(--wui-primary-30);
|
|
545
|
+
box-shadow: none;
|
|
546
|
+
}
|
|
540
547
|
.file-input-btn {
|
|
541
548
|
display: inline-flex;
|
|
542
549
|
align-items: center;
|
|
@@ -579,21 +586,25 @@
|
|
|
579
586
|
width: 100%;
|
|
580
587
|
padding: 20px;
|
|
581
588
|
text-align: center;
|
|
582
|
-
|
|
583
|
-
border
|
|
589
|
+
// Match AppInput: muted resting fill + resting border (dashed kept as the dropzone
|
|
590
|
+
// affordance), border-strong on hover, primary border + surface lift on focus/drag.
|
|
591
|
+
background: var(--color-bg-muted);
|
|
592
|
+
border: 1px dashed var(--color-border);
|
|
584
593
|
transition:
|
|
585
594
|
border-color 0.15s ease,
|
|
586
595
|
background-color 0.15s ease;
|
|
587
596
|
}
|
|
588
|
-
// Hover
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
597
|
+
// Hover mirrors AppInput hover — border-strong, fill unchanged.
|
|
598
|
+
.file-input.file-input-tall:hover:not(:focus-within):not(.dragging) {
|
|
599
|
+
border-color: var(--color-border-strong);
|
|
600
|
+
background: var(--color-bg-muted);
|
|
601
|
+
}
|
|
602
|
+
// Focus and drag-over mirror AppInput focus — soft primary border, lift onto a clean surface.
|
|
592
603
|
.file-input.file-input-tall:focus-within,
|
|
593
604
|
.file-input.file-input-tall:active,
|
|
594
605
|
.file-input.file-input-tall.dragging {
|
|
595
|
-
border-color:
|
|
596
|
-
background: var(--color-
|
|
606
|
+
border-color: var(--wui-primary-30);
|
|
607
|
+
background: var(--color-surface);
|
|
597
608
|
box-shadow: none;
|
|
598
609
|
}
|
|
599
610
|
// A rejected file (wrong type / too large) turns the dropzone border danger to match the
|
|
@@ -639,11 +650,11 @@
|
|
|
639
650
|
.file-input.file-input-tall.has-files:hover,
|
|
640
651
|
.file-input.file-input-tall.has-files:focus-within {
|
|
641
652
|
border-color: var(--color-border-strong);
|
|
642
|
-
background: var(--color-bg-
|
|
653
|
+
background: var(--color-bg-muted);
|
|
643
654
|
}
|
|
644
655
|
.file-input.file-input-tall.has-files.dragging {
|
|
645
|
-
border-color:
|
|
646
|
-
background: var(--color-
|
|
656
|
+
border-color: var(--wui-primary-30);
|
|
657
|
+
background: var(--color-surface);
|
|
647
658
|
}
|
|
648
659
|
// Width presets — put a .file-w-* class on the field (React className / Razor class) to cap the
|
|
649
660
|
// dropzone to a fixed width instead of the full column. Mirror of the datepicker .date-w-* set.
|
package/scss/wui/_overlays.scss
CHANGED
|
@@ -15,8 +15,15 @@
|
|
|
15
15
|
// Do NOT put layout on backdropClassName — that targets a sibling, not
|
|
16
16
|
// .modal.
|
|
17
17
|
.modal-backdrop.wui-modal-dim {
|
|
18
|
-
|
|
18
|
+
// Frost the page behind the dialog — the blur is the star, so the dim is kept
|
|
19
|
+
// SOFT (a heavy scrim would flatten the blur into a muddy grey). A lighter dim
|
|
20
|
+
// over a strong blur reads as clean frosted glass; the modal is still clearly
|
|
21
|
+
// the one live surface. The dialog sits in `.modal` (a higher z-index sibling),
|
|
22
|
+
// so it is never blurred.
|
|
23
|
+
background: color-mix(in srgb, var(--color-scrim) 55%, transparent);
|
|
19
24
|
z-index: var(--wui-z-modal-backdrop);
|
|
25
|
+
backdrop-filter: blur(12px);
|
|
26
|
+
-webkit-backdrop-filter: blur(12px);
|
|
20
27
|
}
|
|
21
28
|
.modal.wui-modal-shell {
|
|
22
29
|
--bs-modal-bg: transparent;
|
|
@@ -68,8 +68,17 @@
|
|
|
68
68
|
}
|
|
69
69
|
.chip-ic {
|
|
70
70
|
display: inline-flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
line-height: 0;
|
|
71
74
|
color: var(--color-text-subtle);
|
|
72
75
|
}
|
|
76
|
+
// Bootstrap Icons carry a -.125em baseline nudge meant for inline-with-text use;
|
|
77
|
+
// inside the chip's flex row it just drops the glyph below the label's centre.
|
|
78
|
+
// Reset it so a library-icon leading glyph lines up with the label.
|
|
79
|
+
.chip-ic .bi::before {
|
|
80
|
+
vertical-align: middle;
|
|
81
|
+
}
|
|
73
82
|
.chip-avatar {
|
|
74
83
|
display: inline-flex;
|
|
75
84
|
width: 18px;
|