@webority/theme 0.64.0 → 0.66.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/dist/purge-safelist.json +39 -0
- package/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_components.scss +15 -0
- package/scss/_tokens.scss +4 -2
- package/scss/_wui-components.scss +5 -0
- package/scss/wui/_chart.scss +2 -0
- package/scss/wui/_controls.scss +15 -0
- package/scss/wui/_data.scss +34 -3
- package/scss/wui/_display.scss +34 -0
- package/scss/wui/_due-date.scss +26 -0
- package/scss/wui/_filter-popover.scss +63 -0
- package/scss/wui/_kanban.scss +18 -0
- package/scss/wui/_metric-strip.scss +85 -0
- package/scss/wui/_modal-remote.scss +34 -0
- package/scss/wui/_page-header-bar.scss +68 -0
- package/scss/wui/_score-scale.scss +61 -0
- package/scss/wui/_shell.scss +28 -2
- package/scss/wui/_split-view.scss +4 -0
- package/scss/wui/_toolbar.scss +8 -0
- package/scss/wui/_utilities.scss +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.66.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": {
|
package/scss/_components.scss
CHANGED
|
@@ -523,6 +523,21 @@
|
|
|
523
523
|
margin-top: 0.25rem;
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
+
// An item that hides from a width up (AppDropdown item `hiddenFrom`): the wide screen shows the
|
|
527
|
+
// same control in its own place, so the one overflow menu drops the row from that width on.
|
|
528
|
+
@media (min-width: 576px) {
|
|
529
|
+
.dropdown-item.wui-hidden-from-sm { display: none; }
|
|
530
|
+
}
|
|
531
|
+
@media (min-width: 768px) {
|
|
532
|
+
.dropdown-item.wui-hidden-from-md { display: none; }
|
|
533
|
+
}
|
|
534
|
+
@media (min-width: 992px) {
|
|
535
|
+
.dropdown-item.wui-hidden-from-lg { display: none; }
|
|
536
|
+
}
|
|
537
|
+
@media (min-width: 1200px) {
|
|
538
|
+
.dropdown-item.wui-hidden-from-xl { display: none; }
|
|
539
|
+
}
|
|
540
|
+
|
|
526
541
|
// ---- Ghost button: the tertiary action --------------------------------
|
|
527
542
|
// Brand-coloured, underlined text with no box in any state. It has no visible container,
|
|
528
543
|
// so nothing may draw one: no hover fill, no sheen sweep, no lift or press nudge, and no
|
package/scss/_tokens.scss
CHANGED
|
@@ -43,8 +43,10 @@
|
|
|
43
43
|
--wui-field-bg: #F9FBFC;
|
|
44
44
|
// Resting and hover border of every input-like control. They clear WCAG 1.4.11's 3:1
|
|
45
45
|
// against the field fill and the surface, so a field's edge is visible without focus.
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// Both sit just above the floor (3.11:1 and 3.63:1 on the fill) so the edge stays light;
|
|
47
|
+
// check-contrast fails if either drops under it.
|
|
48
|
+
--wui-field-border: #888FA4;
|
|
49
|
+
--wui-field-border-hover: #7C839A;
|
|
48
50
|
--wui-border-light: #EBEDF3;
|
|
49
51
|
--wui-border-soft: #F4F6F8;
|
|
50
52
|
// The unfilled part of a progress bar / meter. Darker than every surface it can sit on
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
@import "wui/carousel";
|
|
65
65
|
@import "wui/onboarding-checklist";
|
|
66
66
|
@import "wui/rating";
|
|
67
|
+
@import "wui/score-scale";
|
|
67
68
|
@import "wui/relative-time";
|
|
68
69
|
@import "wui/resizable";
|
|
69
70
|
@import "wui/rolling-number";
|
|
@@ -72,6 +73,10 @@
|
|
|
72
73
|
// After `navigation`/`overlays`: the switcher restates dropdown-item chrome those partials
|
|
73
74
|
// set, so it has to win on equal specificity.
|
|
74
75
|
@import "wui/workspace";
|
|
76
|
+
@import "wui/page-header-bar";
|
|
77
|
+
@import "wui/filter-popover";
|
|
78
|
+
@import "wui/metric-strip";
|
|
79
|
+
@import "wui/due-date";
|
|
75
80
|
|
|
76
81
|
// Country flags are NOT part of this aggregate. The 245 `.wui-flag-*` rules are about 187KB raw
|
|
77
82
|
// (38KB Brotli) of inlined SVG, and most sites never render a phone field or country picker.
|
package/scss/wui/_chart.scss
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// engine draws inside .wui-chart-canvas with colours the element reads from --wui-chart-*.
|
|
12
12
|
wui-chart {
|
|
13
13
|
position: relative;
|
|
14
|
+
// Keeps the engine tooltip in the chart's own stacking layer, under sticky page chrome.
|
|
15
|
+
isolation: isolate;
|
|
14
16
|
display: block;
|
|
15
17
|
min-width: 0;
|
|
16
18
|
color: var(--color-text-muted);
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -1471,6 +1471,21 @@ wui-select[compact] {
|
|
|
1471
1471
|
font-size: 12px;
|
|
1472
1472
|
}
|
|
1473
1473
|
|
|
1474
|
+
// A count beside an AppSegmented option's label: a small muted figure with tabular digits, the
|
|
1475
|
+
// same treatment as a choice chip's count. Plain colour, no opacity, so it keeps its contrast.
|
|
1476
|
+
.wui-segmented-option-count {
|
|
1477
|
+
font-size: 11px;
|
|
1478
|
+
font-weight: 500;
|
|
1479
|
+
font-variant-numeric: tabular-nums;
|
|
1480
|
+
color: var(--color-text-muted);
|
|
1481
|
+
}
|
|
1482
|
+
// Keyboard focus on the selected segment: the ring sits inside the pill, over its selected ring,
|
|
1483
|
+
// so the focus cue and the selection read as one shape and not as two rings. The selector outranks
|
|
1484
|
+
// the unscoped focus rule in _display.scss, which loads after this partial.
|
|
1485
|
+
.segmented .segmented-option:has(.segmented-input:checked:focus-visible) {
|
|
1486
|
+
outline-offset: var(--wui-focus-ring-inset);
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1474
1489
|
// Form section heading — a branded accent bar rather than a full-width hairline,
|
|
1475
1490
|
// so a long form reads as designed sections instead of raw rules.
|
|
1476
1491
|
//
|
package/scss/wui/_data.scss
CHANGED
|
@@ -194,7 +194,6 @@
|
|
|
194
194
|
font-size: 12px;
|
|
195
195
|
font-weight: 500;
|
|
196
196
|
letter-spacing: 0.04em;
|
|
197
|
-
text-transform: uppercase;
|
|
198
197
|
white-space: nowrap;
|
|
199
198
|
vertical-align: middle;
|
|
200
199
|
transition:
|
|
@@ -293,6 +292,10 @@
|
|
|
293
292
|
// block above) — if it ever needs viewport-relative sizing again, do it without turning
|
|
294
293
|
// the scroll container into a query container.
|
|
295
294
|
}
|
|
295
|
+
// Flush: no inset above the column header row, so the header sits at the top of the wrap.
|
|
296
|
+
.table-wrap.wui-table-flush:has(> .table-ledger) {
|
|
297
|
+
padding-top: 0;
|
|
298
|
+
}
|
|
296
299
|
.table-ledger {
|
|
297
300
|
border-spacing: 0;
|
|
298
301
|
}
|
|
@@ -306,7 +309,6 @@
|
|
|
306
309
|
font-weight: 500;
|
|
307
310
|
line-height: 17px;
|
|
308
311
|
letter-spacing: 0.04em;
|
|
309
|
-
text-transform: uppercase;
|
|
310
312
|
// --color-text-subtle (#78829D) is ~3.5:1 on the muted band, below WCAG AA's 4.5:1
|
|
311
313
|
// for 12px text. A column header names the data under it — that is not decorative
|
|
312
314
|
// text, so it takes the muted ramp (~7.5:1). Caught by the axe pass, on both surfaces.
|
|
@@ -884,6 +886,12 @@
|
|
|
884
886
|
min-width: 0;
|
|
885
887
|
}
|
|
886
888
|
|
|
889
|
+
// The identity column takes the width the other columns leave (its header carries wui-col-grow).
|
|
890
|
+
// Its name clamps to one line, and the full text is the tooltip.
|
|
891
|
+
.table-ledger th.wui-col-grow {
|
|
892
|
+
width: 100%;
|
|
893
|
+
}
|
|
894
|
+
|
|
887
895
|
// Row-selection checkbox column.
|
|
888
896
|
.table-ledger th.wui-select-cell,
|
|
889
897
|
.table-ledger td.wui-select-cell {
|
|
@@ -953,7 +961,6 @@
|
|
|
953
961
|
font-size: 12px;
|
|
954
962
|
font-weight: 600;
|
|
955
963
|
letter-spacing: 0.04em;
|
|
956
|
-
text-transform: uppercase;
|
|
957
964
|
color: var(--color-text-subtle);
|
|
958
965
|
}
|
|
959
966
|
.wui-column-menu-item {
|
|
@@ -1027,6 +1034,7 @@
|
|
|
1027
1034
|
display: flex;
|
|
1028
1035
|
align-items: center;
|
|
1029
1036
|
justify-content: space-between;
|
|
1037
|
+
flex-wrap: wrap;
|
|
1030
1038
|
gap: 12px;
|
|
1031
1039
|
height: auto;
|
|
1032
1040
|
min-height: 0;
|
|
@@ -1067,6 +1075,29 @@
|
|
|
1067
1075
|
.table-ledger.wui-table-stack > * > tr > td:not(.wui-select-cell, .wui-expand-cell):is(:first-child, .wui-select-cell + td, .wui-expand-cell + td)::before {
|
|
1068
1076
|
content: none;
|
|
1069
1077
|
}
|
|
1078
|
+
// A title wraps in full on a card: a phone reader has no tooltip to read the rest from.
|
|
1079
|
+
.table-ledger.wui-table-stack .u-name {
|
|
1080
|
+
display: block;
|
|
1081
|
+
overflow: visible;
|
|
1082
|
+
}
|
|
1083
|
+
// A group header is a section label between the cards, not a card of its own.
|
|
1084
|
+
.table-ledger.wui-table-stack > tbody > tr.wui-group-row {
|
|
1085
|
+
margin-bottom: 0;
|
|
1086
|
+
padding: 0;
|
|
1087
|
+
border: 0;
|
|
1088
|
+
border-radius: 0;
|
|
1089
|
+
background: none;
|
|
1090
|
+
}
|
|
1091
|
+
.table-ledger.wui-table-stack > tbody > tr.wui-group-row > th.wui-group-cell {
|
|
1092
|
+
display: block;
|
|
1093
|
+
padding: 12px 0 4px;
|
|
1094
|
+
border: 0;
|
|
1095
|
+
background: none;
|
|
1096
|
+
}
|
|
1097
|
+
// A row with no actions leaves no empty band under its card.
|
|
1098
|
+
.table-ledger.wui-table-stack > tbody > tr > td:has(> .u-row-actions:empty) {
|
|
1099
|
+
display: none;
|
|
1100
|
+
}
|
|
1070
1101
|
// Empty cells (a footer's blank columns) take no line.
|
|
1071
1102
|
.table-ledger.wui-table-stack > * > tr > td:empty {
|
|
1072
1103
|
display: none;
|
package/scss/wui/_display.scss
CHANGED
|
@@ -413,6 +413,40 @@ a.info-row:hover .info-value {
|
|
|
413
413
|
--descs-cols: 1;
|
|
414
414
|
}
|
|
415
415
|
}
|
|
416
|
+
// Summary layout (React `layout="summary"`): one row per item, its label left and its value right.
|
|
417
|
+
// Declared after the column variants so the single column wins on equal specificity.
|
|
418
|
+
.wui-descs-summary .descs-list {
|
|
419
|
+
--descs-cols: 1;
|
|
420
|
+
}
|
|
421
|
+
.wui-descs-summary .descs-item {
|
|
422
|
+
flex-direction: row;
|
|
423
|
+
align-items: baseline;
|
|
424
|
+
justify-content: space-between;
|
|
425
|
+
gap: 16px;
|
|
426
|
+
grid-column: 1 / -1;
|
|
427
|
+
padding: 12px 16px;
|
|
428
|
+
}
|
|
429
|
+
.wui-descs-summary .descs-label,
|
|
430
|
+
.wui-descs-summary .descs-value {
|
|
431
|
+
padding: 0;
|
|
432
|
+
}
|
|
433
|
+
.wui-descs-summary .descs-value {
|
|
434
|
+
margin-left: auto;
|
|
435
|
+
text-align: right;
|
|
436
|
+
font-variant-numeric: tabular-nums;
|
|
437
|
+
}
|
|
438
|
+
// Emphasis (React item `emphasis`): the total row, bold, with a rule above it. The row before it
|
|
439
|
+
// drops its own bottom rule so the two do not stack into a double line.
|
|
440
|
+
.descs-item.wui-descs-emphasis {
|
|
441
|
+
border-top: 1px solid var(--color-text-muted);
|
|
442
|
+
}
|
|
443
|
+
.descs-item:has(+ .wui-descs-emphasis) {
|
|
444
|
+
border-bottom-color: transparent;
|
|
445
|
+
}
|
|
446
|
+
.descs-item.wui-descs-emphasis .descs-label,
|
|
447
|
+
.descs-item.wui-descs-emphasis .descs-value {
|
|
448
|
+
font-weight: 700;
|
|
449
|
+
}
|
|
416
450
|
|
|
417
451
|
// ============================================================================
|
|
418
452
|
// Segmented control — 2–4 mutually-exclusive options shown side by side.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// ── Due date ──
|
|
2
|
+
// AppDueDate / <app-due-date> (a <wui-due-date> element): a due date that never wraps. An overdue
|
|
3
|
+
// date reads in danger with its glyph, a date within soon-days reads "N days left" in warning.
|
|
4
|
+
//
|
|
5
|
+
// Part of the wui-components layer, see _wui-components.scss for load order.
|
|
6
|
+
|
|
7
|
+
.wui-due-date {
|
|
8
|
+
white-space: nowrap;
|
|
9
|
+
font-variant-numeric: tabular-nums;
|
|
10
|
+
|
|
11
|
+
> .wui-icon {
|
|
12
|
+
margin-right: 0.25rem;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.wui-due-date-danger {
|
|
17
|
+
color: var(--color-danger-text);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.wui-due-date-warning {
|
|
21
|
+
color: var(--color-warning-text);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.wui-due-date-empty {
|
|
25
|
+
color: var(--color-text-muted);
|
|
26
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// The floating filter panel: <wui-filter-popover> with its Filters button, drawn by AppFilterBar
|
|
2
|
+
// `floating` and <app-filter-bar floating>. The element places the panel (anchorOverlay, fixed), so
|
|
3
|
+
// this partial only draws it: a 600 px panel of facets in two columns, and a foot with the Keep open
|
|
4
|
+
// switch and Clear all.
|
|
5
|
+
|
|
6
|
+
.wui-filter-popover {
|
|
7
|
+
display: inline-block;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.wui-filter-popover-trigger {
|
|
11
|
+
white-space: nowrap;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.wui-filter-popover-panel {
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
width: 600px;
|
|
17
|
+
max-width: calc(100vw - 1rem);
|
|
18
|
+
max-height: 80vh;
|
|
19
|
+
overflow-y: auto;
|
|
20
|
+
padding: 1rem;
|
|
21
|
+
background: var(--color-surface);
|
|
22
|
+
border: 1px solid var(--color-border);
|
|
23
|
+
border-radius: var(--wui-radius-lg);
|
|
24
|
+
box-shadow: var(--wui-shadow-overlay);
|
|
25
|
+
z-index: var(--wui-z-popover);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.wui-filter-popover-panel[hidden] {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.wui-filter-popover-grid {
|
|
33
|
+
display: grid;
|
|
34
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
35
|
+
gap: 0.75rem 1rem;
|
|
36
|
+
align-items: start;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@media (max-width: 575.98px) {
|
|
40
|
+
.wui-filter-popover-grid {
|
|
41
|
+
grid-template-columns: minmax(0, 1fr);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.wui-filter-popover-foot {
|
|
46
|
+
display: flex;
|
|
47
|
+
flex-wrap: wrap;
|
|
48
|
+
align-items: center;
|
|
49
|
+
justify-content: space-between;
|
|
50
|
+
gap: 0.5rem 1rem;
|
|
51
|
+
margin-top: 1rem;
|
|
52
|
+
padding-top: 0.75rem;
|
|
53
|
+
border-top: 1px solid var(--color-border);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.wui-filter-popover-count {
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.wui-filter-popover-more {
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
cursor: default;
|
|
63
|
+
}
|
package/scss/wui/_kanban.scss
CHANGED
|
@@ -145,6 +145,14 @@
|
|
|
145
145
|
display: none;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
// head-layout="stacked": the stage name takes a line of its own and the tone dot goes.
|
|
149
|
+
.wui-kanban[data-head-layout="stacked"] .wui-kanban-dot {
|
|
150
|
+
display: none;
|
|
151
|
+
}
|
|
152
|
+
.wui-kanban[data-head-layout="stacked"] .wui-kanban-title {
|
|
153
|
+
flex: 1 0 100%;
|
|
154
|
+
}
|
|
155
|
+
|
|
148
156
|
// The page's own controls at the right of the column head (a "+" that adds a card, say). They sit
|
|
149
157
|
// before the collapse button, which then gives up its auto margin.
|
|
150
158
|
.wui-kanban-actions {
|
|
@@ -256,6 +264,11 @@
|
|
|
256
264
|
.wui-kanban-card:hover {
|
|
257
265
|
border-color: var(--color-border-strong);
|
|
258
266
|
}
|
|
267
|
+
// The card just added (active-card-key), tinted as the active row of a table is.
|
|
268
|
+
.wui-kanban-card[aria-current="true"] {
|
|
269
|
+
background: var(--color-primary-soft);
|
|
270
|
+
border-color: var(--color-primary);
|
|
271
|
+
}
|
|
259
272
|
.wui-kanban-card:focus-visible {
|
|
260
273
|
outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
|
|
261
274
|
outline-offset: var(--wui-focus-ring-offset);
|
|
@@ -499,6 +512,11 @@
|
|
|
499
512
|
overscroll-behavior-y: contain;
|
|
500
513
|
padding-top: 4px;
|
|
501
514
|
}
|
|
515
|
+
// The body is a flex column with a fixed height here, so a card would squash to fit; it keeps its
|
|
516
|
+
// own height and the column scrolls instead.
|
|
517
|
+
.wui-kanban[fill] .wui-kanban-card {
|
|
518
|
+
flex-shrink: 0;
|
|
519
|
+
}
|
|
502
520
|
}
|
|
503
521
|
|
|
504
522
|
// ── Phone column switcher and swipe hint (hidden from md up) ──
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Metric strip: a row of linked values, each a label over a value over a note.
|
|
3
|
+
// No card chrome. Markup and classes are the same for <app-metric-strip> and
|
|
4
|
+
// React AppMetricStrip.
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
.wui-metric-strip {
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
10
|
+
gap: 16px;
|
|
11
|
+
align-items: end;
|
|
12
|
+
}
|
|
13
|
+
// Two columns below 768px, the column count from the attribute above it.
|
|
14
|
+
@media (min-width: 768px) {
|
|
15
|
+
.wui-metric-strip.wui-metric-strip-cols-3 {
|
|
16
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
17
|
+
}
|
|
18
|
+
.wui-metric-strip.wui-metric-strip-cols-4 {
|
|
19
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
20
|
+
}
|
|
21
|
+
.wui-metric-strip.wui-metric-strip-cols-5 {
|
|
22
|
+
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.wui-metric {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
min-width: 0;
|
|
30
|
+
color: inherit;
|
|
31
|
+
text-decoration: none;
|
|
32
|
+
}
|
|
33
|
+
a.wui-metric:focus-visible {
|
|
34
|
+
outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
|
|
35
|
+
outline-offset: var(--wui-focus-ring-offset);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.wui-metric-label {
|
|
39
|
+
font-size: 13px;
|
|
40
|
+
font-weight: 500;
|
|
41
|
+
color: var(--color-text-muted);
|
|
42
|
+
white-space: nowrap;
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
text-overflow: ellipsis;
|
|
45
|
+
}
|
|
46
|
+
.wui-metric-value {
|
|
47
|
+
margin-top: 2px;
|
|
48
|
+
font-size: 20px;
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
line-height: 1.2;
|
|
51
|
+
font-variant-numeric: tabular-nums;
|
|
52
|
+
color: var(--color-text);
|
|
53
|
+
white-space: nowrap;
|
|
54
|
+
}
|
|
55
|
+
a.wui-metric:hover .wui-metric-value {
|
|
56
|
+
text-decoration: underline;
|
|
57
|
+
text-underline-offset: 3px;
|
|
58
|
+
}
|
|
59
|
+
.wui-metric-note {
|
|
60
|
+
margin-top: 2px;
|
|
61
|
+
font-size: 12px;
|
|
62
|
+
color: var(--color-text-muted);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.wui-metric-strip-sm .wui-metric-label,
|
|
66
|
+
.wui-metric-strip-sm .wui-metric-note {
|
|
67
|
+
font-size: 12px;
|
|
68
|
+
}
|
|
69
|
+
.wui-metric-strip-sm .wui-metric-value {
|
|
70
|
+
font-size: 18px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// The tone tints the value only; the label and note stay muted.
|
|
74
|
+
.wui-metric-tone-success .wui-metric-value {
|
|
75
|
+
color: var(--color-success-text);
|
|
76
|
+
}
|
|
77
|
+
.wui-metric-tone-warning .wui-metric-value {
|
|
78
|
+
color: var(--color-warning-text);
|
|
79
|
+
}
|
|
80
|
+
.wui-metric-tone-danger .wui-metric-value {
|
|
81
|
+
color: var(--color-danger-text);
|
|
82
|
+
}
|
|
83
|
+
.wui-metric-tone-info .wui-metric-value {
|
|
84
|
+
color: var(--color-info-text);
|
|
85
|
+
}
|
|
@@ -53,3 +53,37 @@
|
|
|
53
53
|
.wui-modal-submit-error {
|
|
54
54
|
margin-bottom: 16px;
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
// The remote drawer: the same shell and card, docked to the end edge at full height. Width
|
|
58
|
+
// comes from `--wui-modal-max-width` (sm 360, md 480, lg 640), set on the shell by the script.
|
|
59
|
+
.modal.wui-modal-shell.wui-remote-drawer {
|
|
60
|
+
justify-content: flex-end;
|
|
61
|
+
align-items: stretch;
|
|
62
|
+
padding: 0;
|
|
63
|
+
}
|
|
64
|
+
.modal.wui-remote-drawer .modal-dialog.wui-remote-drawer-dialog {
|
|
65
|
+
margin: 0;
|
|
66
|
+
max-width: none;
|
|
67
|
+
width: min(var(--wui-modal-max-width, 480px), 100%);
|
|
68
|
+
height: 100%;
|
|
69
|
+
display: flex;
|
|
70
|
+
pointer-events: none;
|
|
71
|
+
transform: translateX(100%);
|
|
72
|
+
transition: transform 0.25s var(--wui-ease-ui);
|
|
73
|
+
}
|
|
74
|
+
.modal.wui-remote-drawer.show .modal-dialog.wui-remote-drawer-dialog {
|
|
75
|
+
transform: none;
|
|
76
|
+
}
|
|
77
|
+
.modal.wui-remote-drawer .wui-remote-drawer-dialog .wui-modal {
|
|
78
|
+
pointer-events: auto;
|
|
79
|
+
max-width: none;
|
|
80
|
+
max-height: none;
|
|
81
|
+
height: 100%;
|
|
82
|
+
border-radius: 0;
|
|
83
|
+
box-shadow: var(--shadow-overlay-lg);
|
|
84
|
+
}
|
|
85
|
+
@media (prefers-reduced-motion: reduce) {
|
|
86
|
+
.modal.wui-remote-drawer .modal-dialog.wui-remote-drawer-dialog {
|
|
87
|
+
transition: none;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// ── Bar ── (`size="bar"`): one 40px header row. The breadcrumbs lead as an inline prefix, the
|
|
2
|
+
// title takes the spare width and ends in an ellipsis, and the row never wraps from md up.
|
|
3
|
+
// Below md the row wraps, so the actions drop under the title instead of overlapping it.
|
|
4
|
+
// Selectors name `.page-header.page-header-bar` because `.page-header-bar` alone is also the
|
|
5
|
+
// inner row of every other size.
|
|
6
|
+
.page-header.page-header-bar {
|
|
7
|
+
flex-wrap: nowrap;
|
|
8
|
+
gap: 8px;
|
|
9
|
+
min-height: 40px;
|
|
10
|
+
// The bar sits 4px above the first content block.
|
|
11
|
+
margin-bottom: 4px;
|
|
12
|
+
}
|
|
13
|
+
.page-header.page-header-bar .page-title {
|
|
14
|
+
flex: 1 1 0;
|
|
15
|
+
min-width: 0;
|
|
16
|
+
font-size: 20px;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
text-overflow: ellipsis;
|
|
19
|
+
white-space: nowrap;
|
|
20
|
+
}
|
|
21
|
+
.page-header.page-header-bar .page-status,
|
|
22
|
+
.page-header-bar-views,
|
|
23
|
+
.page-header-bar-meta,
|
|
24
|
+
.page-header-bar-tools,
|
|
25
|
+
.page-header.page-header-bar .page-actions {
|
|
26
|
+
flex: none;
|
|
27
|
+
}
|
|
28
|
+
.page-header-bar-crumbs {
|
|
29
|
+
flex: none;
|
|
30
|
+
font-size: 13px;
|
|
31
|
+
color: var(--color-text-muted);
|
|
32
|
+
white-space: nowrap;
|
|
33
|
+
}
|
|
34
|
+
.page-header-bar-views,
|
|
35
|
+
.page-header-bar-tools,
|
|
36
|
+
.page-header-bar-bulk {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: 8px;
|
|
40
|
+
}
|
|
41
|
+
.page-header-bar-bulk[hidden] {
|
|
42
|
+
display: none;
|
|
43
|
+
}
|
|
44
|
+
.page-header-bar-meta {
|
|
45
|
+
font-size: 13px;
|
|
46
|
+
color: var(--color-text-muted);
|
|
47
|
+
white-space: nowrap;
|
|
48
|
+
}
|
|
49
|
+
// A meta line set below the bar takes a row of its own under the bar, with no row gap, so the
|
|
50
|
+
// detail budget (72px to the first section) holds: 12 + 40 + 16 + 4.
|
|
51
|
+
.page-header.wui-page-header-meta-below {
|
|
52
|
+
flex-wrap: wrap;
|
|
53
|
+
row-gap: 0;
|
|
54
|
+
}
|
|
55
|
+
.page-header.wui-page-header-meta-below > .page-header-bar-meta {
|
|
56
|
+
flex: 0 0 100%;
|
|
57
|
+
line-height: 16px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@media (max-width: 767.98px) {
|
|
61
|
+
.page-header.page-header-bar {
|
|
62
|
+
flex-wrap: wrap;
|
|
63
|
+
}
|
|
64
|
+
// The title keeps its content width here, so a long row wraps the actions under it.
|
|
65
|
+
.page-header.page-header-bar .page-title {
|
|
66
|
+
flex: 0 1 auto;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// ── Score scale ──
|
|
2
|
+
// AppScoreScale / <app-score-scale>: one value per radio on a row of equal-width items that wraps on narrow screens.
|
|
3
|
+
// Each value is a label around a visually hidden native radio, so the arrow keys and the checked state come from the browser.
|
|
4
|
+
//
|
|
5
|
+
// Part of the wui-components layer, see _wui-components.scss for load order.
|
|
6
|
+
|
|
7
|
+
.wui-score-scale {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-wrap: wrap;
|
|
10
|
+
gap: 6px;
|
|
11
|
+
}
|
|
12
|
+
.wui-score-scale__item {
|
|
13
|
+
position: relative;
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
flex: 1 1 2.75rem;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
min-height: 44px;
|
|
19
|
+
margin: 0;
|
|
20
|
+
padding: 0 6px;
|
|
21
|
+
border: 1px solid var(--color-border);
|
|
22
|
+
border-radius: var(--wui-radius-sm);
|
|
23
|
+
background: var(--color-surface);
|
|
24
|
+
color: var(--color-text);
|
|
25
|
+
font-variant-numeric: tabular-nums;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
transition:
|
|
28
|
+
border-color 0.12s ease,
|
|
29
|
+
background 0.12s ease,
|
|
30
|
+
color 0.12s ease;
|
|
31
|
+
}
|
|
32
|
+
.wui-score-scale__item:not(.wui-score-scale__item--selected):hover {
|
|
33
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
34
|
+
}
|
|
35
|
+
.wui-score-scale__item--selected {
|
|
36
|
+
border-color: var(--color-primary);
|
|
37
|
+
background: var(--color-primary);
|
|
38
|
+
color: var(--color-on-primary-ink);
|
|
39
|
+
}
|
|
40
|
+
.wui-score-scale__item:has(input:focus-visible) {
|
|
41
|
+
outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
|
|
42
|
+
outline-offset: var(--wui-focus-ring-offset);
|
|
43
|
+
}
|
|
44
|
+
.wui-score-scale__item:has(input:disabled) {
|
|
45
|
+
opacity: 0.55;
|
|
46
|
+
cursor: not-allowed;
|
|
47
|
+
}
|
|
48
|
+
.wui-score-scale__ends {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-basis: 100%;
|
|
51
|
+
justify-content: space-between;
|
|
52
|
+
gap: 12px;
|
|
53
|
+
color: var(--color-text-muted);
|
|
54
|
+
font-size: 13px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media (prefers-reduced-motion: reduce) {
|
|
58
|
+
.wui-score-scale__item {
|
|
59
|
+
transition: none;
|
|
60
|
+
}
|
|
61
|
+
}
|
package/scss/wui/_shell.scss
CHANGED
|
@@ -734,9 +734,31 @@
|
|
|
734
734
|
}
|
|
735
735
|
|
|
736
736
|
.app-content {
|
|
737
|
+
--app-content-padding-top: 24px;
|
|
737
738
|
flex: 1 1 auto;
|
|
738
739
|
min-width: 0;
|
|
739
|
-
padding:
|
|
740
|
+
padding: var(--app-content-padding-top) 28px 24px;
|
|
741
|
+
}
|
|
742
|
+
[data-density="compact"] .app-content {
|
|
743
|
+
--app-content-padding-top: 12px;
|
|
744
|
+
}
|
|
745
|
+
// The skip link is the shell's first stop. It stays off screen until it takes focus, and being
|
|
746
|
+
// absolutely placed it takes no cell of the shell's grid.
|
|
747
|
+
.wui-skip-link {
|
|
748
|
+
position: absolute;
|
|
749
|
+
top: 8px;
|
|
750
|
+
left: 8px;
|
|
751
|
+
z-index: var(--wui-z-fixed);
|
|
752
|
+
padding: 8px 12px;
|
|
753
|
+
border-radius: var(--wui-radius);
|
|
754
|
+
background: var(--color-surface);
|
|
755
|
+
color: var(--color-text);
|
|
756
|
+
transform: translateY(-200%);
|
|
757
|
+
}
|
|
758
|
+
.wui-skip-link:focus-visible {
|
|
759
|
+
transform: none;
|
|
760
|
+
outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
|
|
761
|
+
outline-offset: var(--wui-focus-ring-offset);
|
|
740
762
|
}
|
|
741
763
|
.app-footer {
|
|
742
764
|
position: fixed;
|
|
@@ -816,7 +838,11 @@
|
|
|
816
838
|
padding-inline: 16px;
|
|
817
839
|
}
|
|
818
840
|
.app-shell.app-shell .app-content {
|
|
819
|
-
padding-
|
|
841
|
+
--app-content-padding-top: 16px;
|
|
842
|
+
padding-block: var(--app-content-padding-top) 32px;
|
|
843
|
+
}
|
|
844
|
+
.app-shell.app-shell[data-density="compact"] .app-content {
|
|
845
|
+
--app-content-padding-top: 12px;
|
|
820
846
|
}
|
|
821
847
|
}
|
|
822
848
|
// Phones: the topbar's icon controls meet the 44px touch floor (36px is a pointer size).
|
|
@@ -125,6 +125,10 @@
|
|
|
125
125
|
overflow-y: auto;
|
|
126
126
|
overscroll-behavior: contain;
|
|
127
127
|
}
|
|
128
|
+
.wui-split-items:focus-visible {
|
|
129
|
+
outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
|
|
130
|
+
outline-offset: var(--wui-focus-ring-inset);
|
|
131
|
+
}
|
|
128
132
|
|
|
129
133
|
.wui-split-item {
|
|
130
134
|
display: flex;
|
package/scss/wui/_toolbar.scss
CHANGED
|
@@ -136,3 +136,11 @@
|
|
|
136
136
|
transition: none;
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
+
|
|
140
|
+
// The filters slot of <app-table-toolbar> takes the spare width of its row from md up, so the filter
|
|
141
|
+
// bar and its floating panel sit beside the search and the actions rather than wrapping under them.
|
|
142
|
+
@media (min-width: 768px) {
|
|
143
|
+
.wui-table-toolbar-filters {
|
|
144
|
+
flex: 1 1 auto;
|
|
145
|
+
}
|
|
146
|
+
}
|