@webority/theme 0.76.0 → 0.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/purge-safelist.json +14 -1
- package/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_components.scss +8 -8
- package/scss/_wui-aliases.scss +4 -3
- package/scss/_wui-core.scss +88 -0
- package/scss/wui/_calendar.scss +5 -0
- package/scss/wui/_card.scss +30 -11
- package/scss/wui/_command-palette.scss +9 -0
- package/scss/wui/_controls.scss +1 -2
- package/scss/wui/_data.scss +7 -0
- package/scss/wui/_display.scss +18 -10
- package/scss/wui/_dock.scss +5 -0
- package/scss/wui/_file-viewer.scss +5 -0
- package/scss/wui/_floating.scss +7 -3
- package/scss/wui/_flow.scss +210 -1
- package/scss/wui/_image-crop.scss +5 -0
- package/scss/wui/_interaction.scss +28 -38
- package/scss/wui/_kanban.scss +5 -0
- package/scss/wui/_media.scss +3 -2
- package/scss/wui/_rich-text.scss +21 -0
- package/scss/wui/_shell.scss +6 -1
- package/scss/wui/_table-server.scss +5 -0
- package/scss/wui/_tree.scss +5 -0
- package/scss/wui/_utilities.scss +1 -0
- package/scss/wui/_workspace.scss +22 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.77.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
|
@@ -27,12 +27,11 @@
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.btn-primary {
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
--bs-btn-hover-
|
|
33
|
-
--bs-btn-
|
|
34
|
-
--bs-btn-active-
|
|
35
|
-
--bs-btn-active-border-color: var(--wui-primary-pressed);
|
|
30
|
+
// Hover and press read --color-primary-hover: the pressed primary in light and the lifted dark primary in dark (_wui-aliases.scss), so a rebrand and dark mode both track it.
|
|
31
|
+
--bs-btn-hover-bg: var(--color-primary-hover);
|
|
32
|
+
--bs-btn-hover-border-color: var(--color-primary-hover);
|
|
33
|
+
--bs-btn-active-bg: var(--color-primary-hover);
|
|
34
|
+
--bs-btn-active-border-color: var(--color-primary-hover);
|
|
36
35
|
// Bootstrap computes this label colour at SCSS compile time from the library's
|
|
37
36
|
// default $primary, so it can never react to a product overriding --wui-primary as a
|
|
38
37
|
// runtime CSS var. Routing it through the token makes a light-primary brand's
|
|
@@ -858,9 +857,10 @@
|
|
|
858
857
|
.card-header-actions + .card-close {
|
|
859
858
|
margin-left: 0.25rem;
|
|
860
859
|
}
|
|
860
|
+
// Hover tints the button danger, the same as the modal and drawer close buttons.
|
|
861
861
|
.card-close:hover {
|
|
862
|
-
background: var(--color-
|
|
863
|
-
color: var(--color-text);
|
|
862
|
+
background: var(--color-danger-soft);
|
|
863
|
+
color: var(--color-danger-text);
|
|
864
864
|
}
|
|
865
865
|
.card-close:focus-visible {
|
|
866
866
|
outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
|
package/scss/_wui-aliases.scss
CHANGED
|
@@ -25,9 +25,10 @@
|
|
|
25
25
|
--color-progress-track: var(--wui-progress-track);
|
|
26
26
|
// Label text inside a progress bar fill.
|
|
27
27
|
--color-progress-label: var(--wui-bg-light);
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
|
|
28
|
+
// Icon ink on a solid status fill (the filled timeline dots): white in both modes, by design
|
|
29
|
+
// choice. White on the bright success and warning fills sits under the 3:1 icon floor, so the
|
|
30
|
+
// contrast gate does not measure this pair.
|
|
31
|
+
--color-on-status-fill: #FFFFFF;
|
|
31
32
|
// Ink on a DEEP status fill (the -text tokens, used as a fill the way primary is): white in
|
|
32
33
|
// light mode, where those shades measure 5.5 to 6.2:1; near-black in dark mode, where -text is
|
|
33
34
|
// the bright fill.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// _wui-core.scss
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// The core widget layer: every partial of _wui-components.scss except the
|
|
5
|
+
// on-demand set, in the same order. A portal imports this globally, and each
|
|
6
|
+
// on-demand partial (wui/<name>) from the lazy route that renders it.
|
|
7
|
+
//
|
|
8
|
+
// scripts/check-theme-split.mjs keeps this file in step with _wui-components.scss.
|
|
9
|
+
// The split guards two things: a widget rule left in core defeats the split,
|
|
10
|
+
// and a core rule that styles an on-demand class silently changes when that
|
|
11
|
+
// layer is absent.
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
// Shared tone-pill mixin — used by .badge, .status-label, and .chip.
|
|
15
|
+
// Must be imported before components that use it.
|
|
16
|
+
@import "wui/tone-pill";
|
|
17
|
+
|
|
18
|
+
// The order below is LOAD-BEARING. A later partial may deliberately override an
|
|
19
|
+
// earlier one, so reordering these imports silently changes the cascade — and
|
|
20
|
+
// nothing in the build will complain. `pnpm check:theme` measures the rules that
|
|
21
|
+
// have to win against Bootstrap; the rest is guarded by review.
|
|
22
|
+
@import "wui/controls";
|
|
23
|
+
@import "wui/choice";
|
|
24
|
+
@import "wui/media";
|
|
25
|
+
@import "wui/avatar-picker";
|
|
26
|
+
@import "wui/navigation";
|
|
27
|
+
@import "wui/card";
|
|
28
|
+
@import "wui/data";
|
|
29
|
+
@import "wui/illustration";
|
|
30
|
+
@import "wui/table-server";
|
|
31
|
+
@import "wui/inline-edit";
|
|
32
|
+
@import "wui/mention-input";
|
|
33
|
+
@import "wui/comment-thread";
|
|
34
|
+
@import "wui/overlays";
|
|
35
|
+
@import "wui/drawer";
|
|
36
|
+
@import "wui/modal-remote";
|
|
37
|
+
@import "wui/utilities";
|
|
38
|
+
@import "wui/typography";
|
|
39
|
+
@import "wui/disclosure";
|
|
40
|
+
@import "wui/display";
|
|
41
|
+
@import "wui/flow";
|
|
42
|
+
// After `flow`: the pricing section extends its `.plan-grid` rules.
|
|
43
|
+
@import "wui/sections";
|
|
44
|
+
@import "wui/chart";
|
|
45
|
+
@import "wui/chart-card";
|
|
46
|
+
@import "wui/scrollbar";
|
|
47
|
+
@import "wui/sortable";
|
|
48
|
+
// Before `split-view`, which places the legend sheet inside the list column.
|
|
49
|
+
@import "wui/record-pager";
|
|
50
|
+
@import "wui/shortcuts";
|
|
51
|
+
@import "wui/split-view";
|
|
52
|
+
@import "wui/groups";
|
|
53
|
+
@import "wui/carousel";
|
|
54
|
+
@import "wui/onboarding-checklist";
|
|
55
|
+
@import "wui/rating";
|
|
56
|
+
@import "wui/score-scale";
|
|
57
|
+
@import "wui/relative-time";
|
|
58
|
+
@import "wui/resizable";
|
|
59
|
+
@import "wui/rolling-number";
|
|
60
|
+
@import "wui/show-more";
|
|
61
|
+
@import "wui/truncate";
|
|
62
|
+
@import "wui/page-header-bar";
|
|
63
|
+
@import "wui/filter-popover";
|
|
64
|
+
@import "wui/metric-strip";
|
|
65
|
+
@import "wui/due-date";
|
|
66
|
+
|
|
67
|
+
// Country flags are NOT part of this layer. The 245 `.wui-flag-*` rules are about 187KB raw
|
|
68
|
+
// (38KB Brotli) of inlined SVG, and most sites never render a phone field or country picker.
|
|
69
|
+
// A site that does imports `@webority/theme/scss/flags` once, after this file; without it
|
|
70
|
+
// <wui-phone> and <wui-country-select> show an empty grey box where the flag should be.
|
|
71
|
+
@import "wui/shell";
|
|
72
|
+
@import "wui/shell-parts";
|
|
73
|
+
@import "wui/consent-banner";
|
|
74
|
+
@import "wui/lightbox";
|
|
75
|
+
@import "wui/toolbar";
|
|
76
|
+
@import "wui/theme-toggle";
|
|
77
|
+
@import "wui/floating";
|
|
78
|
+
@import "wui/code-block";
|
|
79
|
+
@import "wui/context-menu";
|
|
80
|
+
@import "wui/menubar";
|
|
81
|
+
@import "wui/navigation-menu";
|
|
82
|
+
@import "wui/hover-card";
|
|
83
|
+
@import "wui/notification-center";
|
|
84
|
+
@import "wui/whats-new";
|
|
85
|
+
// After every component partial: the shared focus ring and pressed wash settle ties with their hover rules.
|
|
86
|
+
@import "wui/interaction";
|
|
87
|
+
// Last: restates the settled state of menus other partials style, so it must win.
|
|
88
|
+
@import "wui/menu-motion";
|
package/scss/wui/_calendar.scss
CHANGED
|
@@ -254,3 +254,8 @@ wui-calendar[data-view="week"] .wui-calendar-surface > * {
|
|
|
254
254
|
transition: none;
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
|
+
|
|
258
|
+
// The shared pressed wash of wui/interaction, restated here so it ships with this on-demand layer.
|
|
259
|
+
:is(.wui-calendar-nav-btn, .wui-calendar-today-btn, .wui-calendar-view-btn):active:not(:disabled, [aria-disabled="true"]) {
|
|
260
|
+
background-image: linear-gradient(var(--color-pressed-overlay), var(--color-pressed-overlay)) !important;
|
|
261
|
+
}
|
package/scss/wui/_card.scss
CHANGED
|
@@ -21,14 +21,24 @@
|
|
|
21
21
|
// Default card is borderless — a flat surface. The border-STYLE stays solid (Bootstrap's
|
|
22
22
|
// `border: var(--bs-card-border-width) solid …`), so a variant or a consumer class can
|
|
23
23
|
// bring the edge back just by giving it a width (set --bs-card-border-width).
|
|
24
|
-
|
|
24
|
+
// With a unit: the inner radius below subtracts it, and calc() drops a bare 0.
|
|
25
|
+
--bs-card-border-width: 0px;
|
|
26
|
+
// Bootstrap compiles the inner radius from $card-border-radius, which is smaller than the
|
|
27
|
+
// token radius the card actually draws. The header, footer and media round their corners
|
|
28
|
+
// with it, so their fill poked out over a bordered card's corners and the border looked cut.
|
|
29
|
+
// Derive it from the live radius and border width instead.
|
|
30
|
+
--bs-card-inner-border-radius: calc(var(--bs-card-border-radius) - var(--bs-card-border-width));
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
// A card header that carries a title on the left and controls on the right — the
|
|
28
34
|
// pattern nearly every panel header uses, so it belongs here rather than being
|
|
29
35
|
// re-flexed with utilities on each one.
|
|
36
|
+
// The header and footer share the card's own fill (a muted card stays muted to its top edge),
|
|
37
|
+
// and the header carries no rule of its own: Bootstrap's divider followed the card's border
|
|
38
|
+
// width, so only the elevated variant drew one.
|
|
30
39
|
.card-header {
|
|
31
|
-
--bs-card-cap-bg:
|
|
40
|
+
--bs-card-cap-bg: transparent;
|
|
41
|
+
border-bottom: 0;
|
|
32
42
|
display: flex;
|
|
33
43
|
align-items: center;
|
|
34
44
|
justify-content: space-between;
|
|
@@ -63,8 +73,9 @@
|
|
|
63
73
|
--bs-card-border-width: 1px;
|
|
64
74
|
border-color: var(--color-border-strong);
|
|
65
75
|
}
|
|
76
|
+
// Through the variable, so the inner radius and the border agree.
|
|
66
77
|
.card-outlined {
|
|
67
|
-
border-width: 2px;
|
|
78
|
+
--bs-card-border-width: 2px;
|
|
68
79
|
border-color: var(--color-border-strong);
|
|
69
80
|
}
|
|
70
81
|
.card-muted {
|
|
@@ -88,14 +99,16 @@
|
|
|
88
99
|
.card-accent.tone-info {
|
|
89
100
|
--card-accent: var(--color-info);
|
|
90
101
|
}
|
|
102
|
+
// The stripe is a left-only border on a layer with the card's own corners. CSS corner shaping
|
|
103
|
+
// curves its inner edge with the radius minus each side's width, so the stripe thins smoothly
|
|
104
|
+
// to nothing round both left corners. A straight bar clipped by the corners showed sliced ends.
|
|
91
105
|
.card-accent::before {
|
|
92
106
|
content: "";
|
|
93
107
|
position: absolute;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
background: var(--card-accent);
|
|
108
|
+
inset: 0;
|
|
109
|
+
border-left: 4px solid var(--card-accent);
|
|
110
|
+
border-radius: inherit;
|
|
111
|
+
pointer-events: none;
|
|
99
112
|
}
|
|
100
113
|
|
|
101
114
|
// ── Padding scale ── (header + body + footer). `md` is the Bootstrap default.
|
|
@@ -114,6 +127,12 @@
|
|
|
114
127
|
.card-pad-lg > .card-footer {
|
|
115
128
|
padding: 28px 32px;
|
|
116
129
|
}
|
|
130
|
+
// With no rule under the header, its bottom padding alone separates the title from the body;
|
|
131
|
+
// the body's own top padding on top of it left a double gap. After the scale, so every
|
|
132
|
+
// padding step keeps the single gap.
|
|
133
|
+
.card > .card-header + .card-body {
|
|
134
|
+
padding-top: 0;
|
|
135
|
+
}
|
|
117
136
|
|
|
118
137
|
// ── Constrained width ── (`maxWidth`): caps + centres a standalone card. Cards
|
|
119
138
|
// are fluid (fill their column) unless this is set.
|
|
@@ -124,7 +143,7 @@
|
|
|
124
143
|
|
|
125
144
|
// ── Footer ── (React `footer` / Razor <app-card-footer>): mirrors the header row.
|
|
126
145
|
.card-footer {
|
|
127
|
-
--bs-card-cap-bg:
|
|
146
|
+
--bs-card-cap-bg: transparent;
|
|
128
147
|
display: flex;
|
|
129
148
|
align-items: center;
|
|
130
149
|
gap: 12px;
|
|
@@ -164,8 +183,8 @@
|
|
|
164
183
|
}
|
|
165
184
|
// Media flush at the top follows the card's rounded corners.
|
|
166
185
|
.card > .card-media:first-child {
|
|
167
|
-
border-top-left-radius: var(--radius
|
|
168
|
-
border-top-right-radius: var(--radius
|
|
186
|
+
border-top-left-radius: var(--bs-card-inner-border-radius);
|
|
187
|
+
border-top-right-radius: var(--bs-card-inner-border-radius);
|
|
169
188
|
}
|
|
170
189
|
|
|
171
190
|
// ── Clickable card ── (`href` / `onClick`): the whole surface is one control.
|
|
@@ -180,9 +180,13 @@ button.wui-cmd-crumb:focus-visible {
|
|
|
180
180
|
// Keeps a little of the next row visible when the keyboard scrolls the active one in.
|
|
181
181
|
scroll-margin: 6px;
|
|
182
182
|
}
|
|
183
|
+
// Focus stays in the input, and the hover tint alone marks the keyboard's row at about 1.2:1, so
|
|
184
|
+
// an inset ring makes it plain; inset, because the list scrolls and would clip an outer ring.
|
|
183
185
|
.wui-cmd-item[aria-selected="true"] {
|
|
184
186
|
background: var(--color-primary-soft);
|
|
185
187
|
color: var(--color-primary-ink);
|
|
188
|
+
outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
|
|
189
|
+
outline-offset: var(--wui-focus-ring-inset);
|
|
186
190
|
}
|
|
187
191
|
.wui-cmd-item[aria-disabled="true"] {
|
|
188
192
|
color: var(--color-text-muted);
|
|
@@ -296,3 +300,8 @@ button.wui-cmd-crumb:focus-visible {
|
|
|
296
300
|
color 0.12s ease;
|
|
297
301
|
}
|
|
298
302
|
}
|
|
303
|
+
|
|
304
|
+
// The shared pressed wash of wui/interaction, restated here so it ships with this on-demand layer.
|
|
305
|
+
:is(.wui-cmd-back, button.wui-cmd-crumb):active:not(:disabled, [aria-disabled="true"]) {
|
|
306
|
+
background-image: linear-gradient(var(--color-pressed-overlay), var(--color-pressed-overlay)) !important;
|
|
307
|
+
}
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -1145,8 +1145,7 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
|
|
|
1145
1145
|
gap: 4px;
|
|
1146
1146
|
min-width: 0;
|
|
1147
1147
|
}
|
|
1148
|
-
// Inside the flex column
|
|
1149
|
-
.field > wui-rich-text,
|
|
1148
|
+
// Inside the flex column it sizes to its content, so it stretches to the field instead.
|
|
1150
1149
|
.field > wui-mention-input {
|
|
1151
1150
|
align-self: stretch;
|
|
1152
1151
|
width: 100%;
|
package/scss/wui/_data.scss
CHANGED
|
@@ -198,6 +198,7 @@
|
|
|
198
198
|
font-size: 12px;
|
|
199
199
|
font-weight: 500;
|
|
200
200
|
letter-spacing: 0.04em;
|
|
201
|
+
text-transform: uppercase;
|
|
201
202
|
white-space: nowrap;
|
|
202
203
|
vertical-align: middle;
|
|
203
204
|
transition:
|
|
@@ -562,6 +563,10 @@
|
|
|
562
563
|
white-space: nowrap;
|
|
563
564
|
flex: none;
|
|
564
565
|
}
|
|
566
|
+
// 3px padding and a 1px border on each side, so the strip reaches the small control height beside the other filter controls.
|
|
567
|
+
.wui-filter-bar .segmented-sm {
|
|
568
|
+
--segmented-option-h: calc(var(--wui-control-height-sm) - 8px);
|
|
569
|
+
}
|
|
565
570
|
// Facets folded behind the Filters button stack in one panel, each at the panel's width. Both surfaces
|
|
566
571
|
// draw it as the same <details>; in the Razor bar it sits inside the form, so the controls still submit.
|
|
567
572
|
.wui-filter-more-panel {
|
|
@@ -1082,7 +1087,9 @@
|
|
|
1082
1087
|
align-items: flex-end;
|
|
1083
1088
|
}
|
|
1084
1089
|
// The leading cell is the card's title line: full width, no label.
|
|
1090
|
+
// Block, not the flex row of the other cells, so a name with a sub-line stacks instead of sitting beside it.
|
|
1085
1091
|
.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) {
|
|
1092
|
+
display: block;
|
|
1086
1093
|
justify-content: flex-start;
|
|
1087
1094
|
padding: 0 32px 6px 0;
|
|
1088
1095
|
text-align: left;
|
package/scss/wui/_display.scss
CHANGED
|
@@ -175,21 +175,27 @@
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
// Spotlight — an editorial feature tile: the label sits top-left with the icon chip pinned
|
|
178
|
-
// top-right, the value is set in the
|
|
179
|
-
//
|
|
180
|
-
//
|
|
178
|
+
// top-right, the value is set large in the product's own font, and an optional caption reads
|
|
179
|
+
// under the number. For the single showcase stat on a marketing/portal surface where the
|
|
180
|
+
// number is the hero.
|
|
181
181
|
.kpi.kpi-spotlight {
|
|
182
182
|
display: grid;
|
|
183
183
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
184
|
+
// Rows pack at the top: a tile stretched to a taller neighbour's height puts the spare
|
|
185
|
+
// space below the caption, so the label and icon row stays level across tiles.
|
|
186
|
+
align-content: start;
|
|
184
187
|
column-gap: 16px;
|
|
185
188
|
padding: 22px 24px;
|
|
186
|
-
--wui-kpi-value-size:
|
|
189
|
+
--wui-kpi-value-size: 36px;
|
|
187
190
|
}
|
|
188
191
|
.kpi.kpi-spotlight .kpi-icon {
|
|
189
192
|
grid-column: 2;
|
|
190
193
|
grid-row: 1;
|
|
191
194
|
align-self: start;
|
|
192
195
|
justify-self: end;
|
|
196
|
+
// A larger chip than the plain tile's 40px, to match the hero-sized value; the glyph keeps its size.
|
|
197
|
+
width: 48px;
|
|
198
|
+
height: 48px;
|
|
193
199
|
margin-bottom: 0;
|
|
194
200
|
}
|
|
195
201
|
.kpi.kpi-spotlight .kpi-label,
|
|
@@ -206,12 +212,9 @@
|
|
|
206
212
|
}
|
|
207
213
|
.kpi.kpi-spotlight .kpi-value {
|
|
208
214
|
margin-top: 14px;
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
// never read as serif. A product that wants its own face overrides this one selector.
|
|
212
|
-
// Size comes from --wui-kpi-value-size (44px default, set on the spotlight root above), so a
|
|
215
|
+
// No font-family: the value inherits the product's own font like every other tile.
|
|
216
|
+
// Size comes from --wui-kpi-value-size (36px default, set on the spotlight root above), so a
|
|
213
217
|
// custom `valueSize` (px) can tame a too-large headline.
|
|
214
|
-
font-family: var(--wui-font-display);
|
|
215
218
|
line-height: 1;
|
|
216
219
|
font-weight: 600;
|
|
217
220
|
}
|
|
@@ -357,6 +360,11 @@ a.info-row:hover .info-value {
|
|
|
357
360
|
border-bottom: 1px solid var(--color-border);
|
|
358
361
|
grid-column: span var(--descs-span, 1);
|
|
359
362
|
}
|
|
363
|
+
@for $n from 1 through 12 {
|
|
364
|
+
.wui-descs-span-#{$n} {
|
|
365
|
+
--descs-span: #{$n};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
360
368
|
.descs-label {
|
|
361
369
|
font-size: 12px;
|
|
362
370
|
font-weight: 600;
|
|
@@ -1312,7 +1320,7 @@ a.info-row:hover .info-value {
|
|
|
1312
1320
|
position: absolute;
|
|
1313
1321
|
left: 0;
|
|
1314
1322
|
right: 0;
|
|
1315
|
-
bottom:
|
|
1323
|
+
bottom: 1px;
|
|
1316
1324
|
height: 1px;
|
|
1317
1325
|
background: currentColor;
|
|
1318
1326
|
transform: scaleX(0);
|
package/scss/wui/_dock.scss
CHANGED
|
@@ -100,3 +100,8 @@ wui-dock {
|
|
|
100
100
|
transition: none;
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
|
|
104
|
+
// The shared pressed wash of wui/interaction, restated here so it ships with this on-demand layer.
|
|
105
|
+
.dock-item:active:not(:disabled, [aria-disabled="true"]) {
|
|
106
|
+
background-image: linear-gradient(var(--color-pressed-overlay), var(--color-pressed-overlay)) !important;
|
|
107
|
+
}
|
|
@@ -500,3 +500,8 @@ wui-file-viewer:fullscreen {
|
|
|
500
500
|
min-width: 48px;
|
|
501
501
|
}
|
|
502
502
|
|
|
503
|
+
// The shared pressed wash of wui/interaction, restated here so it ships with this on-demand layer.
|
|
504
|
+
.wui-file-viewer-zoom:active:not(:disabled, [aria-disabled="true"]) {
|
|
505
|
+
background-image: linear-gradient(var(--color-pressed-overlay), var(--color-pressed-overlay)) !important;
|
|
506
|
+
}
|
|
507
|
+
|
package/scss/wui/_floating.scss
CHANGED
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
flex-direction: column-reverse;
|
|
15
15
|
gap: 12px;
|
|
16
16
|
margin: 1rem;
|
|
17
|
+
// Clear the home indicator and the notch on a phone.
|
|
18
|
+
margin-bottom: max(1rem, env(safe-area-inset-bottom));
|
|
19
|
+
margin-left: max(1rem, env(safe-area-inset-left));
|
|
20
|
+
margin-right: max(1rem, env(safe-area-inset-right));
|
|
17
21
|
// The container spans only its items, but a gap between them must not swallow clicks on the page.
|
|
18
22
|
pointer-events: none;
|
|
19
23
|
}
|
|
@@ -53,9 +57,9 @@
|
|
|
53
57
|
// Quieter than the primary circle: a light brand tint, the brand icon and a thin brand border.
|
|
54
58
|
.wui-floating-btn--secondary {
|
|
55
59
|
--wui-floating-size: 40px;
|
|
56
|
-
--bs-btn-border-color: var(--
|
|
57
|
-
--bs-btn-hover-border-color: var(--
|
|
58
|
-
--bs-btn-active-border-color: var(--
|
|
60
|
+
--bs-btn-border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
61
|
+
--bs-btn-hover-border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
62
|
+
--bs-btn-active-border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
59
63
|
}
|
|
60
64
|
@media (prefers-reduced-motion: reduce) {
|
|
61
65
|
.wui-floating-btn {
|
package/scss/wui/_flow.scss
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// .stepper — compact: numbered circle above a label, line behind.
|
|
10
10
|
// .stepper-journey — wide: 40px dot beside a label + title, for a page masthead.
|
|
11
11
|
// .stepper-icons — large connected icon badges with a "STEP n" eyebrow.
|
|
12
|
+
// .wui-step-tiles — a row of tiles: rounded icon square, label and a state line, chevrons between.
|
|
12
13
|
//
|
|
13
14
|
// Fully themable through a small set of `--wui-step-*` custom properties — set them on
|
|
14
15
|
// the stepper (or the wizard host, or any ancestor: they inherit), or flip the whole
|
|
@@ -517,11 +518,160 @@ button.step.done:focus-visible .step-circle {
|
|
|
517
518
|
}
|
|
518
519
|
}
|
|
519
520
|
|
|
521
|
+
// .wui-step-tiles — each step is a list item holding a rounded icon square with the label and a
|
|
522
|
+
// state line (Done, Current, To do) beside it; chevrons join the tiles. Like every variant it is one
|
|
523
|
+
// brand-primary accent: the current tile is solid accent and done tiles the soft tint, so a tone
|
|
524
|
+
// class recolours the current one and a done-tone class the finished ones.
|
|
525
|
+
// ----------------------------------------------------------------------------
|
|
526
|
+
.wui-step-tiles {
|
|
527
|
+
padding: 0 16px;
|
|
528
|
+
}
|
|
529
|
+
.wui-step-tiles > ol {
|
|
530
|
+
display: flex;
|
|
531
|
+
align-items: center;
|
|
532
|
+
gap: 6px;
|
|
533
|
+
width: 100%;
|
|
534
|
+
margin: 0;
|
|
535
|
+
padding: 0;
|
|
536
|
+
list-style: none;
|
|
537
|
+
}
|
|
538
|
+
.wui-step-tile {
|
|
539
|
+
display: flex;
|
|
540
|
+
align-items: center;
|
|
541
|
+
min-width: 0;
|
|
542
|
+
}
|
|
543
|
+
// Every tile after the first takes an equal share of the free width and centres its chevron in it.
|
|
544
|
+
.wui-step-tile + .wui-step-tile {
|
|
545
|
+
flex: 1 1 auto;
|
|
546
|
+
padding-inline-start: 12px;
|
|
547
|
+
}
|
|
548
|
+
.wui-step-tile + .wui-step-tile > .wui-step-tile-body {
|
|
549
|
+
margin-inline-start: 12px;
|
|
550
|
+
}
|
|
551
|
+
.wui-step-tile + .wui-step-tile::before {
|
|
552
|
+
content: "";
|
|
553
|
+
flex: none;
|
|
554
|
+
width: 7px;
|
|
555
|
+
height: 7px;
|
|
556
|
+
margin-inline: auto;
|
|
557
|
+
border-top: 2px solid var(--wui-step-connector, var(--color-border-strong));
|
|
558
|
+
border-right: 2px solid var(--wui-step-connector, var(--color-border-strong));
|
|
559
|
+
transform: rotate(45deg);
|
|
560
|
+
}
|
|
561
|
+
.wui-step-tile-body {
|
|
562
|
+
display: flex;
|
|
563
|
+
align-items: center;
|
|
564
|
+
gap: 10px;
|
|
565
|
+
min-width: 0;
|
|
566
|
+
padding: 4px 8px 4px 0;
|
|
567
|
+
border: 0;
|
|
568
|
+
border-radius: var(--radius);
|
|
569
|
+
background: none;
|
|
570
|
+
font: inherit;
|
|
571
|
+
text-align: start;
|
|
572
|
+
color: inherit;
|
|
573
|
+
}
|
|
574
|
+
button.wui-step-tile-body {
|
|
575
|
+
cursor: pointer;
|
|
576
|
+
}
|
|
577
|
+
button.wui-step-tile-body:focus-visible {
|
|
578
|
+
outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
|
|
579
|
+
outline-offset: var(--wui-focus-ring-offset);
|
|
580
|
+
}
|
|
581
|
+
.wui-step-tiles .step-circle {
|
|
582
|
+
width: var(--wui-step-tile-size, 34px);
|
|
583
|
+
height: var(--wui-step-tile-size, 34px);
|
|
584
|
+
border: 0;
|
|
585
|
+
border-radius: var(--radius);
|
|
586
|
+
background: var(--color-bg-muted);
|
|
587
|
+
color: var(--color-text-subtle);
|
|
588
|
+
font-size: var(--wui-step-number-font, 13px);
|
|
589
|
+
}
|
|
590
|
+
.wui-step-tile.active .step-circle {
|
|
591
|
+
background: var(--wui-step-accent, var(--color-primary-ink));
|
|
592
|
+
color: var(--wui-step-accent-fg, var(--color-on-primary-ink));
|
|
593
|
+
}
|
|
594
|
+
.wui-step-tile.done .step-circle {
|
|
595
|
+
background: var(--wui-step-done-soft, var(--wui-step-soft, var(--color-primary-soft)));
|
|
596
|
+
color: var(--wui-step-done-soft-fg, var(--wui-step-soft-fg, var(--color-primary-ink)));
|
|
597
|
+
}
|
|
598
|
+
.wui-step-tile.error .step-circle {
|
|
599
|
+
background: var(--color-danger-soft);
|
|
600
|
+
color: var(--color-danger-text);
|
|
601
|
+
}
|
|
602
|
+
.wui-step-tile.skipped .step-circle {
|
|
603
|
+
border: 1px dashed var(--color-border-strong);
|
|
604
|
+
color: var(--color-text-muted);
|
|
605
|
+
}
|
|
606
|
+
.wui-step-tiles .step-text {
|
|
607
|
+
display: flex;
|
|
608
|
+
flex-direction: column;
|
|
609
|
+
gap: 1px;
|
|
610
|
+
min-width: 0;
|
|
611
|
+
}
|
|
612
|
+
.wui-step-tiles .step-label {
|
|
613
|
+
max-width: none;
|
|
614
|
+
font-size: var(--wui-step-label-font, 13px);
|
|
615
|
+
font-weight: 600;
|
|
616
|
+
line-height: 1.25;
|
|
617
|
+
color: var(--color-text);
|
|
618
|
+
white-space: nowrap;
|
|
619
|
+
}
|
|
620
|
+
.wui-step-tile.active .step-label {
|
|
621
|
+
color: var(--wui-step-accent, var(--color-primary-ink));
|
|
622
|
+
}
|
|
623
|
+
.wui-step-tile.error .step-label {
|
|
624
|
+
color: var(--color-danger-text);
|
|
625
|
+
}
|
|
626
|
+
button.wui-step-tile-body:hover .step-label {
|
|
627
|
+
color: var(--wui-step-accent, var(--color-primary-ink));
|
|
628
|
+
}
|
|
629
|
+
.wui-step-tiles .wui-step-tile-state {
|
|
630
|
+
font-size: 12px;
|
|
631
|
+
line-height: 1.3;
|
|
632
|
+
color: var(--color-text-muted);
|
|
633
|
+
white-space: nowrap;
|
|
634
|
+
}
|
|
635
|
+
// A vertical wizard, or a phone, stacks the tiles into a list; the chevrons only make sense in a row.
|
|
636
|
+
.wui-step-tiles.stepper-vertical > ol {
|
|
637
|
+
flex-direction: column;
|
|
638
|
+
align-items: flex-start;
|
|
639
|
+
gap: 12px;
|
|
640
|
+
}
|
|
641
|
+
.wui-step-tiles.stepper-vertical .wui-step-tile + .wui-step-tile {
|
|
642
|
+
flex: none;
|
|
643
|
+
padding-inline-start: 0;
|
|
644
|
+
}
|
|
645
|
+
.wui-step-tiles.stepper-vertical .wui-step-tile + .wui-step-tile > .wui-step-tile-body {
|
|
646
|
+
margin-inline-start: 0;
|
|
647
|
+
}
|
|
648
|
+
.wui-step-tiles.stepper-vertical .wui-step-tile + .wui-step-tile::before {
|
|
649
|
+
content: none;
|
|
650
|
+
}
|
|
651
|
+
@include media-breakpoint-down(md) {
|
|
652
|
+
.wui-step-tiles.wui-step-tiles > ol {
|
|
653
|
+
flex-direction: column;
|
|
654
|
+
align-items: flex-start;
|
|
655
|
+
gap: 12px;
|
|
656
|
+
}
|
|
657
|
+
.wui-step-tiles.wui-step-tiles .wui-step-tile + .wui-step-tile {
|
|
658
|
+
flex: none;
|
|
659
|
+
padding-inline-start: 0;
|
|
660
|
+
}
|
|
661
|
+
.wui-step-tiles.wui-step-tiles .wui-step-tile + .wui-step-tile > .wui-step-tile-body {
|
|
662
|
+
margin-inline-start: 0;
|
|
663
|
+
}
|
|
664
|
+
.wui-step-tiles.wui-step-tiles .wui-step-tile + .wui-step-tile::before {
|
|
665
|
+
content: none;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
520
669
|
// No connectors: the wizard's hide-connectors option drops the line between steps on every
|
|
521
670
|
// variant and orientation. Placed after the variants so it outranks their equal-specificity
|
|
522
671
|
// rails; the journey bar's .step-line siblings are simply not emitted.
|
|
523
672
|
.wui-stepper-no-connectors .step + .step::before,
|
|
524
|
-
.wui-stepper-no-connectors .step:not(:last-child)::after
|
|
673
|
+
.wui-stepper-no-connectors .step:not(:last-child)::after,
|
|
674
|
+
.wui-stepper-no-connectors .wui-step-tile + .wui-step-tile::before {
|
|
525
675
|
content: none;
|
|
526
676
|
}
|
|
527
677
|
|
|
@@ -999,6 +1149,11 @@ button.step.done:focus-visible .step-circle {
|
|
|
999
1149
|
// width here and lines up with the fields beside it. A .date-w-* class on the field still wins.
|
|
1000
1150
|
--wui-datepicker-width: 100%;
|
|
1001
1151
|
}
|
|
1152
|
+
@for $n from 1 through 12 {
|
|
1153
|
+
.wui-form-cols-#{$n} {
|
|
1154
|
+
--form-cols: #{$n};
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1002
1157
|
// Below md, collapse to 1 column.
|
|
1003
1158
|
@include media-breakpoint-down(md) {
|
|
1004
1159
|
.form-row-grid.form-row-grid {
|
|
@@ -1056,6 +1211,60 @@ wui-wizard-step[hidden] {
|
|
|
1056
1211
|
.wizard-stepper {
|
|
1057
1212
|
margin-bottom: 24px;
|
|
1058
1213
|
}
|
|
1214
|
+
// The optional bar above the stepper (`top-bar`): the title at the start, then the step count and the
|
|
1215
|
+
// close button at the end.
|
|
1216
|
+
.wui-wizard-topbar {
|
|
1217
|
+
display: flex;
|
|
1218
|
+
align-items: center;
|
|
1219
|
+
gap: 12px;
|
|
1220
|
+
box-sizing: border-box;
|
|
1221
|
+
height: 56px;
|
|
1222
|
+
padding: 0 16px;
|
|
1223
|
+
border-bottom: 1px solid var(--color-border);
|
|
1224
|
+
}
|
|
1225
|
+
.wui-wizard-topbar + .wizard-stepper {
|
|
1226
|
+
margin-top: 16px;
|
|
1227
|
+
}
|
|
1228
|
+
.wui-wizard-topbar-title {
|
|
1229
|
+
flex: 1 1 auto;
|
|
1230
|
+
min-width: 0;
|
|
1231
|
+
overflow: hidden;
|
|
1232
|
+
font-size: 16px;
|
|
1233
|
+
font-weight: 600;
|
|
1234
|
+
text-overflow: ellipsis;
|
|
1235
|
+
white-space: nowrap;
|
|
1236
|
+
color: var(--color-text);
|
|
1237
|
+
}
|
|
1238
|
+
.wui-wizard-topbar-count {
|
|
1239
|
+
flex: none;
|
|
1240
|
+
font-size: 13px;
|
|
1241
|
+
color: var(--color-text-subtle);
|
|
1242
|
+
white-space: nowrap;
|
|
1243
|
+
}
|
|
1244
|
+
.wui-wizard-topbar-close {
|
|
1245
|
+
display: inline-grid;
|
|
1246
|
+
flex: none;
|
|
1247
|
+
place-items: center;
|
|
1248
|
+
width: 32px;
|
|
1249
|
+
height: 32px;
|
|
1250
|
+
padding: 0;
|
|
1251
|
+
border: 0;
|
|
1252
|
+
border-radius: var(--radius);
|
|
1253
|
+
background: transparent;
|
|
1254
|
+
color: var(--color-text-muted);
|
|
1255
|
+
cursor: pointer;
|
|
1256
|
+
transition:
|
|
1257
|
+
background-color 0.12s ease,
|
|
1258
|
+
color 0.12s ease;
|
|
1259
|
+
}
|
|
1260
|
+
.wui-wizard-topbar-close:hover {
|
|
1261
|
+
background: var(--color-bg-muted);
|
|
1262
|
+
color: var(--color-text);
|
|
1263
|
+
}
|
|
1264
|
+
.wui-wizard-topbar-close:focus-visible {
|
|
1265
|
+
outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
|
|
1266
|
+
outline-offset: var(--wui-focus-ring-offset);
|
|
1267
|
+
}
|
|
1059
1268
|
// A skipped step reads as "passed over", not "achieved": a dashed, muted circle so it
|
|
1060
1269
|
// is visibly distinct from a ticked (done) step.
|
|
1061
1270
|
.step.skipped .step-circle {
|
|
@@ -213,3 +213,8 @@ wui-image-crop {
|
|
|
213
213
|
min-height: 44px;
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
|
+
|
|
217
|
+
// The shared pressed wash of wui/interaction, restated here so it ships with this on-demand layer.
|
|
218
|
+
:is(.wui-crop-toolbtn, .wui-crop-zoombtn):active:not(:disabled, [aria-disabled="true"]) {
|
|
219
|
+
background-image: linear-gradient(var(--color-pressed-overlay), var(--color-pressed-overlay)) !important;
|
|
220
|
+
}
|