@webority/theme 0.9.9 → 0.9.11
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-components.scss +1 -0
- package/scss/wui/_data.scss +14 -36
- package/scss/wui/_feedback.scss +259 -0
- package/scss/wui/_utilities.scss +80 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.11",
|
|
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/_data.scss
CHANGED
|
@@ -112,40 +112,17 @@
|
|
|
112
112
|
.table-ledger tbody tr:hover td.wui-table-empty:has(> .tbl-empty) {
|
|
113
113
|
background: transparent;
|
|
114
114
|
}
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
// scrolls its columns.
|
|
127
|
-
// Keyed on the empty CELL (`td.wui-table-empty`, the one element unique to the empty
|
|
128
|
-
// state on both surfaces) with CHILD combinators, NOT a loose descendant `:has(.tbl-empty)`.
|
|
129
|
-
// The child chain matches only a table's OWN empty body — so an expandable row whose
|
|
130
|
-
// nested table happens to be empty can never force the outer, data-bearing table into
|
|
131
|
-
// this layout. A data table (no empty cell of its own) is untouched and still scrolls.
|
|
132
|
-
.table-wrap:has(> table > tbody > tr > td.wui-table-empty) {
|
|
133
|
-
overflow-x: hidden;
|
|
134
|
-
}
|
|
135
|
-
.table:has(> tbody > tr > td.wui-table-empty) {
|
|
136
|
-
width: 100%;
|
|
137
|
-
min-width: 0;
|
|
138
|
-
table-layout: fixed;
|
|
139
|
-
}
|
|
140
|
-
// Under the fixed layout above the columns are narrow, so a long header label would
|
|
141
|
-
// spill out of its cell and back into horizontal overflow — the very thing we just
|
|
142
|
-
// removed. Clip each header to its column (ellipsis) in the empty state; short labels
|
|
143
|
-
// are unaffected, and the data table keeps its natural header sizing.
|
|
144
|
-
.table:has(> tbody > tr > td.wui-table-empty) > thead th {
|
|
145
|
-
overflow: hidden;
|
|
146
|
-
white-space: nowrap;
|
|
147
|
-
text-overflow: ellipsis;
|
|
148
|
-
}
|
|
115
|
+
// Empty state sizes exactly like a POPULATED table — a standard responsive table. It fills
|
|
116
|
+
// its container (base `.table { width: 100% }`), so the columns spread to use the width; when
|
|
117
|
+
// the container is too narrow the columns compress as far as they can, and once even their
|
|
118
|
+
// content can't fit, `.table-wrap` (base `overflow-x: auto`) scrolls horizontally rather than
|
|
119
|
+
// clipping — every header label reads in full, never ellipsis-truncated into "REFEREN…".
|
|
120
|
+
//
|
|
121
|
+
// So the empty state carries NO special width rule of its own: the base table layout already
|
|
122
|
+
// gives responsive-then-scroll. We deliberately do NOT pin the message to the viewport with
|
|
123
|
+
// `container-type` + `100cqw` (it forced the colSpan cell, and the whole table, up to the panel
|
|
124
|
+
// width), and we do NOT shrink the table to `width: auto` (that collapsed the columns). Only the
|
|
125
|
+
// cell reset above (no padding/border/hover) is empty-specific.
|
|
149
126
|
|
|
150
127
|
// ── Table (base) ──
|
|
151
128
|
// The shared data-table look, on the plain `.table` class Bootstrap already
|
|
@@ -300,8 +277,9 @@
|
|
|
300
277
|
// ITEM (a very common product layout), forcing the whole table into a tiny horizontal
|
|
301
278
|
// scroll. It was briefly added for a `100cqw` empty-state pin; the collapse is far worse
|
|
302
279
|
// than the empty message not being perfectly centred on a narrow scrolling table, so the
|
|
303
|
-
// pin was removed.
|
|
304
|
-
//
|
|
280
|
+
// pin was removed. The empty state now just sizes like a normal table (see the empty-state
|
|
281
|
+
// block above) — if it ever needs viewport-relative sizing again, do it without turning
|
|
282
|
+
// the scroll container into a query container.
|
|
305
283
|
}
|
|
306
284
|
.table-ledger {
|
|
307
285
|
border-spacing: 0;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// ── In-product feedback ──
|
|
2
|
+
// AppFeedback / <app-feedback> / <wui-feedback> — a floating trigger pinned to a page
|
|
3
|
+
// corner that opens a small panel with a message box. The element authors the markup and
|
|
4
|
+
// owns the states (`open` / `busy` on the host, `hidden` on the panel, error and thank-you
|
|
5
|
+
// blocks); everything visual lives here.
|
|
6
|
+
//
|
|
7
|
+
// Part of the wui-components layer — see _wui-components.scss for load order.
|
|
8
|
+
|
|
9
|
+
wui-feedback {
|
|
10
|
+
position: fixed;
|
|
11
|
+
bottom: 20px;
|
|
12
|
+
z-index: var(--wui-z-fixed);
|
|
13
|
+
display: block;
|
|
14
|
+
}
|
|
15
|
+
wui-feedback,
|
|
16
|
+
wui-feedback[position="bottom-right"] {
|
|
17
|
+
right: 20px;
|
|
18
|
+
left: auto;
|
|
19
|
+
}
|
|
20
|
+
wui-feedback[position="bottom-left"] {
|
|
21
|
+
left: 20px;
|
|
22
|
+
right: auto;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ── Trigger ──
|
|
26
|
+
.feedback-trigger {
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: 8px;
|
|
30
|
+
height: var(--wui-control-height-sm);
|
|
31
|
+
padding: 0 16px;
|
|
32
|
+
border: none;
|
|
33
|
+
border-radius: 999px;
|
|
34
|
+
background: var(--color-primary);
|
|
35
|
+
color: var(--color-primary-text);
|
|
36
|
+
font-size: 14px;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
line-height: 1;
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
box-shadow: var(--wui-shadow-overlay);
|
|
41
|
+
transition:
|
|
42
|
+
background-color 160ms var(--wui-ease-ui),
|
|
43
|
+
transform 160ms var(--wui-ease-ui);
|
|
44
|
+
}
|
|
45
|
+
.feedback-trigger:hover {
|
|
46
|
+
background: var(--color-primary-hover);
|
|
47
|
+
}
|
|
48
|
+
.feedback-trigger:focus-visible {
|
|
49
|
+
outline: 2px solid var(--color-primary);
|
|
50
|
+
outline-offset: 2px;
|
|
51
|
+
}
|
|
52
|
+
.feedback-trigger-icon {
|
|
53
|
+
display: grid;
|
|
54
|
+
place-items: center;
|
|
55
|
+
line-height: 0;
|
|
56
|
+
}
|
|
57
|
+
.feedback-trigger-label {
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Panel ──
|
|
62
|
+
// Anchored to the same corner as the trigger, opening upward — the trigger sits at the
|
|
63
|
+
// bottom of the viewport, so there is only ever room above it.
|
|
64
|
+
.feedback-panel {
|
|
65
|
+
position: absolute;
|
|
66
|
+
bottom: calc(100% + 12px);
|
|
67
|
+
width: 340px;
|
|
68
|
+
max-width: calc(100vw - 40px);
|
|
69
|
+
padding: 16px;
|
|
70
|
+
background: var(--color-surface);
|
|
71
|
+
border: 1px solid var(--color-border);
|
|
72
|
+
border-radius: var(--wui-radius-lg);
|
|
73
|
+
box-shadow: var(--wui-shadow-overlay-lg);
|
|
74
|
+
color: var(--color-text);
|
|
75
|
+
text-align: start;
|
|
76
|
+
}
|
|
77
|
+
wui-feedback[position="bottom-right"] .feedback-panel {
|
|
78
|
+
right: 0;
|
|
79
|
+
}
|
|
80
|
+
wui-feedback[position="bottom-left"] .feedback-panel {
|
|
81
|
+
left: 0;
|
|
82
|
+
}
|
|
83
|
+
// `hidden` on a custom element's child is honoured, but a later display rule would win —
|
|
84
|
+
// state it explicitly so the panel cannot be left visible by cascade order.
|
|
85
|
+
.feedback-panel[hidden] {
|
|
86
|
+
display: none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.feedback-head {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: flex-start;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
gap: 12px;
|
|
94
|
+
margin-bottom: 12px;
|
|
95
|
+
}
|
|
96
|
+
.feedback-heading {
|
|
97
|
+
margin: 0;
|
|
98
|
+
font-size: 15px;
|
|
99
|
+
font-weight: 600;
|
|
100
|
+
line-height: 1.3;
|
|
101
|
+
color: var(--color-text);
|
|
102
|
+
}
|
|
103
|
+
.feedback-close {
|
|
104
|
+
display: grid;
|
|
105
|
+
place-items: center;
|
|
106
|
+
width: 28px;
|
|
107
|
+
height: 28px;
|
|
108
|
+
flex: 0 0 auto;
|
|
109
|
+
padding: 0;
|
|
110
|
+
border: none;
|
|
111
|
+
border-radius: var(--wui-radius-sm);
|
|
112
|
+
background: transparent;
|
|
113
|
+
color: var(--color-text-muted);
|
|
114
|
+
cursor: pointer;
|
|
115
|
+
}
|
|
116
|
+
.feedback-close:hover {
|
|
117
|
+
background: var(--color-bg-muted);
|
|
118
|
+
color: var(--color-text);
|
|
119
|
+
}
|
|
120
|
+
.feedback-close:focus-visible {
|
|
121
|
+
outline: 2px solid var(--color-primary);
|
|
122
|
+
outline-offset: 1px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.feedback-body[hidden],
|
|
126
|
+
.feedback-actions[hidden],
|
|
127
|
+
.feedback-thanks[hidden] {
|
|
128
|
+
display: none;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ── Fields ──
|
|
132
|
+
.feedback-field {
|
|
133
|
+
display: block;
|
|
134
|
+
margin-bottom: 12px;
|
|
135
|
+
}
|
|
136
|
+
.feedback-field-label {
|
|
137
|
+
display: block;
|
|
138
|
+
margin-bottom: 6px;
|
|
139
|
+
font-size: 13px;
|
|
140
|
+
font-weight: 500;
|
|
141
|
+
color: var(--color-text-muted);
|
|
142
|
+
}
|
|
143
|
+
.feedback-message,
|
|
144
|
+
.feedback-email {
|
|
145
|
+
display: block;
|
|
146
|
+
width: 100%;
|
|
147
|
+
padding: 10px 12px;
|
|
148
|
+
background: var(--color-surface);
|
|
149
|
+
border: 1px solid var(--color-border);
|
|
150
|
+
border-radius: var(--wui-radius-sm);
|
|
151
|
+
color: var(--color-text);
|
|
152
|
+
font-family: inherit;
|
|
153
|
+
font-size: 14px;
|
|
154
|
+
line-height: 1.45;
|
|
155
|
+
}
|
|
156
|
+
.feedback-message {
|
|
157
|
+
min-height: 96px;
|
|
158
|
+
resize: vertical;
|
|
159
|
+
}
|
|
160
|
+
.feedback-message:focus-visible,
|
|
161
|
+
.feedback-email:focus-visible {
|
|
162
|
+
outline: none;
|
|
163
|
+
border-color: var(--color-primary);
|
|
164
|
+
box-shadow: 0 0 0 3px var(--color-primary-soft);
|
|
165
|
+
}
|
|
166
|
+
.feedback-email {
|
|
167
|
+
height: var(--wui-control-height-sm);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.feedback-error {
|
|
171
|
+
margin: 0 0 4px;
|
|
172
|
+
font-size: 13px;
|
|
173
|
+
line-height: 1.4;
|
|
174
|
+
color: var(--color-danger-text);
|
|
175
|
+
}
|
|
176
|
+
.feedback-error[hidden] {
|
|
177
|
+
display: none;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ── Actions ──
|
|
181
|
+
.feedback-actions {
|
|
182
|
+
display: flex;
|
|
183
|
+
justify-content: flex-end;
|
|
184
|
+
gap: 8px;
|
|
185
|
+
}
|
|
186
|
+
.feedback-cancel,
|
|
187
|
+
.feedback-submit {
|
|
188
|
+
height: var(--wui-control-height-sm);
|
|
189
|
+
padding: 0 14px;
|
|
190
|
+
border-radius: var(--wui-radius-sm);
|
|
191
|
+
font-size: 14px;
|
|
192
|
+
font-weight: 600;
|
|
193
|
+
line-height: 1;
|
|
194
|
+
cursor: pointer;
|
|
195
|
+
}
|
|
196
|
+
.feedback-cancel {
|
|
197
|
+
border: 1px solid var(--color-border);
|
|
198
|
+
background: transparent;
|
|
199
|
+
color: var(--color-text);
|
|
200
|
+
}
|
|
201
|
+
.feedback-cancel:hover {
|
|
202
|
+
background: var(--color-bg-muted);
|
|
203
|
+
}
|
|
204
|
+
.feedback-submit {
|
|
205
|
+
border: none;
|
|
206
|
+
background: var(--color-primary);
|
|
207
|
+
color: var(--color-primary-text);
|
|
208
|
+
}
|
|
209
|
+
.feedback-submit:hover:not(:disabled) {
|
|
210
|
+
background: var(--color-primary-hover);
|
|
211
|
+
}
|
|
212
|
+
.feedback-submit:disabled {
|
|
213
|
+
opacity: 0.65;
|
|
214
|
+
cursor: default;
|
|
215
|
+
}
|
|
216
|
+
.feedback-cancel:focus-visible,
|
|
217
|
+
.feedback-submit:focus-visible {
|
|
218
|
+
outline: 2px solid var(--color-primary);
|
|
219
|
+
outline-offset: 2px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── Thank-you ──
|
|
223
|
+
.feedback-thanks {
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
gap: 10px;
|
|
227
|
+
padding: 8px 0 4px;
|
|
228
|
+
font-size: 14px;
|
|
229
|
+
color: var(--color-success-text);
|
|
230
|
+
}
|
|
231
|
+
.feedback-thanks-icon {
|
|
232
|
+
display: grid;
|
|
233
|
+
place-items: center;
|
|
234
|
+
line-height: 0;
|
|
235
|
+
}
|
|
236
|
+
.feedback-thanks-text {
|
|
237
|
+
font-weight: 500;
|
|
238
|
+
line-height: 1.4;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ── Small screens ──
|
|
242
|
+
// The panel is nearly the width of the phone, so pin it to both edges rather than to the
|
|
243
|
+
// trigger's corner — a 340px card anchored to one side overhangs on the narrowest devices.
|
|
244
|
+
@include media-breakpoint-down(sm) {
|
|
245
|
+
.feedback-panel {
|
|
246
|
+
position: fixed;
|
|
247
|
+
right: 12px;
|
|
248
|
+
left: 12px;
|
|
249
|
+
bottom: calc(var(--wui-control-height-sm) + 32px);
|
|
250
|
+
width: auto;
|
|
251
|
+
max-width: none;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@media (prefers-reduced-motion: reduce) {
|
|
256
|
+
.feedback-trigger {
|
|
257
|
+
transition: none;
|
|
258
|
+
}
|
|
259
|
+
}
|
package/scss/wui/_utilities.scss
CHANGED
|
@@ -164,12 +164,11 @@
|
|
|
164
164
|
align-items: center;
|
|
165
165
|
justify-content: space-between;
|
|
166
166
|
gap: 16px;
|
|
167
|
-
// Content-driven wrap
|
|
168
|
-
// actions pinned right; once a long title needs
|
|
169
|
-
// row, so the actions drop onto their own line —
|
|
170
|
-
// lone wrapped item at flex-start — instead of
|
|
171
|
-
// doubles as the row-gap to that dropped line.
|
|
172
|
-
// down; this is the any-width, content-driven case.)
|
|
167
|
+
// Content-driven wrap at EVERY width — there is no breakpoint stack. While the title block
|
|
168
|
+
// and actions both fit, space-between keeps the actions pinned right; once a long title needs
|
|
169
|
+
// the full width they can no longer share the row, so the actions drop onto their own line —
|
|
170
|
+
// left-aligned, since space-between places a lone wrapped item at flex-start — instead of
|
|
171
|
+
// being crushed against the title. The gap above doubles as the row-gap to that dropped line.
|
|
173
172
|
flex-wrap: wrap;
|
|
174
173
|
}
|
|
175
174
|
// Left block: an optional leading icon tile + the title stack, kept together.
|
|
@@ -262,6 +261,14 @@
|
|
|
262
261
|
display: flex;
|
|
263
262
|
gap: 8px;
|
|
264
263
|
align-items: center;
|
|
264
|
+
// Several action buttons wrap among themselves if their own line still can't hold them all
|
|
265
|
+
// — but they never force the block full-width, so a single button stays its natural size.
|
|
266
|
+
flex-wrap: wrap;
|
|
267
|
+
// Don't let the actions be squeezed. The title block (`.page-header-lead`, min-width: 0)
|
|
268
|
+
// absorbs the shrink instead, so the actions keep their natural size and the row stays on
|
|
269
|
+
// one line while it fits — then `.page-header-bar`'s flex-wrap drops the WHOLE actions
|
|
270
|
+
// block onto its own line only once the content genuinely can't share the row.
|
|
271
|
+
flex: none;
|
|
265
272
|
}
|
|
266
273
|
|
|
267
274
|
// ── Size scale ── (`size`): the title and the block's rhythm scale together.
|
|
@@ -318,21 +325,18 @@
|
|
|
318
325
|
// its actions stack, and the actions stretch to the full width rather than
|
|
319
326
|
// crowding the title. Bootstrap's lg tier (991.98px), not an invented breakpoint.
|
|
320
327
|
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
328
|
+
// The masthead bar is NOT stacked at a breakpoint. It wraps by CONTENT: `.page-header-bar`
|
|
329
|
+
// carries `flex-wrap: wrap` and its actions `flex: none`, so the title and actions share one
|
|
330
|
+
// row wherever they fit and the actions drop below only when the row genuinely can't hold both
|
|
331
|
+
// — a hard `max-width: 991.98px` column-stack pushed the actions under the title even on a
|
|
332
|
+
// 768px tablet that had ample room for them side by side.
|
|
333
|
+
//
|
|
334
|
+
// Selectors below are DOUBLED (`.field-row.field-row`) on purpose. A media query adds no
|
|
335
|
+
// specificity, so a consumer's own single-class rule — written later in the cascade, as a
|
|
336
|
+
// consuming app's stylesheet always is — silently beats the responsive rule and the layout
|
|
337
|
+
// never collapses. Doubling puts these at (0,2,0) so they hold, while still leaving a consumer
|
|
338
|
+
// free to override deliberately.
|
|
326
339
|
@media (max-width: 991.98px) {
|
|
327
|
-
.page-header-bar.page-header-bar {
|
|
328
|
-
flex-direction: column;
|
|
329
|
-
align-items: stretch;
|
|
330
|
-
gap: 12px;
|
|
331
|
-
}
|
|
332
|
-
.page-actions.page-actions {
|
|
333
|
-
flex-wrap: wrap;
|
|
334
|
-
width: 100%;
|
|
335
|
-
}
|
|
336
340
|
// A two-up field row is too tight for a tablet column.
|
|
337
341
|
.field-row.field-row,
|
|
338
342
|
.field-row-3.field-row-3 {
|
|
@@ -422,6 +426,62 @@
|
|
|
422
426
|
display: inline-block;
|
|
423
427
|
}
|
|
424
428
|
|
|
429
|
+
// Password-requirements checklist — the content of the popover AppInput (type="password" +
|
|
430
|
+
// passwordRequirements) opens on focus. Rendered identically by React (a JSX <ul>) and Razor
|
|
431
|
+
// (the same <ul> built in webority-ui.js), so both surfaces read the same. `.pw-popover` is the
|
|
432
|
+
// custom class both put on Bootstrap's `.popover` panel.
|
|
433
|
+
.pw-popover .popover-body {
|
|
434
|
+
padding: 12px 14px;
|
|
435
|
+
}
|
|
436
|
+
.pw-criteria {
|
|
437
|
+
list-style: none;
|
|
438
|
+
margin: 0;
|
|
439
|
+
padding: 0;
|
|
440
|
+
display: flex;
|
|
441
|
+
flex-direction: column;
|
|
442
|
+
gap: 8px;
|
|
443
|
+
}
|
|
444
|
+
.pw-criteria .pw-rule {
|
|
445
|
+
display: flex;
|
|
446
|
+
align-items: center;
|
|
447
|
+
gap: 8px;
|
|
448
|
+
font-size: 13px;
|
|
449
|
+
line-height: 1.3;
|
|
450
|
+
}
|
|
451
|
+
// The state mark is a baked-colour circled ✓/✕ — matched to the AA status ramp. The list TEXT
|
|
452
|
+
// uses the -text token for contrast; the mark itself needs less and uses the vivid ramp value.
|
|
453
|
+
.pw-criteria .pw-mark {
|
|
454
|
+
flex: none;
|
|
455
|
+
width: 18px;
|
|
456
|
+
height: 18px;
|
|
457
|
+
background: center / contain no-repeat;
|
|
458
|
+
}
|
|
459
|
+
.pw-criteria .pw-rule-text {
|
|
460
|
+
min-width: 0;
|
|
461
|
+
}
|
|
462
|
+
.pw-criteria .pw-rule.met {
|
|
463
|
+
color: var(--color-success-text);
|
|
464
|
+
}
|
|
465
|
+
.pw-criteria .pw-rule.met .pw-mark {
|
|
466
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2317C653' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m8 12 2.5 2.5 5.5-6'/%3E%3C/svg%3E");
|
|
467
|
+
}
|
|
468
|
+
.pw-criteria .pw-rule.unmet {
|
|
469
|
+
color: var(--color-danger-text);
|
|
470
|
+
}
|
|
471
|
+
.pw-criteria .pw-rule.unmet .pw-mark {
|
|
472
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23F8285A' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m9 9 6 6m0-6-6 6'/%3E%3C/svg%3E");
|
|
473
|
+
}
|
|
474
|
+
// The checklist opens with a PLAIN FADE only — no drop / zoom / scale. It opts out of the shared
|
|
475
|
+
// menu-open motion (_menu-motion.scss animates every `.popover.show`) and lets Bootstrap's own
|
|
476
|
+
// `.fade` opacity transition carry the fade in and out. Both surfaces render `.popover.pw-popover`
|
|
477
|
+
// (React via react-bootstrap's Popover, Razor via Bootstrap's popover), so this makes the two
|
|
478
|
+
// identical. Higher specificity than `.popover.show` so it wins regardless of partial load order.
|
|
479
|
+
.popover.pw-popover.show {
|
|
480
|
+
animation: none;
|
|
481
|
+
scale: none;
|
|
482
|
+
translate: none;
|
|
483
|
+
}
|
|
484
|
+
|
|
425
485
|
// Page/system-level announcement bar (AppBanner / <app-banner>). Full-width and persistent —
|
|
426
486
|
// distinct from an inline .alert or a transient toast. Tones reuse the shared soft/text ramp.
|
|
427
487
|
.wui-banner {
|