@webority/theme 0.7.1 → 0.7.3
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/index.scss +15 -0
- package/package.json +2 -1
- package/scss/_components.scss +108 -10
- package/scss/_tokens.scss +44 -0
- package/scss/_variables.scss +10 -8
- package/scss/_wui-aliases.scss +3 -0
- package/scss/_wui-components.scss +1 -0
- package/scss/wui/_choice.scss +8 -3
- package/scss/wui/_controls.scss +13 -5
- package/scss/wui/_data.scss +40 -0
- package/scss/wui/_display.scss +41 -4
- package/scss/wui/_media.scss +24 -0
- package/scss/wui/_navigation.scss +187 -7
- package/scss/wui/_overlays.scss +36 -52
- package/scss/wui/_scrollbar.scss +32 -0
- package/scss/wui/_shell.scss +222 -10
package/index.scss
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Default-brand full chain — compiled to dist/webority-theme.css (the RCL static
|
|
2
|
+
// asset + the zero-toolchain convenience build). Consumers who want brand-exact
|
|
3
|
+
// output import the partials individually with their OWN _variables before bootstrap.
|
|
4
|
+
//
|
|
5
|
+
// Load order is LOAD-BEARING — never reorder:
|
|
6
|
+
// variables (before bootstrap) -> bootstrap -> tokens -> aliases -> components -> wui-components
|
|
7
|
+
// Flags are NOT imported here: _wui-components already imports them (the phone control needs
|
|
8
|
+
// them), and legacy @import does not dedupe, so a second import emitted all ~245 flag classes
|
|
9
|
+
// twice (≈490 selectors) and bloated the bundle.
|
|
10
|
+
@import "scss/variables";
|
|
11
|
+
@import "bootstrap/scss/bootstrap";
|
|
12
|
+
@import "scss/tokens";
|
|
13
|
+
@import "scss/wui-aliases";
|
|
14
|
+
@import "scss/components";
|
|
15
|
+
@import "scss/wui-components";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
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": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"directory": "packages/theme"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
+
"index.scss",
|
|
16
17
|
"scss",
|
|
17
18
|
"dist"
|
|
18
19
|
],
|
package/scss/_components.scss
CHANGED
|
@@ -49,8 +49,11 @@
|
|
|
49
49
|
// ---- Display heading -------------------------------------------------
|
|
50
50
|
// Bootstrap's .display-1..6 ladder skews bigger than our locked design.
|
|
51
51
|
// .h-display drops in where the marketing hero needs the locked
|
|
52
|
-
// font-weight 500 + tight letter-spacing.
|
|
52
|
+
// font-weight 500 + tight letter-spacing. font-family reads --wui-font-display
|
|
53
|
+
// so a product can swap in a serif/brand display face by overriding just that
|
|
54
|
+
// token (default: the same body stack .font-display already uses).
|
|
53
55
|
.h-display {
|
|
56
|
+
font-family: var(--wui-font-display, var(--bs-body-font-family));
|
|
54
57
|
font-weight: 500;
|
|
55
58
|
letter-spacing: -0.03em;
|
|
56
59
|
line-height: 1.05;
|
|
@@ -89,6 +92,14 @@
|
|
|
89
92
|
--bs-btn-hover-border-color: var(--wui-primary-pressed);
|
|
90
93
|
--bs-btn-active-bg: var(--wui-primary-pressed);
|
|
91
94
|
--bs-btn-active-border-color: var(--wui-primary-pressed);
|
|
95
|
+
// Bootstrap computes this label colour at SCSS compile time from the library's
|
|
96
|
+
// default $primary, so it can never react to a product overriding --wui-primary as a
|
|
97
|
+
// runtime CSS var. Routing it through the token makes a light-primary brand's
|
|
98
|
+
// --wui-primary-text override reach the button.
|
|
99
|
+
--bs-btn-color: var(--wui-primary-text);
|
|
100
|
+
--bs-btn-hover-color: var(--wui-primary-text);
|
|
101
|
+
--bs-btn-active-color: var(--wui-primary-text);
|
|
102
|
+
--bs-btn-disabled-color: var(--wui-primary-text);
|
|
92
103
|
}
|
|
93
104
|
|
|
94
105
|
.btn-danger {
|
|
@@ -110,6 +121,56 @@
|
|
|
110
121
|
--bs-btn-disabled-border-color: var(--wui-danger-strong);
|
|
111
122
|
}
|
|
112
123
|
|
|
124
|
+
// Outline variants — tokenized like the solid buttons above so they follow a product's
|
|
125
|
+
// RUNTIME --wui-* brand instead of Bootstrap's compile-time colours. Resting = brand outline
|
|
126
|
+
// on a transparent fill; hover fills with the brand and flips the label to the on-brand text
|
|
127
|
+
// token; active deepens to the pressed fill.
|
|
128
|
+
.btn-outline-primary {
|
|
129
|
+
--bs-btn-color: var(--color-primary);
|
|
130
|
+
--bs-btn-border-color: var(--color-primary);
|
|
131
|
+
--bs-btn-hover-color: var(--wui-primary-text);
|
|
132
|
+
--bs-btn-hover-bg: var(--color-primary);
|
|
133
|
+
--bs-btn-hover-border-color: var(--color-primary);
|
|
134
|
+
--bs-btn-active-color: var(--wui-primary-text);
|
|
135
|
+
--bs-btn-active-bg: var(--wui-primary-pressed);
|
|
136
|
+
--bs-btn-active-border-color: var(--wui-primary-pressed);
|
|
137
|
+
--bs-btn-disabled-color: var(--color-primary);
|
|
138
|
+
--bs-btn-disabled-border-color: var(--color-primary);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Outline danger uses the deepened --wui-danger-strong (5.80:1, carries white) that solid
|
|
142
|
+
// .btn-danger uses, so an outline-danger hover reads identically to solid danger.
|
|
143
|
+
.btn-outline-danger {
|
|
144
|
+
--bs-btn-color: var(--wui-danger-strong);
|
|
145
|
+
--bs-btn-border-color: var(--wui-danger-strong);
|
|
146
|
+
--bs-btn-hover-color: #fff;
|
|
147
|
+
--bs-btn-hover-bg: var(--wui-danger-strong);
|
|
148
|
+
--bs-btn-hover-border-color: var(--wui-danger-strong);
|
|
149
|
+
--bs-btn-active-color: #fff;
|
|
150
|
+
--bs-btn-active-bg: var(--wui-danger-strong-hover);
|
|
151
|
+
--bs-btn-active-border-color: var(--wui-danger-strong-hover);
|
|
152
|
+
--bs-btn-disabled-color: var(--wui-danger-strong);
|
|
153
|
+
--bs-btn-disabled-border-color: var(--wui-danger-strong);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Themed disabled state (fleet default). Bootstrap's own disabled look is just the
|
|
157
|
+
// variant's colour at --bs-btn-disabled-opacity (.65) — barely different from the active
|
|
158
|
+
// button on a tinted brand palette. Give the ordinary case a real desaturated/muted look
|
|
159
|
+
// instead, reusing the same tokens .input:disabled already uses so a disabled button and
|
|
160
|
+
// a disabled field read as the same state.
|
|
161
|
+
//
|
|
162
|
+
// Excludes .btn-primary and .btn-danger, which already theme their OWN disabled state
|
|
163
|
+
// above (a faded version of their own fill, driven by --bs-btn-disabled-*) — :not() keeps
|
|
164
|
+
// this rule from fighting theirs rather than relying on source order.
|
|
165
|
+
.btn:disabled:not(.btn-primary):not(.btn-danger),
|
|
166
|
+
.btn.disabled:not(.btn-primary):not(.btn-danger) {
|
|
167
|
+
background-color: var(--color-bg-muted);
|
|
168
|
+
border-color: var(--color-border);
|
|
169
|
+
color: var(--color-text-subtle);
|
|
170
|
+
opacity: 1;
|
|
171
|
+
cursor: not-allowed;
|
|
172
|
+
}
|
|
173
|
+
|
|
113
174
|
|
|
114
175
|
// ---- Component radii from tokens ------------------------------------
|
|
115
176
|
// .btn/.card/.input capture Bootstrap's compiled $*-border-radius (the library
|
|
@@ -119,6 +180,10 @@
|
|
|
119
180
|
// sm/lg are re-asserted so they keep their own rung instead of the base value.
|
|
120
181
|
.btn {
|
|
121
182
|
--bs-btn-border-radius: var(--wui-radius);
|
|
183
|
+
// Smooth iOS-style corners so buttons match the squircle geometry every other
|
|
184
|
+
// control in the system already uses (inputs, selects, pills). Older browsers
|
|
185
|
+
// fall back to the plain border-radius arc — no breakage.
|
|
186
|
+
corner-shape: squircle;
|
|
122
187
|
}
|
|
123
188
|
.btn-sm,
|
|
124
189
|
.btn-group-sm > .btn {
|
|
@@ -135,6 +200,10 @@
|
|
|
135
200
|
// padding, an icon–label gap, a rounded hover highlight tinted with the brand,
|
|
136
201
|
// and a danger variant so a row-action menu reads as one considered control.
|
|
137
202
|
.dropdown-menu {
|
|
203
|
+
// Elevated surface (like .card), not Bootstrap's default page --bs-body-bg: otherwise the
|
|
204
|
+
// menu blends into a tinted page and, in dark mode, resolves to the page colour instead of
|
|
205
|
+
// the elevated surface.
|
|
206
|
+
--bs-dropdown-bg: var(--color-surface);
|
|
138
207
|
--bs-dropdown-min-width: 12rem;
|
|
139
208
|
--bs-dropdown-padding-x: 0.375rem;
|
|
140
209
|
--bs-dropdown-padding-y: 0.375rem;
|
|
@@ -203,7 +272,7 @@
|
|
|
203
272
|
//
|
|
204
273
|
// Reference values:
|
|
205
274
|
// - height : 48px (h-12 = 3rem)
|
|
206
|
-
// - radius : 16px (
|
|
275
|
+
// - radius : 16px (--wui-radius-xl)
|
|
207
276
|
// - font : 14px / 500 (text-sm font-medium)
|
|
208
277
|
// - padding-x : 20px (px-5) default — Watch demo / outline-dark
|
|
209
278
|
// Primary CTA overrides to 24px (px-6) inline
|
|
@@ -218,7 +287,7 @@
|
|
|
218
287
|
--bs-btn-font-size: 0.875rem; // 14px
|
|
219
288
|
--bs-btn-font-weight: 500;
|
|
220
289
|
--bs-btn-line-height: 1.25;
|
|
221
|
-
--bs-btn-border-radius:
|
|
290
|
+
--bs-btn-border-radius: var(--wui-radius-xl); // 16px
|
|
222
291
|
min-height: 3rem; // 48px (h-12)
|
|
223
292
|
|
|
224
293
|
|
|
@@ -265,6 +334,30 @@
|
|
|
265
334
|
}
|
|
266
335
|
|
|
267
336
|
|
|
337
|
+
// ---- Ghost button (transparent, brand-tinted hover) ------------------
|
|
338
|
+
// A real ghost button: transparent at rest (no fill, no border), current text colour;
|
|
339
|
+
// only the background lights up — a soft brand tint, the same recipe as a
|
|
340
|
+
// .dropdown-item's hover — on hover/active. AppButton's `ghost` variant emits this class
|
|
341
|
+
// on both surfaces; distinct from Bootstrap's .btn-link, which reads as a hyperlink
|
|
342
|
+
// (underlined, link-coloured text) rather than a quiet button. Marketing anchors reach
|
|
343
|
+
// for it directly for the same look on a plain <a>.
|
|
344
|
+
//
|
|
345
|
+
// No .btn-ghost-sm/-lg rungs: unlike .btn-pill (a bespoke marketing sizing system), this
|
|
346
|
+
// is an ordinary .btn variant, so it sizes the same way .btn-primary/.btn-danger do —
|
|
347
|
+
// Bootstrap's own .btn-sm/.btn-lg.
|
|
348
|
+
.btn-ghost {
|
|
349
|
+
--bs-btn-color: var(--color-text);
|
|
350
|
+
--bs-btn-bg: transparent;
|
|
351
|
+
--bs-btn-border-color: transparent;
|
|
352
|
+
--bs-btn-hover-color: var(--color-text);
|
|
353
|
+
--bs-btn-hover-bg: var(--wui-primary-5);
|
|
354
|
+
--bs-btn-hover-border-color: transparent;
|
|
355
|
+
--bs-btn-active-color: var(--color-text);
|
|
356
|
+
--bs-btn-active-bg: var(--wui-primary-10);
|
|
357
|
+
--bs-btn-active-border-color: transparent;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
|
|
268
361
|
// ---- Card "lifted" (shadowless) -------------------------------------
|
|
269
362
|
// Previously a soft drop-shadow card; now flat. The `.card-lifted`
|
|
270
363
|
// class is preserved so existing call sites don't break — it now
|
|
@@ -493,16 +586,21 @@ button.wui-input-leading:focus-visible {
|
|
|
493
586
|
}
|
|
494
587
|
|
|
495
588
|
|
|
496
|
-
// ----
|
|
497
|
-
//
|
|
498
|
-
//
|
|
499
|
-
//
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
border-radius: 16px;
|
|
589
|
+
// ---- Radius step utilities ------------------------------------------
|
|
590
|
+
// Every .btn is already a squircle (see base .btn above), so a button that
|
|
591
|
+
// wants a larger corner only needs a bigger radius — hence .btn-radius-xl,
|
|
592
|
+
// named for what it does (an xl radius step) rather than the corner shape.
|
|
593
|
+
.btn-radius-xl {
|
|
594
|
+
--bs-btn-border-radius: var(--wui-radius-xl); // 16px
|
|
503
595
|
}
|
|
596
|
+
|
|
597
|
+
// Generic (non-button) squircle rounding. Unlike .btn, an arbitrary element
|
|
598
|
+
// gets no corner-shape from the base rules, so this utility sets it itself —
|
|
599
|
+
// otherwise the name would lie and it'd render a plain 32px arc. Older
|
|
600
|
+
// browsers fall back to that arc (Chrome 138+ / Safari 16+ for the squircle).
|
|
504
601
|
.rounded-squircle {
|
|
505
602
|
border-radius: 32px;
|
|
603
|
+
corner-shape: squircle;
|
|
506
604
|
}
|
|
507
605
|
|
|
508
606
|
|
package/scss/_tokens.scss
CHANGED
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
// Easing curves — match locked design's CSS / Framer Motion exports
|
|
52
52
|
--wui-ease-out-quart: cubic-bezier(0.22, 1, 0.36, 1);
|
|
53
53
|
--wui-ease-ui: cubic-bezier(0.32, 0.72, 0, 1);
|
|
54
|
+
// Elastic / spring — overshoots the target then settles (the y > 1 control point).
|
|
55
|
+
// Used by the tab ink-bar so a tab switch springs into place rather than sliding flat.
|
|
56
|
+
--wui-ease-elastic: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
54
57
|
|
|
55
58
|
// Elevation — exposes the Bootstrap $box-shadow* SCSS vars (set in
|
|
56
59
|
// _variables.scss) as CSS vars so inline styles / JS / the portal
|
|
@@ -119,6 +122,15 @@
|
|
|
119
122
|
--wui-container-max: 1512px;
|
|
120
123
|
--wui-container-pad: 24px;
|
|
121
124
|
|
|
125
|
+
// ---- Primary "on-fill" text --------------------------------------
|
|
126
|
+
// Unlike the semantic -text tokens below (a legible STANDALONE colour derived
|
|
127
|
+
// from a fill, for using the status colour AS body text), this is the text/icon
|
|
128
|
+
// colour to use ON TOP OF a solid --wui-primary fill — a selected datepicker day,
|
|
129
|
+
// an active pagination page, a checked segmented option, .btn-primary's label.
|
|
130
|
+
// Default assumes primary is dark enough for white; a brand with a light primary
|
|
131
|
+
// overrides this to a dark value instead.
|
|
132
|
+
--wui-primary-text: #fff;
|
|
133
|
+
|
|
122
134
|
// ---- Semantic status ramps -------------------------------------
|
|
123
135
|
// Canonical info/success/warning/danger scales: `active` (text/icon on
|
|
124
136
|
// light), the base `100` (vivid fills/dots), then tints 50/30/20/10/5.
|
|
@@ -189,6 +201,38 @@
|
|
|
189
201
|
--wui-dark-text-subtle: #8a90a0;
|
|
190
202
|
--wui-dark-primary: #6B83EC;
|
|
191
203
|
--wui-dark-primary-hover:#93A6F2;
|
|
204
|
+
|
|
205
|
+
// ---- Fluid type scale --------------------------------------------
|
|
206
|
+
// clamp(min, preferred, max) so type scales smoothly between viewports instead of
|
|
207
|
+
// stepping at Bootstrap's breakpoints. `base` sits close to Bootstrap's own 1rem body
|
|
208
|
+
// size; every other step is derived at a consistent ratio, up and down. Additive only —
|
|
209
|
+
// nothing in the shared theme consumes these yet, a product opts in per element.
|
|
210
|
+
--wui-fs-xs: clamp(0.75rem, 0.73rem + 0.1vw, 0.8125rem);
|
|
211
|
+
--wui-fs-sm: clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem);
|
|
212
|
+
--wui-fs-base: clamp(0.95rem, 0.9rem + 0.25vw, 1.05rem);
|
|
213
|
+
--wui-fs-lg: clamp(1.05rem, 0.98rem + 0.35vw, 1.25rem);
|
|
214
|
+
--wui-fs-xl: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
|
|
215
|
+
--wui-fs-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem);
|
|
216
|
+
--wui-fs-3xl: clamp(1.875rem, 1.5rem + 1.75vw, 2.75rem);
|
|
217
|
+
|
|
218
|
+
// ---- Display font slot --------------------------------------------
|
|
219
|
+
// A dedicated slot for a display/heading typeface, kept separate from
|
|
220
|
+
// $font-family-base so a product can point ONLY its display headings (.h-display) at a
|
|
221
|
+
// serif or another brand face by overriding this one token, without touching body copy.
|
|
222
|
+
// Defaults to the same stack .font-display already uses.
|
|
223
|
+
--wui-font-display: var(--bs-body-font-family);
|
|
224
|
+
|
|
225
|
+
// ---- Z-index scale -------------------------------------------------
|
|
226
|
+
// Bootstrap's own ladder ($zindex-dropdown etc.), exposed as CSS vars so a component
|
|
227
|
+
// or a consuming product can layer against the same scale instead of inventing values.
|
|
228
|
+
--wui-z-dropdown: 1000;
|
|
229
|
+
--wui-z-sticky: 1020;
|
|
230
|
+
--wui-z-fixed: 1030;
|
|
231
|
+
--wui-z-modal-backdrop: 1040;
|
|
232
|
+
--wui-z-modal: 1050;
|
|
233
|
+
--wui-z-popover: 1060;
|
|
234
|
+
--wui-z-tooltip: 1070;
|
|
235
|
+
--wui-z-toast: 1080;
|
|
192
236
|
}
|
|
193
237
|
|
|
194
238
|
// Bootstrap md / xl (not invented px)
|
package/scss/_variables.scss
CHANGED
|
@@ -11,14 +11,16 @@
|
|
|
11
11
|
// ---- Brand colors ---------------------------------------------------
|
|
12
12
|
// Neutral indigo default. The full ramp lives in _tokens.scss as CSS
|
|
13
13
|
// variables since Bootstrap only consumes the named theme colors.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
$
|
|
17
|
-
$
|
|
18
|
-
$
|
|
19
|
-
$
|
|
20
|
-
$
|
|
21
|
-
$
|
|
14
|
+
// `!default` so a product that sets these in its own _variables BEFORE importing this file
|
|
15
|
+
// (the documented override path) wins — a plain assignment here would clobber the brand.
|
|
16
|
+
$primary: #4F46E5 !default;
|
|
17
|
+
$secondary: #4B5675 !default; // foreground-3 — quiet text + secondary buttons
|
|
18
|
+
$success: #04B440 !default;
|
|
19
|
+
$info: #056EE9 !default;
|
|
20
|
+
$warning: #DFA000 !default;
|
|
21
|
+
$danger: #E82646 !default;
|
|
22
|
+
$light: #F9FBFC !default; // surface-mute
|
|
23
|
+
$dark: #071437 !default; // text-heading
|
|
22
24
|
|
|
23
25
|
// Override `$theme-colors` so Bootstrap's $theme-colors map includes
|
|
24
26
|
// our brand. Anything we add here gets utility classes generated
|
package/scss/_wui-aliases.scss
CHANGED
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
--color-primary: var(--wui-primary);
|
|
37
37
|
--color-primary-hover: var(--wui-primary-pressed);
|
|
38
38
|
--color-primary-soft: var(--wui-primary-10);
|
|
39
|
+
// Text/icon colour for content sitting ON a solid --color-primary fill — see
|
|
40
|
+
// --wui-primary-text for why this is a different axis from the -text tokens below.
|
|
41
|
+
--color-primary-text: var(--wui-primary-text);
|
|
39
42
|
|
|
40
43
|
// Semantic status ramps
|
|
41
44
|
--color-info-active: var(--wui-info-active);
|
package/scss/wui/_choice.scss
CHANGED
|
@@ -415,6 +415,11 @@
|
|
|
415
415
|
color: var(--color-primary);
|
|
416
416
|
font-weight: 500;
|
|
417
417
|
}
|
|
418
|
+
// allow-custom's freeform row — reads as an action ("Add "…"") rather than a listed value.
|
|
419
|
+
.search-select-option-custom {
|
|
420
|
+
font-style: italic;
|
|
421
|
+
color: var(--color-primary);
|
|
422
|
+
}
|
|
418
423
|
.search-select-empty {
|
|
419
424
|
padding: 12px 10px;
|
|
420
425
|
font-size: 13px;
|
|
@@ -627,7 +632,7 @@ button.datepicker-month:hover {
|
|
|
627
632
|
}
|
|
628
633
|
.datepicker-day.is-selected {
|
|
629
634
|
background: var(--color-primary);
|
|
630
|
-
color:
|
|
635
|
+
color: var(--color-primary-text);
|
|
631
636
|
font-weight: 600;
|
|
632
637
|
}
|
|
633
638
|
.datepicker-day:disabled {
|
|
@@ -696,7 +701,7 @@ button.datepicker-month:hover {
|
|
|
696
701
|
}
|
|
697
702
|
.datepicker-cell.is-selected {
|
|
698
703
|
background: var(--color-primary);
|
|
699
|
-
color:
|
|
704
|
+
color: var(--color-primary-text);
|
|
700
705
|
font-weight: 600;
|
|
701
706
|
}
|
|
702
707
|
.datepicker-cell:disabled {
|
|
@@ -842,7 +847,7 @@ button.datepicker-month:hover {
|
|
|
842
847
|
.datepicker-day.is-range-start,
|
|
843
848
|
.datepicker-day.is-range-end {
|
|
844
849
|
background: var(--color-primary);
|
|
845
|
-
color:
|
|
850
|
+
color: var(--color-primary-text);
|
|
846
851
|
font-weight: 600;
|
|
847
852
|
}
|
|
848
853
|
.daterange-hint {
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -348,9 +348,12 @@
|
|
|
348
348
|
resize: none;
|
|
349
349
|
overflow: hidden;
|
|
350
350
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
.
|
|
351
|
+
// The `.error` / `.is-invalid` / `[aria-invalid="true"]` guards keep an invalid field's
|
|
352
|
+
// danger border visible on hover — without them the hover rule repaints it grey and the
|
|
353
|
+
// error signal disappears the moment the cursor passes over the field.
|
|
354
|
+
.input:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
355
|
+
.select:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
356
|
+
.textarea:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error) {
|
|
354
357
|
background-color: var(--color-bg-muted);
|
|
355
358
|
border-color: var(--color-border-strong);
|
|
356
359
|
}
|
|
@@ -360,7 +363,9 @@
|
|
|
360
363
|
outline: none;
|
|
361
364
|
background-color: var(--color-bg-muted);
|
|
362
365
|
color: var(--color-text);
|
|
363
|
-
|
|
366
|
+
// Full-strength brand border is the focus cue (no glow) — the prior 30% tint read at
|
|
367
|
+
// ~1.5:1, below WCAG 2.2 SC 1.4.11's 3:1 for a non-text focus indicator.
|
|
368
|
+
border-color: var(--color-primary);
|
|
364
369
|
box-shadow: none;
|
|
365
370
|
}
|
|
366
371
|
// Textarea renders as .form-control.textarea (react-bootstrap). Match the
|
|
@@ -371,7 +376,9 @@ textarea.textarea.form-control:hover:not(:focus):not(:disabled):not([readonly])
|
|
|
371
376
|
}
|
|
372
377
|
textarea.textarea:focus {
|
|
373
378
|
outline: none;
|
|
374
|
-
|
|
379
|
+
// Full-strength brand border is the focus cue (no glow) — the prior 30% tint read at
|
|
380
|
+
// ~1.5:1, below WCAG 2.2 SC 1.4.11's 3:1 for a non-text focus indicator.
|
|
381
|
+
border-color: var(--color-primary);
|
|
375
382
|
box-shadow: none;
|
|
376
383
|
}
|
|
377
384
|
.input::placeholder,
|
|
@@ -992,6 +999,7 @@ wui-select[compact] {
|
|
|
992
999
|
color: var(--color-success-text);
|
|
993
1000
|
border-color: transparent;
|
|
994
1001
|
}
|
|
1002
|
+
.badge-warning,
|
|
995
1003
|
.badge-warn {
|
|
996
1004
|
background: var(--color-warning-soft);
|
|
997
1005
|
color: var(--color-warning-text);
|
package/scss/wui/_data.scss
CHANGED
|
@@ -407,6 +407,46 @@
|
|
|
407
407
|
cursor: not-allowed;
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
// Numbered mode (mode="numbered") — windowed page buttons sitting between the prev/next
|
|
411
|
+
// arrows in .tpg-nav, same 32px circular hit target as .tpg-arrow.
|
|
412
|
+
.tpg-page {
|
|
413
|
+
display: grid;
|
|
414
|
+
place-items: center;
|
|
415
|
+
min-width: 32px;
|
|
416
|
+
height: 32px;
|
|
417
|
+
padding: 0 6px;
|
|
418
|
+
border: none;
|
|
419
|
+
background: transparent;
|
|
420
|
+
color: var(--color-text-muted);
|
|
421
|
+
border-radius: 50%;
|
|
422
|
+
font-size: 13px;
|
|
423
|
+
font-weight: 600;
|
|
424
|
+
font-variant-numeric: tabular-nums;
|
|
425
|
+
text-decoration: none;
|
|
426
|
+
cursor: pointer;
|
|
427
|
+
transition:
|
|
428
|
+
background 0.14s ease,
|
|
429
|
+
color 0.14s ease;
|
|
430
|
+
}
|
|
431
|
+
.tpg-page:hover:not(.is-active) {
|
|
432
|
+
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
433
|
+
color: var(--color-primary);
|
|
434
|
+
}
|
|
435
|
+
.tpg-page.is-active {
|
|
436
|
+
background: var(--color-primary);
|
|
437
|
+
color: var(--color-primary-text);
|
|
438
|
+
cursor: default;
|
|
439
|
+
}
|
|
440
|
+
.tpg-ellipsis {
|
|
441
|
+
display: grid;
|
|
442
|
+
place-items: center;
|
|
443
|
+
min-width: 24px;
|
|
444
|
+
height: 32px;
|
|
445
|
+
color: var(--color-text-subtle);
|
|
446
|
+
font-size: 13px;
|
|
447
|
+
user-select: none;
|
|
448
|
+
}
|
|
449
|
+
|
|
410
450
|
// Horizontal scroll guard + flush-fit inside a card. Wrapping the table here
|
|
411
451
|
// also stops `.card > .table` header-corner rounding from applying mid-card.
|
|
412
452
|
.table-wrap {
|
package/scss/wui/_display.scss
CHANGED
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
.kpi-label {
|
|
32
32
|
font-size: 13px;
|
|
33
33
|
font-weight: 500;
|
|
34
|
-
|
|
34
|
+
// --color-text-muted (not -subtle): -subtle is ~4.5:1 on white and dips below AA on the
|
|
35
|
+
// filled/accent variant's tinted surface. -muted clears AA on every kpi variant.
|
|
36
|
+
color: var(--color-text-muted);
|
|
35
37
|
}
|
|
36
38
|
.kpi-value {
|
|
37
39
|
margin-top: 6px;
|
|
@@ -76,6 +78,18 @@
|
|
|
76
78
|
margin-top: 2px;
|
|
77
79
|
}
|
|
78
80
|
|
|
81
|
+
// Surface variants — plain (above) is the bordered card; these swap the fill and drop the
|
|
82
|
+
// border so a dashboard can mix a KPI strip with the surrounding card without every tile
|
|
83
|
+
// drawing its own outline.
|
|
84
|
+
.kpi.kpi-filled {
|
|
85
|
+
border-color: transparent;
|
|
86
|
+
background: var(--color-bg-subtle);
|
|
87
|
+
}
|
|
88
|
+
.kpi.kpi-accent {
|
|
89
|
+
border-color: transparent;
|
|
90
|
+
background: var(--color-primary-soft);
|
|
91
|
+
}
|
|
92
|
+
|
|
79
93
|
// A row of tiles. Wraps rather than scrolls — a KPI strip that scrolls hides numbers.
|
|
80
94
|
.kpi-row {
|
|
81
95
|
display: grid;
|
|
@@ -213,11 +227,34 @@ a.info-row:hover .info-value {
|
|
|
213
227
|
.segmented-option:hover {
|
|
214
228
|
color: var(--color-text);
|
|
215
229
|
}
|
|
230
|
+
// A per-option sub-label under the main text — optional, so a segment without one keeps the
|
|
231
|
+
// plain single-line layout above untouched.
|
|
232
|
+
.segmented-option-text {
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
align-items: flex-start;
|
|
236
|
+
gap: 1px;
|
|
237
|
+
line-height: 1.2;
|
|
238
|
+
}
|
|
239
|
+
.segmented-option-label {
|
|
240
|
+
font: inherit;
|
|
241
|
+
color: inherit;
|
|
242
|
+
}
|
|
243
|
+
.segmented-option-note {
|
|
244
|
+
font-size: 10px;
|
|
245
|
+
font-weight: 400;
|
|
246
|
+
// Inherits the option's current text colour (muted, hover, or white-on-checked) rather
|
|
247
|
+
// than a fixed token, so it always matches without a separate state per variant. No opacity
|
|
248
|
+
// muting — that dropped the sub-label below AA on an unchecked option; the 10px size already
|
|
249
|
+
// distinguishes it from the label.
|
|
250
|
+
color: currentColor;
|
|
251
|
+
}
|
|
216
252
|
.segmented-option:has(.segmented-input:checked) {
|
|
217
253
|
background: var(--color-primary);
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
|
|
254
|
+
// Token, not fixed white: the pill is always the brand fill, so its label must
|
|
255
|
+
// contrast with THAT fill, not the page — a light-primary brand overrides
|
|
256
|
+
// --wui-primary-text to a dark value and this follows automatically.
|
|
257
|
+
color: var(--color-primary-text);
|
|
221
258
|
}
|
|
222
259
|
// The ring is drawn on the label because the input it belongs to has no box.
|
|
223
260
|
.segmented-option:has(.segmented-input:focus-visible) {
|
package/scss/wui/_media.scss
CHANGED
|
@@ -494,6 +494,30 @@
|
|
|
494
494
|
.file-preview:hover .file-preview-img {
|
|
495
495
|
transform: scale(1.03);
|
|
496
496
|
}
|
|
497
|
+
// Non-image single file: a centered file-type icon + name filling the same shell as the image
|
|
498
|
+
// thumbnail, so a PDF/doc doesn't render as a broken <img>.
|
|
499
|
+
.file-preview-file {
|
|
500
|
+
display: flex;
|
|
501
|
+
flex-direction: column;
|
|
502
|
+
align-items: center;
|
|
503
|
+
justify-content: center;
|
|
504
|
+
gap: 8px;
|
|
505
|
+
width: 100%;
|
|
506
|
+
height: 100%;
|
|
507
|
+
padding: 16px;
|
|
508
|
+
color: var(--color-text-subtle);
|
|
509
|
+
background: var(--color-bg-muted);
|
|
510
|
+
}
|
|
511
|
+
.file-preview-file-name {
|
|
512
|
+
max-width: 100%;
|
|
513
|
+
overflow: hidden;
|
|
514
|
+
font-size: 13px;
|
|
515
|
+
line-height: 1.4;
|
|
516
|
+
color: var(--color-text);
|
|
517
|
+
text-align: center;
|
|
518
|
+
text-overflow: ellipsis;
|
|
519
|
+
white-space: nowrap;
|
|
520
|
+
}
|
|
497
521
|
.file-preview-overlay {
|
|
498
522
|
position: absolute;
|
|
499
523
|
inset: 0;
|