@webority/theme 0.9.13 → 0.9.15
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 +0 -32
- package/scss/wui/_controls.scss +25 -11
- package/scss/wui/_data.scss +11 -4
- package/scss/wui/_disclosure.scss +7 -3
- package/scss/wui/_display.scss +304 -1
- package/scss/wui/_flow.scss +29 -3
- package/scss/wui/_media.scss +17 -7
- package/scss/wui/_navigation.scss +25 -3
- package/scss/wui/_overlays.scss +14 -8
- package/scss/wui/_utilities.scss +34 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.15",
|
|
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
|
@@ -692,35 +692,6 @@ button.wui-input-leading:focus-visible {
|
|
|
692
692
|
transform: translateY(-1px);
|
|
693
693
|
}
|
|
694
694
|
|
|
695
|
-
// ---- Loading shimmer skeleton ----------------------------------------
|
|
696
|
-
.shimmer {
|
|
697
|
-
position: relative;
|
|
698
|
-
overflow: hidden;
|
|
699
|
-
background: var(--wui-bg-muted);
|
|
700
|
-
}
|
|
701
|
-
.shimmer::after {
|
|
702
|
-
content: "";
|
|
703
|
-
position: absolute;
|
|
704
|
-
inset: 0;
|
|
705
|
-
background: linear-gradient(90deg,
|
|
706
|
-
transparent,
|
|
707
|
-
rgba(255, 255, 255, 0.5),
|
|
708
|
-
transparent);
|
|
709
|
-
transform: translateX(-100%);
|
|
710
|
-
animation: shimmer 1.4s infinite;
|
|
711
|
-
}
|
|
712
|
-
// Dark mode shimmer — lightens the gradient for visibility on darker surfaces.
|
|
713
|
-
:root[data-theme="dark"] .shimmer::after {
|
|
714
|
-
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.08), transparent);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// ---- Keyframes -------------------------------------------------------
|
|
718
|
-
|
|
719
|
-
@keyframes shimmer {
|
|
720
|
-
0% { transform: translateX(-100%); }
|
|
721
|
-
100% { transform: translateX(100%); }
|
|
722
|
-
}
|
|
723
|
-
|
|
724
695
|
// ---- Native scrollbars (document + opt-in) --------------------------
|
|
725
696
|
// Scoped to html/body and `.wui-scroll` — never `*`. Global
|
|
726
697
|
// `*` restyled every nested scroller (modals, tiny panels) and fought
|
|
@@ -805,9 +776,6 @@ html.is-scrolling body::-webkit-scrollbar-thumb {
|
|
|
805
776
|
.press-feedback {
|
|
806
777
|
transition: none;
|
|
807
778
|
}
|
|
808
|
-
.shimmer::after {
|
|
809
|
-
animation: none;
|
|
810
|
-
}
|
|
811
779
|
// Hover animation options: hide the moving layer and drop lift/glow so no
|
|
812
780
|
// option animates, regardless of which .btn-anim-* is applied.
|
|
813
781
|
.btn:not(.btn-icon):not(.u-action)::before {
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -45,6 +45,9 @@
|
|
|
45
45
|
}
|
|
46
46
|
.form-check-label,
|
|
47
47
|
.wui-control-label {
|
|
48
|
+
// `min-width: 0` lets the label wrap inside its column instead of forcing
|
|
49
|
+
// the track to overflow.
|
|
50
|
+
min-width: 0;
|
|
48
51
|
cursor: pointer;
|
|
49
52
|
font-size: 14px;
|
|
50
53
|
line-height: 1.4;
|
|
@@ -52,21 +55,23 @@
|
|
|
52
55
|
user-select: none;
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
+
// The block variant is a 2-column grid — glyph in column 1, label in column 2 — so a long
|
|
59
|
+
// label WRAPS inside its own column and stays beside the glyph, never dropping onto a second
|
|
60
|
+
// line below it (a flex-wrap row breaks lines by each item's max-content size, so any label
|
|
61
|
+
// longer than the remaining width is bumped below the glyph regardless of flex sizing). The
|
|
62
|
+
// hint/error occupy column 2 too, landing on the next row aligned under the label text.
|
|
58
63
|
.form-check {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
.
|
|
63
|
-
|
|
64
|
+
display: grid;
|
|
65
|
+
grid-template-columns: auto 1fr;
|
|
66
|
+
// Vertically centre the glyph against the whole label block, so it sits in
|
|
67
|
+
// the middle when the label wraps to multiple lines.
|
|
68
|
+
align-items: center;
|
|
69
|
+
column-gap: 12px;
|
|
70
|
+
row-gap: 2px;
|
|
64
71
|
}
|
|
65
72
|
.form-check > .field-hint,
|
|
66
73
|
.form-check > .field-error {
|
|
67
|
-
|
|
68
|
-
margin-top: 2px;
|
|
69
|
-
padding-left: var(--wui-check-hint-indent);
|
|
74
|
+
grid-column: 2;
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
// Glyph — shared by BS form-check-input and the wui-check alias.
|
|
@@ -711,6 +716,15 @@ textarea.form-control.autosize {
|
|
|
711
716
|
outline: 2px solid var(--color-primary);
|
|
712
717
|
outline-offset: 2px;
|
|
713
718
|
}
|
|
719
|
+
.pw-toggle .pw-eye-off {
|
|
720
|
+
display: none;
|
|
721
|
+
}
|
|
722
|
+
.pw-toggle.pw-revealed .pw-eye {
|
|
723
|
+
display: none;
|
|
724
|
+
}
|
|
725
|
+
.pw-toggle.pw-revealed .pw-eye-off {
|
|
726
|
+
display: block;
|
|
727
|
+
}
|
|
714
728
|
|
|
715
729
|
// Clearable ✕ — a trailing reset button, placed inside .input-wrap / .input-icon. It only
|
|
716
730
|
// renders while the field has a value; .has-clear reserves the room so text never sits under it.
|
package/scss/wui/_data.scss
CHANGED
|
@@ -645,11 +645,15 @@
|
|
|
645
645
|
white-space: nowrap;
|
|
646
646
|
}
|
|
647
647
|
.u-action {
|
|
648
|
+
// The disc size is a custom property so the wrapper can scale it with the glyph `size`
|
|
649
|
+
// (React AppActionIcon / Razor <app-action-icon> set it to `size + 16px` — a constant 8px
|
|
650
|
+
// ring). Default 36px keeps the historical disc for the default `size=20`.
|
|
651
|
+
--wui-action-size: 36px;
|
|
648
652
|
display: inline-grid;
|
|
649
653
|
place-items: center;
|
|
650
654
|
flex: none;
|
|
651
|
-
width:
|
|
652
|
-
height:
|
|
655
|
+
width: var(--wui-action-size);
|
|
656
|
+
height: var(--wui-action-size);
|
|
653
657
|
padding: 0;
|
|
654
658
|
border: none;
|
|
655
659
|
background: var(--color-bg-subtle);
|
|
@@ -662,9 +666,12 @@
|
|
|
662
666
|
position: relative;
|
|
663
667
|
}
|
|
664
668
|
// Touch pointers need a 44px minimum target. Grow the hit area, not the
|
|
665
|
-
// ink, so row density and alignment are untouched.
|
|
669
|
+
// ink, so row density and alignment are untouched. This lives on ::before, NOT
|
|
670
|
+
// ::after: an AppActionIcon carries `data-wui-tip` for its tooltip, whose bubble
|
|
671
|
+
// owns `[data-wui-tip]::after` (imported later, same specificity) — sharing ::after
|
|
672
|
+
// would let the tooltip overwrite the hit-area box and silently drop the 44px target.
|
|
666
673
|
@media (pointer: coarse) {
|
|
667
|
-
.u-action::
|
|
674
|
+
.u-action::before {
|
|
668
675
|
content: "";
|
|
669
676
|
position: absolute;
|
|
670
677
|
top: 50%;
|
|
@@ -91,17 +91,21 @@
|
|
|
91
91
|
.faq-a-inner {
|
|
92
92
|
min-height: 0;
|
|
93
93
|
overflow: hidden;
|
|
94
|
+
// Horizontal padding is CONSTANT in both states. Toggling it with `.open` made the content
|
|
95
|
+
// jump 18px sideways the instant a pane collapsed (padding isn't transitioned, unlike the
|
|
96
|
+
// grid-row height) — the "slide left" bug. Side padding adds no height, so keeping it on the
|
|
97
|
+
// closed pane is safe; only the BOTTOM padding stays conditional (below), because a constant
|
|
98
|
+
// one leaks height on the collapsed 0fr row.
|
|
99
|
+
padding: 0 18px;
|
|
94
100
|
font-size: 14px;
|
|
95
101
|
line-height: 1.6;
|
|
96
102
|
color: var(--color-text-muted);
|
|
97
103
|
}
|
|
98
|
-
// Padding lives on a child of the clipped element so it does not add height while
|
|
99
|
-
// the row is collapsed to 0fr.
|
|
100
104
|
.faq-a-inner > :first-child {
|
|
101
105
|
margin-top: 0;
|
|
102
106
|
}
|
|
103
107
|
.faq-item.open .faq-a-inner {
|
|
104
|
-
padding:
|
|
108
|
+
padding-bottom: 16px;
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
@media (prefers-reduced-motion: reduce) {
|
package/scss/wui/_display.scss
CHANGED
|
@@ -1121,4 +1121,307 @@ a.info-row:hover .info-value {
|
|
|
1121
1121
|
}
|
|
1122
1122
|
.result-muted .result-code {
|
|
1123
1123
|
color: var(--color-text-muted);
|
|
1124
|
-
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1127
|
+
// AppLink / <app-link> — a text link with a choice of hover animation.
|
|
1128
|
+
// Base is a flat accent-coloured anchor; `.wui-link--<animation>` layers the
|
|
1129
|
+
// hover treatment. Every animation is disabled under prefers-reduced-motion,
|
|
1130
|
+
// where the link falls back to a static underline. React AppLink + Razor
|
|
1131
|
+
// <app-link> emit identical class names, so the two surfaces share this block.
|
|
1132
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1133
|
+
// The line each animation draws is our own thin ::after (or, for `none`, nothing) —
|
|
1134
|
+
// never the browser's text-decoration. Bootstrap's global `a:hover` re-adds
|
|
1135
|
+
// `text-decoration: underline` to every anchor, and `.wui-link` is an <a>, so without
|
|
1136
|
+
// pinning it off here that global underline bleeds onto the link on hover, sitting under
|
|
1137
|
+
// our animated line as a second, thicker underline. Force it off in every state.
|
|
1138
|
+
.wui-link,
|
|
1139
|
+
.wui-link:hover,
|
|
1140
|
+
.wui-link:focus,
|
|
1141
|
+
.wui-link:focus-visible,
|
|
1142
|
+
.wui-link:active {
|
|
1143
|
+
text-decoration: none;
|
|
1144
|
+
}
|
|
1145
|
+
.wui-link {
|
|
1146
|
+
display: inline-flex;
|
|
1147
|
+
align-items: center;
|
|
1148
|
+
gap: 0.35em;
|
|
1149
|
+
position: relative;
|
|
1150
|
+
color: var(--color-primary);
|
|
1151
|
+
font-weight: 500;
|
|
1152
|
+
cursor: pointer;
|
|
1153
|
+
border-radius: 4px;
|
|
1154
|
+
transition: color 0.25s var(--wui-ease-out-quart);
|
|
1155
|
+
}
|
|
1156
|
+
.wui-link:hover {
|
|
1157
|
+
color: var(--color-primary);
|
|
1158
|
+
}
|
|
1159
|
+
.wui-link:focus-visible {
|
|
1160
|
+
outline: 2px solid var(--color-primary);
|
|
1161
|
+
outline-offset: 3px;
|
|
1162
|
+
}
|
|
1163
|
+
.wui-link.is-disabled {
|
|
1164
|
+
color: var(--color-text-muted);
|
|
1165
|
+
cursor: not-allowed;
|
|
1166
|
+
opacity: 0.65;
|
|
1167
|
+
pointer-events: none;
|
|
1168
|
+
}
|
|
1169
|
+
.wui-link__label {
|
|
1170
|
+
position: relative;
|
|
1171
|
+
z-index: 0;
|
|
1172
|
+
white-space: nowrap;
|
|
1173
|
+
}
|
|
1174
|
+
.wui-link__ic {
|
|
1175
|
+
display: inline-flex;
|
|
1176
|
+
flex: none;
|
|
1177
|
+
}
|
|
1178
|
+
.wui-link__ic--left,
|
|
1179
|
+
.wui-link__ic--right {
|
|
1180
|
+
line-height: 0;
|
|
1181
|
+
}
|
|
1182
|
+
.wui-link--sm {
|
|
1183
|
+
font-size: 0.8125rem;
|
|
1184
|
+
}
|
|
1185
|
+
.wui-link--lg {
|
|
1186
|
+
font-size: 1.125rem;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// underline family — one thin single line, drawn with the same 1px ::after on every
|
|
1190
|
+
// variant so `static`, `underline`, and `underline-center` never differ in weight.
|
|
1191
|
+
// `underline` slides it in from the left, `underline-center` grows it from the middle,
|
|
1192
|
+
// `static` shows it always (see below).
|
|
1193
|
+
.wui-link--underline .wui-link__label::after,
|
|
1194
|
+
.wui-link--underline-center .wui-link__label::after,
|
|
1195
|
+
.wui-link--static .wui-link__label::after {
|
|
1196
|
+
content: "";
|
|
1197
|
+
position: absolute;
|
|
1198
|
+
left: 0;
|
|
1199
|
+
right: 0;
|
|
1200
|
+
bottom: -2px;
|
|
1201
|
+
height: 1px;
|
|
1202
|
+
background: currentColor;
|
|
1203
|
+
transform: scaleX(0);
|
|
1204
|
+
transform-origin: left center;
|
|
1205
|
+
transition: transform 0.28s var(--wui-ease-out-quart);
|
|
1206
|
+
}
|
|
1207
|
+
.wui-link--underline-center .wui-link__label::after {
|
|
1208
|
+
transform-origin: center;
|
|
1209
|
+
}
|
|
1210
|
+
.wui-link--underline:hover .wui-link__label::after,
|
|
1211
|
+
.wui-link--underline:focus-visible .wui-link__label::after,
|
|
1212
|
+
.wui-link--underline-center:hover .wui-link__label::after,
|
|
1213
|
+
.wui-link--underline-center:focus-visible .wui-link__label::after {
|
|
1214
|
+
transform: scaleX(1);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// slide — the whole link (label + icons) nudges to the right.
|
|
1218
|
+
.wui-link--slide {
|
|
1219
|
+
transition: color 0.25s var(--wui-ease-out-quart), transform 0.25s var(--wui-ease-out-quart);
|
|
1220
|
+
}
|
|
1221
|
+
.wui-link--slide:hover,
|
|
1222
|
+
.wui-link--slide:focus-visible {
|
|
1223
|
+
transform: translateX(3px);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// icon — a trailing icon slides and fades in on hover.
|
|
1227
|
+
.wui-link--icon .wui-link__ic--right {
|
|
1228
|
+
margin-inline-start: -0.2em;
|
|
1229
|
+
opacity: 0;
|
|
1230
|
+
transform: translateX(-4px);
|
|
1231
|
+
transition: opacity 0.25s var(--wui-ease-out-quart), transform 0.25s var(--wui-ease-out-quart), margin 0.25s var(--wui-ease-out-quart);
|
|
1232
|
+
}
|
|
1233
|
+
.wui-link--icon:hover .wui-link__ic--right,
|
|
1234
|
+
.wui-link--icon:focus-visible .wui-link__ic--right {
|
|
1235
|
+
margin-inline-start: 0;
|
|
1236
|
+
opacity: 1;
|
|
1237
|
+
transform: translateX(2px);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// highlight — a tinted background wipes across behind the label.
|
|
1241
|
+
.wui-link--highlight .wui-link__label::before {
|
|
1242
|
+
content: "";
|
|
1243
|
+
position: absolute;
|
|
1244
|
+
inset: -0.12em -0.3em;
|
|
1245
|
+
z-index: -1;
|
|
1246
|
+
border-radius: 4px;
|
|
1247
|
+
background: var(--color-primary-soft);
|
|
1248
|
+
transform: scaleX(0);
|
|
1249
|
+
transform-origin: left center;
|
|
1250
|
+
transition: transform 0.3s var(--wui-ease-out-quart);
|
|
1251
|
+
}
|
|
1252
|
+
.wui-link--highlight:hover .wui-link__label::before,
|
|
1253
|
+
.wui-link--highlight:focus-visible .wui-link__label::before {
|
|
1254
|
+
transform: scaleX(1);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// bracket — [ ] brackets slide inward around the label on hover.
|
|
1258
|
+
.wui-link--bracket .wui-link__label {
|
|
1259
|
+
padding-inline: 0.45em;
|
|
1260
|
+
}
|
|
1261
|
+
.wui-link--bracket .wui-link__label::before,
|
|
1262
|
+
.wui-link--bracket .wui-link__label::after {
|
|
1263
|
+
position: absolute;
|
|
1264
|
+
top: 0;
|
|
1265
|
+
opacity: 0;
|
|
1266
|
+
transition: opacity 0.25s var(--wui-ease-out-quart), transform 0.25s var(--wui-ease-out-quart);
|
|
1267
|
+
}
|
|
1268
|
+
.wui-link--bracket .wui-link__label::before {
|
|
1269
|
+
content: "[";
|
|
1270
|
+
left: 0;
|
|
1271
|
+
transform: translateX(0.35em);
|
|
1272
|
+
}
|
|
1273
|
+
.wui-link--bracket .wui-link__label::after {
|
|
1274
|
+
content: "]";
|
|
1275
|
+
right: 0;
|
|
1276
|
+
transform: translateX(-0.35em);
|
|
1277
|
+
}
|
|
1278
|
+
.wui-link--bracket:hover .wui-link__label::before,
|
|
1279
|
+
.wui-link--bracket:focus-visible .wui-link__label::before,
|
|
1280
|
+
.wui-link--bracket:hover .wui-link__label::after,
|
|
1281
|
+
.wui-link--bracket:focus-visible .wui-link__label::after {
|
|
1282
|
+
opacity: 1;
|
|
1283
|
+
transform: translateX(0);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
// fade — starts in body text colour, resolves to the accent on hover.
|
|
1287
|
+
.wui-link--fade {
|
|
1288
|
+
color: var(--color-text);
|
|
1289
|
+
}
|
|
1290
|
+
.wui-link--fade:hover,
|
|
1291
|
+
.wui-link--fade:focus-visible {
|
|
1292
|
+
color: var(--color-primary);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// static — the same thin single line, always visible (no text-decoration, so it matches
|
|
1296
|
+
// the animated underline's weight exactly). At rest the line is fully drawn; on hover it
|
|
1297
|
+
// re-draws with a left→right slide sweep, so the single line still animates.
|
|
1298
|
+
.wui-link--static .wui-link__label::after {
|
|
1299
|
+
transform: scaleX(1);
|
|
1300
|
+
transform-origin: left center;
|
|
1301
|
+
transition: none;
|
|
1302
|
+
}
|
|
1303
|
+
.wui-link--static:hover .wui-link__label::after,
|
|
1304
|
+
.wui-link--static:focus-visible .wui-link__label::after {
|
|
1305
|
+
animation: wui-link-underline-slide 0.32s var(--wui-ease-out-quart);
|
|
1306
|
+
}
|
|
1307
|
+
@keyframes wui-link-underline-slide {
|
|
1308
|
+
from {
|
|
1309
|
+
transform: scaleX(0);
|
|
1310
|
+
}
|
|
1311
|
+
to {
|
|
1312
|
+
transform: scaleX(1);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// grow — the label's letters open up on hover.
|
|
1317
|
+
.wui-link--grow {
|
|
1318
|
+
transition: color 0.25s var(--wui-ease-out-quart), letter-spacing 0.25s var(--wui-ease-out-quart);
|
|
1319
|
+
}
|
|
1320
|
+
.wui-link--grow:hover,
|
|
1321
|
+
.wui-link--grow:focus-visible {
|
|
1322
|
+
letter-spacing: 0.035em;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// glow — a soft accent glow blooms behind the text on hover.
|
|
1326
|
+
.wui-link--glow {
|
|
1327
|
+
transition: color 0.25s var(--wui-ease-out-quart), text-shadow 0.3s var(--wui-ease-out-quart);
|
|
1328
|
+
}
|
|
1329
|
+
.wui-link--glow:hover,
|
|
1330
|
+
.wui-link--glow:focus-visible {
|
|
1331
|
+
text-shadow: 0 0 10px color-mix(in srgb, var(--color-primary) 55%, transparent);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// shine — a light shimmer sweeps across the label on hover. The text is painted with a
|
|
1335
|
+
// gradient (mostly the accent, with a lighter band) clipped to the glyphs; hovering
|
|
1336
|
+
// animates that band from right to left across the word.
|
|
1337
|
+
.wui-link--shine .wui-link__label {
|
|
1338
|
+
background-image: linear-gradient(
|
|
1339
|
+
110deg,
|
|
1340
|
+
var(--color-primary) 0%,
|
|
1341
|
+
var(--color-primary) 42%,
|
|
1342
|
+
color-mix(in srgb, var(--color-primary) 25%, white) 50%,
|
|
1343
|
+
var(--color-primary) 58%,
|
|
1344
|
+
var(--color-primary) 100%
|
|
1345
|
+
);
|
|
1346
|
+
background-size: 220% 100%;
|
|
1347
|
+
background-position: 100% 0;
|
|
1348
|
+
background-repeat: no-repeat;
|
|
1349
|
+
-webkit-background-clip: text;
|
|
1350
|
+
background-clip: text;
|
|
1351
|
+
-webkit-text-fill-color: transparent;
|
|
1352
|
+
color: transparent;
|
|
1353
|
+
}
|
|
1354
|
+
.wui-link--shine:hover .wui-link__label,
|
|
1355
|
+
.wui-link--shine:focus-visible .wui-link__label {
|
|
1356
|
+
animation: wui-link-shine 0.9s var(--wui-ease-out-quart);
|
|
1357
|
+
}
|
|
1358
|
+
@keyframes wui-link-shine {
|
|
1359
|
+
from {
|
|
1360
|
+
background-position: 100% 0;
|
|
1361
|
+
}
|
|
1362
|
+
to {
|
|
1363
|
+
background-position: 0 0;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// none — a plain coloured link, no underline, no motion.
|
|
1368
|
+
.wui-link--none .wui-link__label {
|
|
1369
|
+
text-decoration: none;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1373
|
+
.wui-link,
|
|
1374
|
+
.wui-link--slide,
|
|
1375
|
+
.wui-link__label::after,
|
|
1376
|
+
.wui-link__label::before,
|
|
1377
|
+
.wui-link--icon .wui-link__ic--right {
|
|
1378
|
+
transition: none;
|
|
1379
|
+
}
|
|
1380
|
+
// Animated underlines settle to a visible static line rather than a hidden one.
|
|
1381
|
+
.wui-link--underline .wui-link__label::after,
|
|
1382
|
+
.wui-link--underline-center .wui-link__label::after {
|
|
1383
|
+
transform: scaleX(1);
|
|
1384
|
+
}
|
|
1385
|
+
// The static line drops its hover re-draw sweep.
|
|
1386
|
+
.wui-link--static:hover .wui-link__label::after,
|
|
1387
|
+
.wui-link--static:focus-visible .wui-link__label::after {
|
|
1388
|
+
animation: none;
|
|
1389
|
+
}
|
|
1390
|
+
.wui-link--slide:hover,
|
|
1391
|
+
.wui-link--slide:focus-visible {
|
|
1392
|
+
transform: none;
|
|
1393
|
+
}
|
|
1394
|
+
.wui-link--icon .wui-link__ic--right {
|
|
1395
|
+
opacity: 1;
|
|
1396
|
+
transform: none;
|
|
1397
|
+
margin-inline-start: 0;
|
|
1398
|
+
}
|
|
1399
|
+
.wui-link--highlight:hover .wui-link__label::before,
|
|
1400
|
+
.wui-link--highlight:focus-visible .wui-link__label::before {
|
|
1401
|
+
transform: none;
|
|
1402
|
+
}
|
|
1403
|
+
.wui-link--bracket .wui-link__label::before,
|
|
1404
|
+
.wui-link--bracket .wui-link__label::after {
|
|
1405
|
+
opacity: 1;
|
|
1406
|
+
transform: none;
|
|
1407
|
+
}
|
|
1408
|
+
// grow / glow settle to their rest state — no letter-spacing shift, no glow bloom.
|
|
1409
|
+
.wui-link--grow:hover,
|
|
1410
|
+
.wui-link--grow:focus-visible {
|
|
1411
|
+
letter-spacing: normal;
|
|
1412
|
+
}
|
|
1413
|
+
.wui-link--glow:hover,
|
|
1414
|
+
.wui-link--glow:focus-visible {
|
|
1415
|
+
text-shadow: none;
|
|
1416
|
+
}
|
|
1417
|
+
// shine drops the shimmer and paints the label a solid accent colour.
|
|
1418
|
+
.wui-link--shine:hover .wui-link__label,
|
|
1419
|
+
.wui-link--shine:focus-visible .wui-link__label {
|
|
1420
|
+
animation: none;
|
|
1421
|
+
}
|
|
1422
|
+
.wui-link--shine .wui-link__label {
|
|
1423
|
+
background-image: none;
|
|
1424
|
+
-webkit-text-fill-color: var(--color-primary);
|
|
1425
|
+
color: var(--color-primary);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
package/scss/wui/_flow.scss
CHANGED
|
@@ -676,15 +676,36 @@ button.step:focus-visible .step-circle {
|
|
|
676
676
|
gap: 12px;
|
|
677
677
|
}
|
|
678
678
|
.repeater-row {
|
|
679
|
+
position: relative;
|
|
679
680
|
display: flex;
|
|
681
|
+
// When present, the row-label line is the full-width FIRST line (a header band) and the
|
|
682
|
+
// fields wrap to the line beneath it. The markup renders that band only when the row is
|
|
683
|
+
// labelled or removable (see .repeater-row-label) — a removable row's empty band reserves
|
|
684
|
+
// the gap that keeps the fields clear of the absolute remove control. A row that is neither
|
|
685
|
+
// has no band and stays compact; there is no overlap to avoid, since the control is absent too.
|
|
686
|
+
flex-wrap: wrap;
|
|
680
687
|
align-items: flex-start;
|
|
681
688
|
gap: 12px;
|
|
682
|
-
padding: 14px 16px;
|
|
689
|
+
padding: 14px 16px 16px;
|
|
683
690
|
border: 1px solid var(--color-border);
|
|
684
691
|
border-radius: var(--radius-lg);
|
|
685
692
|
corner-shape: squircle;
|
|
686
693
|
background: var(--color-surface);
|
|
687
694
|
}
|
|
695
|
+
// The row's header band. Rendered only when the row is labelled (shows the label) or removable
|
|
696
|
+
// (an empty band reserving the gap that keeps the fields off the remove control); a row that is
|
|
697
|
+
// neither omits it and stays compact. Full-width so the fields wrap below it; min-height + the row
|
|
698
|
+
// gap clear the 36px remove disc; the right padding keeps any label text clear of the top-right
|
|
699
|
+
// control. Set the text via React rowLabel / Razor <app-repeater-row label="…">.
|
|
700
|
+
.repeater-row-label {
|
|
701
|
+
flex: 0 0 100%;
|
|
702
|
+
min-height: 24px;
|
|
703
|
+
padding-right: 44px;
|
|
704
|
+
font-size: 12px;
|
|
705
|
+
font-weight: 600;
|
|
706
|
+
line-height: 1.4;
|
|
707
|
+
color: var(--color-text-muted);
|
|
708
|
+
}
|
|
688
709
|
.repeater-index {
|
|
689
710
|
flex: none;
|
|
690
711
|
width: 22px;
|
|
@@ -705,8 +726,13 @@ button.step:focus-visible .step-circle {
|
|
|
705
726
|
min-width: 0;
|
|
706
727
|
}
|
|
707
728
|
.repeater-remove {
|
|
708
|
-
flex
|
|
709
|
-
|
|
729
|
+
// Pulled out of the row's flex flow and parked in the top-right corner of the header
|
|
730
|
+
// band, so it never steals horizontal room from the fields — the last field can run to
|
|
731
|
+
// the row's right edge instead of stopping short. Sits a little below the top edge so it
|
|
732
|
+
// reads as part of the band rather than pinned to the corner.
|
|
733
|
+
position: absolute;
|
|
734
|
+
top: 12px;
|
|
735
|
+
right: 12px;
|
|
710
736
|
}
|
|
711
737
|
.repeater-add {
|
|
712
738
|
align-self: flex-start;
|
package/scss/wui/_media.scss
CHANGED
|
@@ -863,10 +863,8 @@
|
|
|
863
863
|
// Round dropzone uploader (workspace/brand logo) and the settings-page photo
|
|
864
864
|
// ring share this same interactive-uploader pattern.
|
|
865
865
|
.set-uploader {
|
|
866
|
-
//
|
|
867
|
-
//
|
|
868
|
-
// px value scales the picker proportionally. md (120px) is the default.
|
|
869
|
-
--wui-logo-size: 120px;
|
|
866
|
+
// The ring diameter (--wui-logo-size) is set on the shell, not here, so the sibling remove
|
|
867
|
+
// control inherits it too; the ring, initials font and icons all derive from it.
|
|
870
868
|
display: inline-flex;
|
|
871
869
|
padding: 0;
|
|
872
870
|
border: none;
|
|
@@ -1026,8 +1024,11 @@
|
|
|
1026
1024
|
width: max-content;
|
|
1027
1025
|
max-width: 100%;
|
|
1028
1026
|
}
|
|
1029
|
-
// Positioning context for the remove control, which is a sibling of the button.
|
|
1027
|
+
// Positioning context for the remove control, which is a sibling of the button. It also owns the
|
|
1028
|
+
// one sizing knob — the ring diameter — so BOTH children (the button and the remove control)
|
|
1029
|
+
// inherit it. Presets and the numeric `size` prop set it inline; md (120px) is the default.
|
|
1030
1030
|
.set-uploader-shell {
|
|
1031
|
+
--wui-logo-size: 120px;
|
|
1031
1032
|
position: relative;
|
|
1032
1033
|
display: inline-block;
|
|
1033
1034
|
}
|
|
@@ -1105,8 +1106,11 @@
|
|
|
1105
1106
|
display: inline-flex;
|
|
1106
1107
|
align-items: center;
|
|
1107
1108
|
justify-content: center;
|
|
1108
|
-
|
|
1109
|
-
|
|
1109
|
+
// Scales with the ring (~23% of the diameter reads well) so it stays proportional at every
|
|
1110
|
+
// size instead of dwarfing a small (sm) ring and vanishing on a large (lg) one. Clamped so it
|
|
1111
|
+
// never shrinks below a comfortable tap target or grows past the badge look; md lands on 28px.
|
|
1112
|
+
width: clamp(20px, calc(var(--wui-logo-size) * 0.23), 34px);
|
|
1113
|
+
height: clamp(20px, calc(var(--wui-logo-size) * 0.23), 34px);
|
|
1110
1114
|
padding: 0;
|
|
1111
1115
|
border: 2px solid var(--color-surface);
|
|
1112
1116
|
border-radius: 50%;
|
|
@@ -1115,6 +1119,12 @@
|
|
|
1115
1119
|
cursor: pointer;
|
|
1116
1120
|
transition: transform 0.15s;
|
|
1117
1121
|
}
|
|
1122
|
+
// The × glyph scales with the button (its width/height attributes are set in JS/Razor as a
|
|
1123
|
+
// baseline; this overrides them, the way the overlay icon is scaled under its container query).
|
|
1124
|
+
.set-uploader-remove > .wui-icon {
|
|
1125
|
+
width: clamp(11px, calc(var(--wui-logo-size) * 0.115), 17px);
|
|
1126
|
+
height: clamp(11px, calc(var(--wui-logo-size) * 0.115), 17px);
|
|
1127
|
+
}
|
|
1118
1128
|
.set-uploader-remove:hover {
|
|
1119
1129
|
transform: scale(1.08);
|
|
1120
1130
|
}
|
|
@@ -193,6 +193,9 @@
|
|
|
193
193
|
color: var(--color-text-muted);
|
|
194
194
|
white-space: nowrap;
|
|
195
195
|
cursor: pointer;
|
|
196
|
+
// A tab can be a real <a href> (link tab) — strip the browser's link underline so a link
|
|
197
|
+
// tab is visually identical to a button tab.
|
|
198
|
+
text-decoration: none;
|
|
196
199
|
transition:
|
|
197
200
|
color 0.12s ease,
|
|
198
201
|
border-color 0.12s ease;
|
|
@@ -210,9 +213,18 @@
|
|
|
210
213
|
object-fit: cover;
|
|
211
214
|
border-radius: var(--radius-sm);
|
|
212
215
|
}
|
|
213
|
-
.app-tab:hover:not(:disabled):not(.active) {
|
|
216
|
+
.app-tab:hover:not(:disabled):not(.disabled):not(.active) {
|
|
214
217
|
color: var(--color-text);
|
|
215
218
|
}
|
|
219
|
+
// A link tab is a real <a>, and Bootstrap's `a:hover` (plus the theme's link hover) re-add an
|
|
220
|
+
// underline that the base `text-decoration: none` above (lower specificity) can't hold off. Scope
|
|
221
|
+
// under `.app-tabs` so these win in every state — a tab must read as a tab, never a link.
|
|
222
|
+
.app-tabs .app-tab,
|
|
223
|
+
.app-tabs .app-tab:hover,
|
|
224
|
+
.app-tabs .app-tab:focus,
|
|
225
|
+
.app-tabs .app-tab:focus-visible {
|
|
226
|
+
text-decoration: none;
|
|
227
|
+
}
|
|
216
228
|
.app-tab.active {
|
|
217
229
|
color: var(--color-primary);
|
|
218
230
|
// The active tab carries weight as well as colour — colour alone is a weak
|
|
@@ -238,10 +250,20 @@
|
|
|
238
250
|
.app-tabs-vertical .app-tab.active::after {
|
|
239
251
|
display: none;
|
|
240
252
|
}
|
|
241
|
-
|
|
253
|
+
// `:disabled` matches a disabled <button> tab; `.disabled`/[aria-disabled] cover a disabled
|
|
254
|
+
// link tab (an <a> can't be truly :disabled). Same inert, muted treatment for both.
|
|
255
|
+
.app-tab:disabled,
|
|
256
|
+
.app-tab.disabled,
|
|
257
|
+
.app-tab[aria-disabled="true"] {
|
|
242
258
|
color: var(--color-text-subtle);
|
|
243
259
|
cursor: not-allowed;
|
|
244
260
|
}
|
|
261
|
+
// A disabled link tab already has no href; also stop it swallowing clicks (a <button:disabled>
|
|
262
|
+
// gets this from the UA — an <a> does not).
|
|
263
|
+
.app-tab.disabled,
|
|
264
|
+
.app-tab[aria-disabled="true"] {
|
|
265
|
+
pointer-events: none;
|
|
266
|
+
}
|
|
245
267
|
.app-tab:focus-visible {
|
|
246
268
|
outline: 2px solid var(--color-primary);
|
|
247
269
|
outline-offset: -2px;
|
|
@@ -251,7 +273,7 @@
|
|
|
251
273
|
.app-tab.danger {
|
|
252
274
|
color: var(--color-danger-text);
|
|
253
275
|
}
|
|
254
|
-
.app-tab.danger:hover:not(:disabled):not(.active) {
|
|
276
|
+
.app-tab.danger:hover:not(:disabled):not(.disabled):not(.active) {
|
|
255
277
|
color: var(--color-danger-text);
|
|
256
278
|
}
|
|
257
279
|
.app-tab.danger.active {
|