@webority/theme 0.9.13 → 0.9.14
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/_controls.scss +16 -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 +10 -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.14",
|
|
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/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.
|
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,10 +676,13 @@ button.step:focus-visible .step-circle {
|
|
|
676
676
|
gap: 12px;
|
|
677
677
|
}
|
|
678
678
|
.repeater-row {
|
|
679
|
+
position: relative;
|
|
679
680
|
display: flex;
|
|
680
681
|
align-items: flex-start;
|
|
681
682
|
gap: 12px;
|
|
682
|
-
|
|
683
|
+
// Extra top room so the remove control (absolute, top-right) sits in a header
|
|
684
|
+
// band clear of the fields — the fields start below it and still run full width.
|
|
685
|
+
padding: 28px 16px 16px;
|
|
683
686
|
border: 1px solid var(--color-border);
|
|
684
687
|
border-radius: var(--radius-lg);
|
|
685
688
|
corner-shape: squircle;
|
|
@@ -705,8 +708,12 @@ button.step:focus-visible .step-circle {
|
|
|
705
708
|
min-width: 0;
|
|
706
709
|
}
|
|
707
710
|
.repeater-remove {
|
|
708
|
-
flex
|
|
709
|
-
|
|
711
|
+
// Pulled out of the row's flex flow and parked in the top-right corner, level
|
|
712
|
+
// with the field labels, so it never steals horizontal room from the fields —
|
|
713
|
+
// the last field can now run to the row's right edge instead of stopping short.
|
|
714
|
+
position: absolute;
|
|
715
|
+
top: 6px;
|
|
716
|
+
right: 8px;
|
|
710
717
|
}
|
|
711
718
|
.repeater-add {
|
|
712
719
|
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 {
|
package/scss/wui/_overlays.scss
CHANGED
|
@@ -404,16 +404,18 @@
|
|
|
404
404
|
// It sits above the element; for a collision-aware / richer tooltip, use AppTooltip /
|
|
405
405
|
// <app-tooltip> instead.
|
|
406
406
|
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
//
|
|
413
|
-
// and the tab instance is never registered.
|
|
407
|
+
// Sole remaining user: <app-tabs> tab buttons, which CANNOT use a Bootstrap tooltip — the button
|
|
408
|
+
// already owns a `bs.tab` instance and Bootstrap permits only one instance per element, so binding
|
|
409
|
+
// `bs.Tooltip` to it logs "Bootstrap doesn't allow more than one instance per element" and the tab
|
|
410
|
+
// instance is never registered. Everything else with a `tooltip` prop (AppIcon / <app-icon>
|
|
411
|
+
// included) now routes through the shared collision-aware AppTooltip / Bootstrap-tooltip (Popper)
|
|
412
|
+
// engine instead.
|
|
414
413
|
[data-wui-tip] {
|
|
415
414
|
position: relative;
|
|
416
415
|
}
|
|
416
|
+
// The wrapper the `tooltip` prop on AppIcon / <app-icon> puts around a glyph — now just the
|
|
417
|
+
// inline-flex layout; the tooltip itself rides the shared Popper engine (Bootstrap-tooltip
|
|
418
|
+
// data-attributes on this span, AppTooltip on React), not the CSS bubble above.
|
|
417
419
|
.wui-icon-tip {
|
|
418
420
|
display: inline-flex;
|
|
419
421
|
vertical-align: -0.125em;
|
|
@@ -424,7 +426,11 @@
|
|
|
424
426
|
bottom: calc(100% + 8px);
|
|
425
427
|
left: 50%;
|
|
426
428
|
transform: translateX(-50%);
|
|
427
|
-
|
|
429
|
+
// Sits on the dedicated tooltip rung — above menus/popovers/modals, below toasts — so it is
|
|
430
|
+
// never stacked under an overlay it is meant to annotate. This is a fixed-position CSS bubble
|
|
431
|
+
// (always above); for a collision-aware tooltip that flips to stay on-screen, use AppTooltip /
|
|
432
|
+
// <app-tooltip> (Popper), which AppActionIcon wraps for exactly that reason.
|
|
433
|
+
z-index: var(--wui-z-tooltip, 1650);
|
|
428
434
|
width: max-content;
|
|
429
435
|
max-width: 240px;
|
|
430
436
|
padding: 7px 11px;
|