@webority/theme 0.13.5 → 0.15.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 +50 -6
- package/dist/purge-safelist.json +824 -0
- package/dist/webority-flags.css +1 -0
- package/dist/webority-theme.css +1 -1
- package/index.scss +3 -3
- package/package.json +4 -2
- package/scss/_components.scss +6 -6
- package/scss/_tokens.scss +6 -0
- package/scss/_wui-aliases.scss +36 -0
- package/scss/_wui-components.scss +6 -14
- package/scss/wui/_choice.scss +106 -27
- package/scss/wui/_command-palette.scss +195 -0
- package/scss/wui/_controls.scss +26 -6
- package/scss/wui/_display.scss +73 -19
- package/scss/wui/_flow.scss +10 -3
- package/scss/wui/_media.scss +6 -4
- package/scss/wui/_overlays.scss +13 -8
- package/scss/wui/_shell.scss +8 -2
- package/scss/wui/_utilities.scss +213 -206
- package/scss/wui/_workspace.scss +1 -1
package/index.scss
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Load order is LOAD-BEARING — never reorder:
|
|
6
6
|
// variables (before bootstrap) -> bootstrap -> tokens -> aliases -> components -> wui-components
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// Country flags are NOT imported here, and neither does _wui-components: they are an opt-in
|
|
8
|
+
// layer (scss/_flags.scss, compiled on its own to dist/webority-flags.css) for sites that render
|
|
9
|
+
// the phone input or country picker.
|
|
10
10
|
@import "scss/variables";
|
|
11
11
|
@import "bootstrap/scss/bootstrap";
|
|
12
12
|
@import "scss/tokens";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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": {
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
"./scss/wui/*": "./scss/wui/_*.scss",
|
|
27
27
|
"./scss/*": "./scss/_*.scss",
|
|
28
28
|
"./css": "./dist/webority-theme.css",
|
|
29
|
+
"./css/flags": "./dist/webority-flags.css",
|
|
30
|
+
"./purge-safelist.json": "./dist/purge-safelist.json",
|
|
29
31
|
"./package.json": "./package.json"
|
|
30
32
|
},
|
|
31
33
|
"sass": "index.scss",
|
|
@@ -39,6 +41,6 @@
|
|
|
39
41
|
"sass-embedded": "^1.97.0"
|
|
40
42
|
},
|
|
41
43
|
"scripts": {
|
|
42
|
-
"build": "sass index.scss dist/webority-theme.css --load-path=node_modules --style=compressed --no-source-map --quiet-deps --silence-deprecation=if-function && node ../../scripts/sync-rcl-assets.mjs theme"
|
|
44
|
+
"build": "sass index.scss dist/webority-theme.css --load-path=node_modules --style=compressed --no-source-map --quiet-deps --silence-deprecation=if-function && sass scss/_flags.scss dist/webority-flags.css --style=compressed --no-source-map --quiet && node ../../scripts/gen-purge-safelist.mjs && node ../../scripts/sync-rcl-assets.mjs theme"
|
|
43
45
|
}
|
|
44
46
|
}
|
package/scss/_components.scss
CHANGED
|
@@ -245,32 +245,32 @@
|
|
|
245
245
|
// sizes step font-size, horizontal padding and radius together (vertical size
|
|
246
246
|
// comes from --wui-control-height, so a button lines up with an input). Weight
|
|
247
247
|
// is 600 on every rung — buttons read as a deliberate action, not body text.
|
|
248
|
-
// Medium (default):
|
|
248
|
+
// Medium (default): 14px / 600, 24px side padding, 18px squircle radius, 45px tall.
|
|
249
249
|
.btn {
|
|
250
250
|
--bs-btn-border-radius: 18px;
|
|
251
|
-
--bs-btn-font-size: 0.
|
|
251
|
+
--bs-btn-font-size: 0.875rem;
|
|
252
252
|
--bs-btn-font-weight: 600;
|
|
253
253
|
--bs-btn-padding-x: 1.5rem;
|
|
254
254
|
// Progressive enhancement only (Chrome/Safari recent). Unsupported engines
|
|
255
255
|
// keep a plain border-radius arc — not a cross-browser design contract.
|
|
256
256
|
corner-shape: squircle;
|
|
257
257
|
}
|
|
258
|
-
// Small:
|
|
258
|
+
// Small: 12px, 18px side padding, 14px squircle radius (proportional to md), 35px tall.
|
|
259
259
|
.btn-sm:not(.btn-icon),
|
|
260
260
|
.btn-group-sm > .btn:not(.btn-icon) {
|
|
261
261
|
--bs-btn-border-radius: 14px;
|
|
262
|
-
--bs-btn-font-size: 0.
|
|
262
|
+
--bs-btn-font-size: 0.75rem;
|
|
263
263
|
--bs-btn-padding-x: 1.125rem;
|
|
264
264
|
display: inline-flex;
|
|
265
265
|
align-items: center;
|
|
266
266
|
justify-content: center;
|
|
267
267
|
min-height: 35px;
|
|
268
268
|
}
|
|
269
|
-
// Large:
|
|
269
|
+
// Large: 16px, 32px side padding, 22px squircle radius (proportional to md), 55px tall — a spacious CTA.
|
|
270
270
|
.btn-lg:not(.btn-icon),
|
|
271
271
|
.btn-group-lg > .btn:not(.btn-icon) {
|
|
272
272
|
--bs-btn-border-radius: 22px;
|
|
273
|
-
--bs-btn-font-size:
|
|
273
|
+
--bs-btn-font-size: 1rem;
|
|
274
274
|
--bs-btn-padding-x: 2rem;
|
|
275
275
|
display: inline-flex;
|
|
276
276
|
align-items: center;
|
package/scss/_tokens.scss
CHANGED
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
--wui-field-bg: #F9FBFC;
|
|
44
44
|
--wui-border-light: #EBEDF3;
|
|
45
45
|
--wui-border-soft: #F4F6F8;
|
|
46
|
+
// The unfilled part of a progress bar / meter. Darker than every surface it can sit on
|
|
47
|
+
// (card, page, muted fill) so the empty track reads as a track, yet light enough that the
|
|
48
|
+
// mid-yellow warning fill still separates from it.
|
|
49
|
+
--wui-progress-track: #E1E5EE;
|
|
46
50
|
|
|
47
51
|
// Gradients (hero, CTA)
|
|
48
52
|
--wui-hero-gradient:
|
|
@@ -217,6 +221,8 @@
|
|
|
217
221
|
--wui-dark-bg-surface: #181b23;
|
|
218
222
|
--wui-dark-border: #2a2f3a;
|
|
219
223
|
--wui-dark-border-strong:#3a414f;
|
|
224
|
+
// Lighter than the dark page, surface and muted fill, so the empty track shows on all three.
|
|
225
|
+
--wui-dark-progress-track: #323845;
|
|
220
226
|
--wui-dark-text: #e6e8ee;
|
|
221
227
|
--wui-dark-text-muted: #b4b9c6;
|
|
222
228
|
--wui-dark-text-subtle: #8a90a0;
|
package/scss/_wui-aliases.scss
CHANGED
|
@@ -18,9 +18,18 @@
|
|
|
18
18
|
--color-border: var(--wui-border-light);
|
|
19
19
|
--color-border-light: var(--wui-border-soft);
|
|
20
20
|
--color-border-strong: var(--wui-text-5);
|
|
21
|
+
--color-progress-track: var(--wui-progress-track);
|
|
22
|
+
// Label text inside a progress bar fill.
|
|
23
|
+
--color-progress-label: var(--wui-bg-light);
|
|
21
24
|
|
|
22
25
|
// Text ramp
|
|
23
26
|
--color-text: var(--wui-text-heading);
|
|
27
|
+
// Body copy on a tinted surface (alert bodies): a step softer than the heading ink.
|
|
28
|
+
--color-text-body: var(--wui-text-1);
|
|
29
|
+
// A dark-ink glyph such as Bootstrap's .btn-close needs no filter on a light surface.
|
|
30
|
+
--color-close-filter: none;
|
|
31
|
+
// The border of a focused / open / active control (input, select, OTP slot, dropzone).
|
|
32
|
+
--color-focus-border: var(--wui-primary-30);
|
|
24
33
|
--color-text-muted: var(--wui-text-2);
|
|
25
34
|
--color-text-subtle: var(--wui-text-3);
|
|
26
35
|
|
|
@@ -54,6 +63,7 @@
|
|
|
54
63
|
--color-info-soft: var(--color-info-10);
|
|
55
64
|
// Use this — never --color-info — when the colour IS the text.
|
|
56
65
|
--color-info-text: var(--wui-info-text);
|
|
66
|
+
--color-info-text-on-soft: var(--wui-info-text);
|
|
57
67
|
|
|
58
68
|
--color-success-active: var(--wui-success-active);
|
|
59
69
|
--color-success: var(--wui-success);
|
|
@@ -66,6 +76,7 @@
|
|
|
66
76
|
--color-success-soft: var(--color-success-10);
|
|
67
77
|
// Use this — never --color-success — when the colour IS the text.
|
|
68
78
|
--color-success-text: var(--wui-success-text);
|
|
79
|
+
--color-success-text-on-soft: var(--wui-success-text);
|
|
69
80
|
|
|
70
81
|
--color-warning-active: var(--wui-warning-active);
|
|
71
82
|
--color-warning: var(--wui-warning);
|
|
@@ -79,6 +90,7 @@
|
|
|
79
90
|
--color-warning-soft: var(--color-warning-10);
|
|
80
91
|
// Use this — never --color-warning — when the colour IS the text.
|
|
81
92
|
--color-warning-text: var(--wui-warning-text);
|
|
93
|
+
--color-warning-text-on-soft: var(--wui-warning-text);
|
|
82
94
|
|
|
83
95
|
--color-danger-active: var(--wui-danger-active);
|
|
84
96
|
--color-danger: var(--wui-danger);
|
|
@@ -91,6 +103,7 @@
|
|
|
91
103
|
--color-danger-soft: var(--color-danger-10);
|
|
92
104
|
// Use this — never --color-danger — when the colour IS the text.
|
|
93
105
|
--color-danger-text: var(--wui-danger-text);
|
|
106
|
+
--color-danger-text-on-soft: var(--wui-danger-text);
|
|
94
107
|
|
|
95
108
|
// Accent → warning ramp
|
|
96
109
|
--color-accent: var(--color-warning);
|
|
@@ -137,10 +150,19 @@
|
|
|
137
150
|
--color-border: var(--wui-dark-border);
|
|
138
151
|
--color-border-light: color-mix(in srgb, var(--wui-dark-border) 50%, transparent);
|
|
139
152
|
--color-border-strong: var(--wui-dark-border-strong);
|
|
153
|
+
--color-progress-track: var(--wui-dark-progress-track);
|
|
154
|
+
// Same near-black as --color-primary-text below: 4.97:1 or better on every bright fill.
|
|
155
|
+
--color-progress-label: #0F0F0F;
|
|
140
156
|
|
|
141
157
|
--color-text: var(--wui-dark-text);
|
|
142
158
|
--color-text-muted: var(--wui-dark-text-muted);
|
|
143
159
|
--color-text-subtle: var(--wui-dark-text-subtle);
|
|
160
|
+
--color-text-body: var(--wui-dark-text);
|
|
161
|
+
// Bootstrap's .btn-close glyph is ink-dark; on a dark surface it needs the white filter.
|
|
162
|
+
--color-close-filter: invert(1) grayscale(100%) brightness(200%);
|
|
163
|
+
// The light pastel focus border glows against a dark field; dark mode uses the dark theme's
|
|
164
|
+
// own primary (the button fill), which still clears 3:1 against every dark surface.
|
|
165
|
+
--color-focus-border: var(--wui-dark-primary);
|
|
144
166
|
|
|
145
167
|
// Re-bridge the Bootstrap colour vars onto the (now dark) app tokens so the
|
|
146
168
|
// named-var text/body/border utilities — .text-body-secondary, .text-muted,
|
|
@@ -183,6 +205,14 @@
|
|
|
183
205
|
--color-warning-text: var(--wui-warning);
|
|
184
206
|
--color-info-text: var(--wui-info);
|
|
185
207
|
|
|
208
|
+
// Text ON a soft tint (an alert heading). The bright tone alone falls under WCAG AA on its own
|
|
209
|
+
// 18% tint over the dark page (info and danger reach only ~3.8:1), so it is lifted with 30%
|
|
210
|
+
// white: every tone then clears 4.5:1 on the dark page, surface and muted fill.
|
|
211
|
+
--color-success-text-on-soft: color-mix(in srgb, var(--wui-success) 70%, white);
|
|
212
|
+
--color-danger-text-on-soft: color-mix(in srgb, var(--wui-danger) 70%, white);
|
|
213
|
+
--color-warning-text-on-soft: color-mix(in srgb, var(--wui-warning) 70%, white);
|
|
214
|
+
--color-info-text-on-soft: color-mix(in srgb, var(--wui-info) 70%, white);
|
|
215
|
+
|
|
186
216
|
// Light mode maps -strong to deep AA fills; on dark surfaces those sink into the page.
|
|
187
217
|
// Remap to the bright fills so timeline dots / strong accents stay visible (same as -text).
|
|
188
218
|
--color-success-strong: var(--wui-success);
|
|
@@ -194,6 +224,12 @@
|
|
|
194
224
|
--color-warning-soft: color-mix(in srgb, var(--wui-warning) 18%, transparent);
|
|
195
225
|
--color-danger-soft: color-mix(in srgb, var(--wui-danger) 18%, transparent);
|
|
196
226
|
--color-info-soft: color-mix(in srgb, var(--wui-info) 18%, transparent);
|
|
227
|
+
// The -50 border tone: the light hex would glow on a dark page, so it becomes a translucent
|
|
228
|
+
// mix of the vivid tone that reads as a border on the dark tint.
|
|
229
|
+
--color-success-50: color-mix(in srgb, var(--wui-success) 45%, transparent);
|
|
230
|
+
--color-warning-50: color-mix(in srgb, var(--wui-warning) 45%, transparent);
|
|
231
|
+
--color-danger-50: color-mix(in srgb, var(--wui-danger) 45%, transparent);
|
|
232
|
+
--color-info-50: color-mix(in srgb, var(--wui-info) 45%, transparent);
|
|
197
233
|
--color-accent-soft: var(--color-warning-soft);
|
|
198
234
|
|
|
199
235
|
--color-scrim: color-mix(in srgb, black 45%, transparent);
|
|
@@ -43,23 +43,15 @@
|
|
|
43
43
|
// set, so it has to win on equal specificity.
|
|
44
44
|
@import "wui/workspace";
|
|
45
45
|
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
// wui-components) got AppPhoneNumberInput with an empty grey box where every flag should be,
|
|
51
|
-
// and nothing failing anywhere to point at the cause. The component shipped; its styling did
|
|
52
|
-
// not. That is the same escape as th.sortable and .u-name, and the fix is the same principle:
|
|
53
|
-
// a component and the CSS it requires travel together.
|
|
54
|
-
|
|
55
|
-
// It is not free — roughly 183KB raw, 45KB gzipped, mostly inlined SVG. A product that never
|
|
56
|
-
// renders a phone field can skip it by importing the `wui/*` partials above individually
|
|
57
|
-
// instead of this aggregate; that is the deliberate opt-out, and it is a choice rather than
|
|
58
|
-
// an accident.
|
|
59
|
-
@import "flags";
|
|
46
|
+
// Country flags are NOT part of this aggregate. The 245 `.wui-flag-*` rules are about 187KB raw
|
|
47
|
+
// (38KB Brotli) of inlined SVG, and most sites never render a phone field or country picker.
|
|
48
|
+
// A site that does imports `@webority/theme/scss/flags` once, after this file; without it
|
|
49
|
+
// <wui-phone> and <wui-country-select> show an empty grey box where the flag should be.
|
|
60
50
|
@import "wui/shell";
|
|
61
51
|
@import "wui/dock";
|
|
62
52
|
@import "wui/consent-banner";
|
|
63
53
|
@import "wui/toolbar";
|
|
54
|
+
// After `overlays`: anchors the shared modal shell to the top when it holds the palette card.
|
|
55
|
+
@import "wui/command-palette";
|
|
64
56
|
// Last: restates the settled state of menus other partials style, so it must win.
|
|
65
57
|
@import "wui/menu-motion";
|
package/scss/wui/_choice.scss
CHANGED
|
@@ -58,18 +58,14 @@
|
|
|
58
58
|
background: var(--color-surface);
|
|
59
59
|
color: var(--color-primary);
|
|
60
60
|
}
|
|
61
|
-
//
|
|
62
|
-
// selected,
|
|
63
|
-
.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
.struct-card.checked .struct-card-ic img {
|
|
71
|
-
filter: none;
|
|
72
|
-
opacity: 1;
|
|
61
|
+
// An image in the round icon disc (React iconNode={<img …/>}) keeps its own colours whether or not
|
|
62
|
+
// the card is selected, fills the whole disc and is cropped to its circle, whatever the image's own
|
|
63
|
+
// size or aspect. The tile variant drops the disc and sizes its illustration itself, so it is left out.
|
|
64
|
+
.struct-card:not(.struct-card-tile) .struct-card-ic img {
|
|
65
|
+
width: 100%;
|
|
66
|
+
height: 100%;
|
|
67
|
+
object-fit: cover;
|
|
68
|
+
border-radius: inherit;
|
|
73
69
|
}
|
|
74
70
|
.struct-card-body {
|
|
75
71
|
display: flex;
|
|
@@ -113,6 +109,77 @@
|
|
|
113
109
|
.struct-card-check svg path {
|
|
114
110
|
stroke-width: 2.25;
|
|
115
111
|
}
|
|
112
|
+
// No tick (hideCheck / hide-check): the plain card no longer needs the top/bottom room the
|
|
113
|
+
// corner tick sat in, so it tightens. The tile and image cards keep their own padding.
|
|
114
|
+
.wui-struct-card-no-check:not(.struct-card-tile):not(.struct-card-img-card) {
|
|
115
|
+
padding-top: 14px;
|
|
116
|
+
padding-bottom: 14px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Sizes (size="sm" | "lg"; md is the base card above). Padding, gap, icon disc, type and the tick
|
|
120
|
+
// offset step together so a card reads as one unit at every size. The tile variant owns its layout
|
|
121
|
+
// and type (its later rules win), and the image card steps only its body padding and type.
|
|
122
|
+
.wui-struct-card-sm {
|
|
123
|
+
gap: 9px;
|
|
124
|
+
}
|
|
125
|
+
.wui-struct-card-sm:not(.struct-card-tile):not(.struct-card-img-card) {
|
|
126
|
+
padding: 14px;
|
|
127
|
+
}
|
|
128
|
+
.wui-struct-card-sm.wui-struct-card-no-check:not(.struct-card-tile):not(.struct-card-img-card) {
|
|
129
|
+
padding-top: 10px;
|
|
130
|
+
padding-bottom: 10px;
|
|
131
|
+
}
|
|
132
|
+
.wui-struct-card-sm .struct-card-ic {
|
|
133
|
+
width: 36px;
|
|
134
|
+
height: 36px;
|
|
135
|
+
}
|
|
136
|
+
.wui-struct-card-sm .struct-card-title {
|
|
137
|
+
font-size: 14px;
|
|
138
|
+
}
|
|
139
|
+
.wui-struct-card-sm .struct-card-desc {
|
|
140
|
+
font-size: 13px;
|
|
141
|
+
}
|
|
142
|
+
.wui-struct-card-sm .struct-card-check {
|
|
143
|
+
top: 7px;
|
|
144
|
+
right: 7px;
|
|
145
|
+
}
|
|
146
|
+
.wui-struct-card-sm.struct-card-img-card .struct-card-body {
|
|
147
|
+
padding: 9px 11px;
|
|
148
|
+
}
|
|
149
|
+
.wui-struct-card-lg {
|
|
150
|
+
gap: 14px;
|
|
151
|
+
}
|
|
152
|
+
.wui-struct-card-lg:not(.struct-card-tile):not(.struct-card-img-card) {
|
|
153
|
+
padding: 26px;
|
|
154
|
+
}
|
|
155
|
+
.wui-struct-card-lg.wui-struct-card-no-check:not(.struct-card-tile):not(.struct-card-img-card) {
|
|
156
|
+
padding-top: 18px;
|
|
157
|
+
padding-bottom: 18px;
|
|
158
|
+
}
|
|
159
|
+
.wui-struct-card-lg .struct-card-ic {
|
|
160
|
+
width: 56px;
|
|
161
|
+
height: 56px;
|
|
162
|
+
}
|
|
163
|
+
.wui-struct-card-lg .struct-card-title {
|
|
164
|
+
font-size: 18px;
|
|
165
|
+
}
|
|
166
|
+
.wui-struct-card-lg .struct-card-desc {
|
|
167
|
+
font-size: 15px;
|
|
168
|
+
}
|
|
169
|
+
.wui-struct-card-lg .struct-card-check {
|
|
170
|
+
top: 13px;
|
|
171
|
+
right: 13px;
|
|
172
|
+
}
|
|
173
|
+
.wui-struct-card-lg.struct-card-img-card .struct-card-body {
|
|
174
|
+
padding: 15px 18px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Stacked layout (layout="stacked"): the icon disc sits on its own line and the title and
|
|
178
|
+
// description start on the line below, left-aligned. The gap between them follows the size step.
|
|
179
|
+
.wui-struct-card-stacked {
|
|
180
|
+
flex-direction: column;
|
|
181
|
+
align-items: flex-start;
|
|
182
|
+
}
|
|
116
183
|
|
|
117
184
|
// Tile variant: big centered illustration + label. Opt in with
|
|
118
185
|
// className="struct-card-tile".
|
|
@@ -131,13 +198,9 @@
|
|
|
131
198
|
border-radius: 0;
|
|
132
199
|
background: transparent;
|
|
133
200
|
}
|
|
134
|
-
|
|
135
|
-
.struct-card-tile .struct-card-ic img,
|
|
136
|
-
.struct-card-tile.checked .struct-card-ic img {
|
|
201
|
+
.struct-card-tile .struct-card-ic img {
|
|
137
202
|
width: 100px;
|
|
138
203
|
height: 100px;
|
|
139
|
-
filter: none;
|
|
140
|
-
opacity: 1;
|
|
141
204
|
}
|
|
142
205
|
.struct-card-tile .struct-card-body {
|
|
143
206
|
align-items: center;
|
|
@@ -185,10 +248,10 @@
|
|
|
185
248
|
}
|
|
186
249
|
.search-select-control.open {
|
|
187
250
|
// White fill + the active/focus border, matching a focused text input (which lifts off the
|
|
188
|
-
// muted resting fill onto white with a --
|
|
251
|
+
// muted resting fill onto white with a --color-focus-border border). Keeps the control seamless
|
|
189
252
|
// with the open menu and the floated label's white mask.
|
|
190
253
|
background: var(--color-surface);
|
|
191
|
-
border-color: var(--
|
|
254
|
+
border-color: var(--color-focus-border);
|
|
192
255
|
}
|
|
193
256
|
.search-select-caret {
|
|
194
257
|
flex-shrink: 0;
|
|
@@ -367,7 +430,7 @@
|
|
|
367
430
|
}
|
|
368
431
|
.search-select-search:focus-within {
|
|
369
432
|
background: var(--color-surface);
|
|
370
|
-
border-color: var(--
|
|
433
|
+
border-color: var(--color-focus-border);
|
|
371
434
|
}
|
|
372
435
|
.search-select-search:focus-within svg {
|
|
373
436
|
color: var(--color-primary);
|
|
@@ -506,12 +569,21 @@
|
|
|
506
569
|
// While the calendar is open the trigger reads as a focused field — clean white surface, not
|
|
507
570
|
// the muted resting fill. Opening moves focus INTO the popup, so :focus drops off the trigger;
|
|
508
571
|
// this .open rule keeps it white (matching .search-select-control.open) instead of snapping
|
|
509
|
-
// back to grey the moment the dropdown appears.
|
|
510
|
-
// the trigger → :focus), greying only on an outside click that blurs it.
|
|
572
|
+
// back to grey the moment the dropdown appears.
|
|
511
573
|
background-color: var(--color-surface);
|
|
512
|
-
border-color:
|
|
574
|
+
border-color: var(--color-focus-border);
|
|
513
575
|
box-shadow: none;
|
|
514
576
|
}
|
|
577
|
+
// Closing hands focus back to the trigger (so the keyboard user lands where they started), which
|
|
578
|
+
// would leave a mouse user with an "active" field after the calendar is gone. Once the popup is
|
|
579
|
+
// shut, the trigger keeps the focus look only for keyboard focus (:focus-visible); a pick or
|
|
580
|
+
// close by pointer returns it to rest. An errored field (aria-invalid on the host) keeps its red.
|
|
581
|
+
wui-datepicker:not([aria-invalid="true"]) .datepicker:not(.open) .datepicker-trigger:focus:not(:focus-visible),
|
|
582
|
+
wui-date-range:not([aria-invalid="true"]) .daterange:not(.open) .daterange-trigger:focus:not(:focus-visible),
|
|
583
|
+
wui-timepicker:not([aria-invalid="true"]) .timepicker:not(.open) .timepicker-trigger:focus:not(:focus-visible) {
|
|
584
|
+
background-color: var(--color-field-bg);
|
|
585
|
+
border-color: var(--color-border);
|
|
586
|
+
}
|
|
515
587
|
// Portalled to <body> and fixed-positioned from JS so a modal's
|
|
516
588
|
// overflow:hidden can't clip it; top/left come from inline styles. z-index
|
|
517
589
|
// sits above the modal layer.
|
|
@@ -677,6 +749,13 @@ button.datepicker-month:hover {
|
|
|
677
749
|
color: var(--color-border-strong);
|
|
678
750
|
cursor: not-allowed;
|
|
679
751
|
}
|
|
752
|
+
// Today outside the allowed range keeps its ring, so the user can still find it, but in the
|
|
753
|
+
// disabled ink: a primary ring would read as a date they can pick.
|
|
754
|
+
.datepicker-day.is-today:disabled,
|
|
755
|
+
.datepicker-cell.is-today:disabled {
|
|
756
|
+
box-shadow: inset 0 0 0 1.5px var(--color-border-strong);
|
|
757
|
+
color: var(--color-border-strong);
|
|
758
|
+
}
|
|
680
759
|
.datepicker-foot {
|
|
681
760
|
display: flex;
|
|
682
761
|
align-items: center;
|
|
@@ -835,7 +914,7 @@ button.datepicker-month:hover {
|
|
|
835
914
|
.daterange.open .daterange-trigger {
|
|
836
915
|
// Stay white while the two-month popover is open — see the note on .datepicker.open above.
|
|
837
916
|
background-color: var(--color-surface);
|
|
838
|
-
border-color:
|
|
917
|
+
border-color: var(--color-focus-border);
|
|
839
918
|
box-shadow: none;
|
|
840
919
|
}
|
|
841
920
|
.daterange-pop {
|
|
@@ -951,7 +1030,7 @@ button.datepicker-month:hover {
|
|
|
951
1030
|
// Match AppInput's focus: clean surface fill + a soft primary border.
|
|
952
1031
|
.app-phone-number-input .phone-input-wrap:focus-within {
|
|
953
1032
|
background: var(--color-surface);
|
|
954
|
-
border-color: var(--
|
|
1033
|
+
border-color: var(--color-focus-border);
|
|
955
1034
|
}
|
|
956
1035
|
.app-phone-number-input .phone-input-wrap:has(> .input:disabled) {
|
|
957
1036
|
background: var(--color-bg-muted);
|
|
@@ -1176,7 +1255,7 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
|
|
|
1176
1255
|
// AppInput's exact focus: clean surface fill + its soft primary border token.
|
|
1177
1256
|
.dropdown-search-input:focus {
|
|
1178
1257
|
background: var(--color-surface);
|
|
1179
|
-
border-color: var(--
|
|
1258
|
+
border-color: var(--color-focus-border);
|
|
1180
1259
|
}
|
|
1181
1260
|
// AppInput's placeholder colour, not the browser default grey.
|
|
1182
1261
|
.dropdown-search-input::placeholder {
|
|
@@ -1319,7 +1398,7 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
|
|
|
1319
1398
|
.timepicker.open .timepicker-trigger {
|
|
1320
1399
|
// Stay white while the time list is open — see the note on .datepicker.open above.
|
|
1321
1400
|
background-color: var(--color-surface);
|
|
1322
|
-
border-color:
|
|
1401
|
+
border-color: var(--color-focus-border);
|
|
1323
1402
|
box-shadow: none;
|
|
1324
1403
|
}
|
|
1325
1404
|
.timepicker-pop {
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// ── Command palette ──
|
|
2
|
+
// AppCommandPalette / <app-command-palette> / <wui-command-palette>: app-wide "search and jump".
|
|
3
|
+
// The overlay is the shared modal (.wui-modal-shell, .wui-modal), with .wui-cmd-card on the
|
|
4
|
+
// card; this partial styles only what sits inside it, plus the top anchoring. The element marks
|
|
5
|
+
// the active result with aria-selected="true", which is the one state hook below. Every colour
|
|
6
|
+
// is a theme token, so dark mode follows the token flip with no rules of its own.
|
|
7
|
+
//
|
|
8
|
+
// Part of the wui-components layer, see _wui-components.scss for load order.
|
|
9
|
+
|
|
10
|
+
// Anchored near the top rather than centred, so the input stays put while results grow and
|
|
11
|
+
// shrink underneath it.
|
|
12
|
+
.modal.wui-modal-shell:has(.wui-cmd-card) {
|
|
13
|
+
align-items: flex-start;
|
|
14
|
+
padding-top: max(20px, min(12vh, 120px));
|
|
15
|
+
}
|
|
16
|
+
.wui-modal.wui-cmd-card {
|
|
17
|
+
max-height: min(560px, calc(100vh - 2 * max(20px, min(12vh, 120px))));
|
|
18
|
+
}
|
|
19
|
+
// The list scrolls, not the body: the input and the footer stay pinned.
|
|
20
|
+
.wui-cmd-card > .wui-modal-body {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
padding: 0;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
wui-command-palette {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex: 1 1 auto;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
min-height: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.wui-cmd-search {
|
|
35
|
+
display: flex;
|
|
36
|
+
flex: 0 0 auto;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: 10px;
|
|
39
|
+
padding: 0 16px;
|
|
40
|
+
border-bottom: 1px solid var(--color-border);
|
|
41
|
+
}
|
|
42
|
+
.wui-cmd-search-icon {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
flex: 0 0 auto;
|
|
45
|
+
color: var(--color-text-muted);
|
|
46
|
+
}
|
|
47
|
+
.wui-cmd-input {
|
|
48
|
+
flex: 1 1 auto;
|
|
49
|
+
min-width: 0;
|
|
50
|
+
height: 52px;
|
|
51
|
+
padding: 0;
|
|
52
|
+
border: none;
|
|
53
|
+
outline: none;
|
|
54
|
+
background: transparent;
|
|
55
|
+
color: var(--color-text);
|
|
56
|
+
font-size: 16px;
|
|
57
|
+
}
|
|
58
|
+
.wui-cmd-input::placeholder {
|
|
59
|
+
color: var(--color-text-subtle);
|
|
60
|
+
}
|
|
61
|
+
.wui-cmd-spinner {
|
|
62
|
+
flex: 0 0 auto;
|
|
63
|
+
color: var(--color-primary);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.wui-cmd-list {
|
|
67
|
+
flex: 1 1 auto;
|
|
68
|
+
min-height: 0;
|
|
69
|
+
padding: 6px;
|
|
70
|
+
overflow-y: auto;
|
|
71
|
+
overscroll-behavior: contain;
|
|
72
|
+
scrollbar-width: thin;
|
|
73
|
+
scrollbar-color: var(--color-border-strong) transparent;
|
|
74
|
+
}
|
|
75
|
+
.wui-cmd-list[hidden],
|
|
76
|
+
.wui-cmd-group[hidden],
|
|
77
|
+
.wui-cmd-item[hidden] {
|
|
78
|
+
display: none;
|
|
79
|
+
}
|
|
80
|
+
.wui-cmd-group + .wui-cmd-group {
|
|
81
|
+
margin-top: 4px;
|
|
82
|
+
}
|
|
83
|
+
.wui-cmd-group-label {
|
|
84
|
+
padding: 8px 10px 4px;
|
|
85
|
+
color: var(--color-text-muted);
|
|
86
|
+
font-size: 11px;
|
|
87
|
+
font-weight: 600;
|
|
88
|
+
letter-spacing: 0.04em;
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.wui-cmd-item {
|
|
93
|
+
display: flex;
|
|
94
|
+
align-items: center;
|
|
95
|
+
gap: 12px;
|
|
96
|
+
padding: 8px 10px;
|
|
97
|
+
border-radius: var(--radius);
|
|
98
|
+
corner-shape: squircle;
|
|
99
|
+
color: var(--color-text);
|
|
100
|
+
cursor: pointer;
|
|
101
|
+
// Keeps a little of the next row visible when the keyboard scrolls the active one in.
|
|
102
|
+
scroll-margin: 6px;
|
|
103
|
+
}
|
|
104
|
+
.wui-cmd-item[aria-selected="true"] {
|
|
105
|
+
background: var(--color-primary-soft);
|
|
106
|
+
color: var(--color-primary);
|
|
107
|
+
}
|
|
108
|
+
.wui-cmd-item-icon {
|
|
109
|
+
display: inline-grid;
|
|
110
|
+
flex: 0 0 auto;
|
|
111
|
+
place-items: center;
|
|
112
|
+
width: 32px;
|
|
113
|
+
height: 32px;
|
|
114
|
+
border-radius: var(--radius);
|
|
115
|
+
corner-shape: squircle;
|
|
116
|
+
background: var(--color-bg-muted);
|
|
117
|
+
color: var(--color-text-muted);
|
|
118
|
+
}
|
|
119
|
+
.wui-cmd-item[aria-selected="true"] .wui-cmd-item-icon {
|
|
120
|
+
background: var(--color-surface);
|
|
121
|
+
color: var(--color-primary);
|
|
122
|
+
}
|
|
123
|
+
.wui-cmd-item-text {
|
|
124
|
+
display: flex;
|
|
125
|
+
flex: 1 1 auto;
|
|
126
|
+
flex-direction: column;
|
|
127
|
+
min-width: 0;
|
|
128
|
+
}
|
|
129
|
+
.wui-cmd-item-label,
|
|
130
|
+
.wui-cmd-item-desc {
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
text-overflow: ellipsis;
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
line-height: 1.4;
|
|
135
|
+
}
|
|
136
|
+
.wui-cmd-item-label {
|
|
137
|
+
font-size: 14px;
|
|
138
|
+
}
|
|
139
|
+
.wui-cmd-item-desc {
|
|
140
|
+
color: var(--color-text-muted);
|
|
141
|
+
font-size: 12px;
|
|
142
|
+
}
|
|
143
|
+
.wui-cmd-item-meta {
|
|
144
|
+
flex: 0 0 auto;
|
|
145
|
+
color: var(--color-text-muted);
|
|
146
|
+
font-size: 12px;
|
|
147
|
+
}
|
|
148
|
+
// The query inside a label: bolder, never a highlighter block.
|
|
149
|
+
.wui-cmd-mark {
|
|
150
|
+
padding: 0;
|
|
151
|
+
background: transparent;
|
|
152
|
+
color: inherit;
|
|
153
|
+
font-weight: 700;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.wui-cmd-empty {
|
|
157
|
+
padding: 20px 16px;
|
|
158
|
+
color: var(--color-text-muted);
|
|
159
|
+
font-size: 13px;
|
|
160
|
+
text-align: center;
|
|
161
|
+
}
|
|
162
|
+
// The status region is always in the page, so a screen reader hears the empty text arrive;
|
|
163
|
+
// with nothing to say it takes no room.
|
|
164
|
+
.wui-cmd-empty:empty {
|
|
165
|
+
display: none;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.wui-cmd-footer {
|
|
169
|
+
display: flex;
|
|
170
|
+
flex: 0 0 auto;
|
|
171
|
+
flex-wrap: wrap;
|
|
172
|
+
align-items: center;
|
|
173
|
+
gap: 6px 16px;
|
|
174
|
+
padding: 8px 16px;
|
|
175
|
+
border-top: 1px solid var(--color-border);
|
|
176
|
+
color: var(--color-text-muted);
|
|
177
|
+
font-size: 12px;
|
|
178
|
+
}
|
|
179
|
+
.wui-cmd-footer kbd {
|
|
180
|
+
padding: 1px 6px;
|
|
181
|
+
border: 1px solid var(--color-border);
|
|
182
|
+
border-radius: var(--wui-radius-sm);
|
|
183
|
+
background: var(--color-bg-muted);
|
|
184
|
+
color: var(--color-text);
|
|
185
|
+
font-family: inherit;
|
|
186
|
+
font-size: 11px;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
190
|
+
.wui-cmd-item {
|
|
191
|
+
transition:
|
|
192
|
+
background-color 0.12s ease,
|
|
193
|
+
color 0.12s ease;
|
|
194
|
+
}
|
|
195
|
+
}
|