@webority/theme 0.7.4 → 0.7.6

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.
@@ -0,0 +1,337 @@
1
+ // =====================================================================
2
+ // Webority UI — marketing / editorial surface helpers (OPT-IN).
3
+ // Not part of the default theme chain. Import AFTER core theme when a
4
+ // marketing site needs section rhythm, hero chrome, pill CTAs, or scroll
5
+ // motion — portals should omit this file.
6
+ //
7
+ // @import "@webority/theme/scss/components";
8
+ // @import "@webority/theme/scss/wui-components";
9
+ // @import "@webority/theme/scss/marketing";
10
+ // =====================================================================
11
+
12
+ // ---- Section helpers ------------------------------------------------
13
+ // Marketing + portal both use vertically-padded sections with the
14
+ // brand's spacing rhythm. Bootstrap's py-* gets you part-way; these
15
+ // are the named variants the design system reaches for.
16
+ .section-y-sm { padding-block: 3rem; }
17
+ .section-y { padding-block: 5rem; }
18
+ .section-y-lg { padding-block: 7rem; }
19
+ @media (min-width: 768px) {
20
+ .section-y-sm { padding-block: 4rem; }
21
+ .section-y { padding-block: 6rem; }
22
+ .section-y-lg { padding-block: 9rem; }
23
+ }
24
+
25
+ // ---- Eyebrow (accent-dot + label) -----------------------------------
26
+ .eyebrow {
27
+ display: inline-flex;
28
+ align-items: center;
29
+ gap: 0.5rem;
30
+ font-size: 14px;
31
+ font-weight: 700;
32
+ line-height: 1;
33
+ color: var(--wui-primary);
34
+ }
35
+ .eyebrow::before {
36
+ content: "";
37
+ width: 0.5rem;
38
+ height: 0.5rem;
39
+ border-radius: 9999px;
40
+ background: var(--wui-primary);
41
+ }
42
+ .eyebrow--on-dark {
43
+ color: rgba(255, 255, 255, 0.85);
44
+ }
45
+ .eyebrow--on-dark::before {
46
+ background: #FFFFFF;
47
+ }
48
+
49
+ // ---- Display heading -------------------------------------------------
50
+ // Bootstrap's .display-1..6 ladder skews bigger than our locked design.
51
+ // .h-display drops in where the marketing hero needs the locked
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).
55
+ .h-display {
56
+ font-family: var(--wui-font-display, var(--bs-body-font-family));
57
+ font-weight: 500;
58
+ letter-spacing: -0.03em;
59
+ line-height: 1.05;
60
+ }
61
+
62
+ // ---- Gradient backgrounds -------------------------------------------
63
+ .bg-hero-gradient { background: var(--wui-hero-gradient); }
64
+ .bg-cta-gradient { background: var(--wui-cta-gradient); }
65
+
66
+ // ---- Marketing pill buttons (locked design parity) -------------------
67
+ // Pixel-locked to the standalone reference's `btn-squircle inline-flex
68
+ // h-12 items-center text-sm font-medium` pattern. Each marketing CTA
69
+ // layers .btn-pill on a Bootstrap variant — these classes only override
70
+ // Bootstrap's btn CSS vars (height / padding / radius / font), so
71
+ // every variant inherits the right size without redefining colors.
72
+ //
73
+ // Reference values:
74
+ // - height : 48px (h-12 = 3rem)
75
+ // - radius : 16px (--wui-radius-xl)
76
+ // - font : 14px / 500 (text-sm font-medium)
77
+ // - padding-x : 20px (px-5) default — Watch demo / outline-dark
78
+ // Primary CTA overrides to 24px (px-6) inline
79
+ // - corner-shape : squircle (iOS-style smooth curve)
80
+ //
81
+ // Example:
82
+ // <a class="btn btn-primary btn-pill" style="--bs-btn-padding-x:1.5rem;">…</a>
83
+ // <a class="btn btn-light btn-pill-sm">Learn more</a>
84
+ .btn-pill {
85
+ --bs-btn-padding-y: 0; // height driven by min-height + flex centering
86
+ --bs-btn-padding-x: 1.25rem; // 20px (px-5)
87
+ --bs-btn-font-size: 0.875rem; // 14px
88
+ --bs-btn-font-weight: 500;
89
+ --bs-btn-line-height: 1.25;
90
+ --bs-btn-border-radius: var(--wui-radius-xl); // 16px
91
+ min-height: 3rem; // 48px (h-12)
92
+
93
+
94
+ display: inline-flex;
95
+ align-items: center;
96
+ gap: 0.375rem; // 6px — matches primary arrow's ml-1.5
97
+ }
98
+ .btn-pill-sm {
99
+ --bs-btn-padding-y: 0;
100
+ --bs-btn-padding-x: 0.875rem; // 14px (px-3.5)
101
+ --bs-btn-font-size: 0.875rem; // 14px (text-sm)
102
+ --bs-btn-font-weight: 500;
103
+ --bs-btn-line-height: 1.25;
104
+ --bs-btn-border-radius: 12px;
105
+ min-height: 2.5rem; // 40px (h-10) — reference "Learn more" CTA
106
+
107
+
108
+ display: inline-flex;
109
+ align-items: center;
110
+ gap: 0.375rem;
111
+ }
112
+
113
+ // ---- Ghost-dark button (frosted glass on light surface) -------------
114
+ // Pixel-locked to the reference's `WatchDemoButton` "outline-dark" variant
115
+ // (src/components/marketing/brand/watch-demo-button.tsx):
116
+ // - border : rgba(13, 24, 43, 0.15) (foreground/15 in marketing-root)
117
+ // - bg : rgba(255, 255, 255, 0.6) (surface/60)
118
+ // - text : --wui-text-heading (text-foreground in marketing-root)
119
+ // - blur : 4px (backdrop-blur-sm)
120
+ // - hover bg: #ffffff (hover:bg-surface = full opacity)
121
+ .btn-ghost-dark {
122
+ --bs-btn-color: var(--wui-text-heading);
123
+ --bs-btn-bg: rgba(255, 255, 255, 0.60);
124
+ --bs-btn-border-color: rgba(13, 24, 43, 0.15);
125
+ --bs-btn-hover-color: var(--wui-text-heading);
126
+ --bs-btn-hover-bg: #ffffff;
127
+ --bs-btn-hover-border-color: rgba(13, 24, 43, 0.20);
128
+ --bs-btn-active-color: var(--wui-text-heading);
129
+ --bs-btn-active-bg: #ffffff;
130
+ --bs-btn-active-border-color: rgba(13, 24, 43, 0.20);
131
+ backdrop-filter: blur(4px);
132
+ -webkit-backdrop-filter: blur(4px);
133
+ }
134
+
135
+ // ---- Link with the marketing wipe underline (matches locked design) -
136
+ // Uses background-image (not a pseudo-element with inline-block) so the link
137
+ // stays inline. inline-block + overflow:hidden used to give the link its own
138
+ // baseline, which dropped it below surrounding prose whenever the parent had
139
+ // a non-default font-size (see Pricing footnote "Talk to sales" regression).
140
+
141
+ .link-wipe {
142
+ background-image: linear-gradient(currentColor, currentColor);
143
+ background-repeat: no-repeat;
144
+ background-position-x: 100%;
145
+ background-position-y: 100%;
146
+ background-size: 0 1px;
147
+ transition: background-size 500ms cubic-bezier(0.4, 0, 0.2, 1);
148
+ }
149
+ @media (hover: hover) {
150
+ .link-wipe:hover {
151
+ background-position-x: 0%;
152
+ background-size: 100% 1px;
153
+ }
154
+ }
155
+ .link-wipe--accent {
156
+ background-image: linear-gradient(var(--wui-primary), var(--wui-primary));
157
+ }
158
+
159
+ // ---- Container with the marketing 1512px canvas ---------------------
160
+ // Pair with --wui-container-pad (responsive). Distinct from Bootstrap's
161
+ // .container (max-width steps at 540/720/960/1140/1320). Use this when a
162
+ // section needs to breathe up to 1512px on wide monitors.
163
+ .container-cloves {
164
+ max-width: var(--wui-container-max, 1512px);
165
+ margin-inline: auto;
166
+ padding-inline: var(--wui-container-pad, 24px);
167
+ width: 100%;
168
+ }
169
+
170
+ // ---- Hero radial wash ------------------------------------------------
171
+ // Soft tinted glow over an elevated surface — sits behind hero copy.
172
+ .hero-wash {
173
+ background:
174
+ radial-gradient(80% 50% at 50% 0%,
175
+ color-mix(in srgb, var(--wui-primary) 8%, transparent), transparent 70%),
176
+ var(--wui-bg-elevated);
177
+ }
178
+
179
+ // ---- Page entry fade -------------------------------------------------
180
+ // Apply to <main> in _Layout so every page entry has a polite fade-up.
181
+ .page-fade-in {
182
+ animation: pageFadeIn 0.25s var(--wui-ease-ui) backwards;
183
+ }
184
+
185
+ // ---- Single-element fade-in ------------------------------------------
186
+ .fade-in {
187
+ animation: fadeIn 220ms var(--wui-ease-out-quart);
188
+ }
189
+
190
+ // ---- Stagger groups --------------------------------------------------
191
+ // Wrap a container in `.stagger-children` and its direct children
192
+ // cascade in 40ms apart, capped at 12 children. For bigger groups,
193
+ // use the IntersectionObserver-driven `[data-reveal]` pattern below.
194
+ .stagger-children > * {
195
+ animation: staggerRise 360ms var(--wui-ease-out-quart) backwards;
196
+ }
197
+ .stagger-children > *:nth-child(1) { animation-delay: 0ms; }
198
+ .stagger-children > *:nth-child(2) { animation-delay: 40ms; }
199
+ .stagger-children > *:nth-child(3) { animation-delay: 80ms; }
200
+ .stagger-children > *:nth-child(4) { animation-delay: 120ms; }
201
+ .stagger-children > *:nth-child(5) { animation-delay: 160ms; }
202
+ .stagger-children > *:nth-child(6) { animation-delay: 200ms; }
203
+ .stagger-children > *:nth-child(7) { animation-delay: 240ms; }
204
+ .stagger-children > *:nth-child(8) { animation-delay: 280ms; }
205
+ .stagger-children > *:nth-child(9) { animation-delay: 320ms; }
206
+ .stagger-children > *:nth-child(10) { animation-delay: 360ms; }
207
+ .stagger-children > *:nth-child(11) { animation-delay: 400ms; }
208
+ .stagger-children > *:nth-child(12) { animation-delay: 440ms; }
209
+
210
+ // ---- Opt-in single-element stagger -----------------------------------
211
+ // Use when you need granular control: set `--stagger: 240ms` inline on
212
+ // individual items inside a non-stagger-children container.
213
+ .stagger-item {
214
+ opacity: 0;
215
+ animation: rise 320ms var(--wui-ease-out-quart) forwards;
216
+ animation-delay: var(--stagger, 0ms);
217
+ }
218
+
219
+ // ---- Marquee (customer-logo carousel etc.) ---------------------------
220
+ // Pair with a flex wrapper that duplicates its children so the
221
+ // translateX(-50%) loops seamlessly. Example:
222
+ // <div class="overflow-hidden">
223
+ // <div class="d-flex animate-marquee" style="width: max-content;">
224
+ // <ul class="d-flex gap-5 mb-0 list-unstyled">...</ul>
225
+ // <ul class="d-flex gap-5 mb-0 list-unstyled" aria-hidden="true">...</ul>
226
+ // </div>
227
+ // </div>
228
+ .animate-marquee {
229
+ animation: marketing-marquee 32s linear infinite;
230
+ }
231
+
232
+ // ---- Scroll-triggered reveals (paired with wwwroot/js/reveal.js) -----
233
+ // Author opts in by adding `data-reveal` to a section. The JS module
234
+ // adds `.is-visible` when the element scrolls into view; CSS transitions
235
+ // fade + lift on the transition. Stagger descendants via
236
+ // `[data-reveal-stagger] > *` with `--reveal-step` inline (default 40ms).
237
+ [data-reveal] {
238
+ opacity: 0;
239
+ transform: translateY(8px);
240
+ transition:
241
+ opacity 540ms var(--wui-ease-out-quart),
242
+ transform 540ms var(--wui-ease-out-quart);
243
+ will-change: opacity, transform;
244
+ }
245
+ [data-reveal].is-visible {
246
+ opacity: 1;
247
+ transform: none;
248
+ }
249
+
250
+ [data-reveal-stagger] > * {
251
+ opacity: 0;
252
+ transform: translateY(8px);
253
+ transition:
254
+ opacity 540ms var(--wui-ease-out-quart),
255
+ transform 540ms var(--wui-ease-out-quart);
256
+ }
257
+ [data-reveal-stagger].is-visible > * {
258
+ opacity: 1;
259
+ transform: none;
260
+ }
261
+ [data-reveal-stagger].is-visible > *:nth-child(1) { transition-delay: calc(var(--reveal-step, 40ms) * 0); }
262
+ [data-reveal-stagger].is-visible > *:nth-child(2) { transition-delay: calc(var(--reveal-step, 40ms) * 1); }
263
+ [data-reveal-stagger].is-visible > *:nth-child(3) { transition-delay: calc(var(--reveal-step, 40ms) * 2); }
264
+ [data-reveal-stagger].is-visible > *:nth-child(4) { transition-delay: calc(var(--reveal-step, 40ms) * 3); }
265
+ [data-reveal-stagger].is-visible > *:nth-child(5) { transition-delay: calc(var(--reveal-step, 40ms) * 4); }
266
+ [data-reveal-stagger].is-visible > *:nth-child(6) { transition-delay: calc(var(--reveal-step, 40ms) * 5); }
267
+ [data-reveal-stagger].is-visible > *:nth-child(7) { transition-delay: calc(var(--reveal-step, 40ms) * 6); }
268
+ [data-reveal-stagger].is-visible > *:nth-child(8) { transition-delay: calc(var(--reveal-step, 40ms) * 7); }
269
+ [data-reveal-stagger].is-visible > *:nth-child(9) { transition-delay: calc(var(--reveal-step, 40ms) * 8); }
270
+ [data-reveal-stagger].is-visible > *:nth-child(10) { transition-delay: calc(var(--reveal-step, 40ms) * 9); }
271
+ [data-reveal-stagger].is-visible > *:nth-child(11) { transition-delay: calc(var(--reveal-step, 40ms) * 10); }
272
+ [data-reveal-stagger].is-visible > *:nth-child(12) { transition-delay: calc(var(--reveal-step, 40ms) * 11); }
273
+
274
+ // ---- Section content-visibility (marketing long pages) ----
275
+ // Opt-in long marketing pages: skip layout/paint for offscreen sections.
276
+ // Do not apply to sticky hosts or the first/above-fold section.
277
+ [data-section]:not(:first-of-type):not([data-section="inner-hero"]):not([data-section="home-hero"]):not([data-section-skip-cv]),
278
+ .section-y:not(:first-of-type):not([data-section-skip-cv]),
279
+ .section-y-sm:not(:first-of-type):not([data-section-skip-cv]),
280
+ .section-y-lg:not(:first-of-type):not([data-section-skip-cv]) {
281
+ content-visibility: auto;
282
+ contain-intrinsic-size: auto 720px;
283
+ }
284
+
285
+ // ---- Marketing motion keyframes ------------------------------------
286
+ @keyframes fadeIn {
287
+ from { opacity: 0; transform: translateY(4px); }
288
+ to { opacity: 1; transform: translateY(0); }
289
+ }
290
+ @keyframes pageFadeIn {
291
+ from { opacity: 0; transform: translateY(6px); }
292
+ to { opacity: 1; transform: translateY(0); }
293
+ }
294
+ @keyframes rise {
295
+ from { opacity: 0; transform: translateY(8px); }
296
+ to { opacity: 1; transform: translateY(0); }
297
+ }
298
+ @keyframes staggerRise {
299
+ from { opacity: 0; transform: translateY(8px); }
300
+ to { opacity: 1; transform: translateY(0); }
301
+ }
302
+ @keyframes marketing-marquee {
303
+ from { transform: translateX(0); }
304
+ to { transform: translateX(-50%); }
305
+ }
306
+
307
+ // GPU / reduced-motion for marketing-only selectors
308
+ .btn-pill,
309
+ .btn-pill-sm,
310
+ [data-reveal],
311
+ [data-reveal-stagger] > * {
312
+ will-change: auto;
313
+ }
314
+ .btn-pill:hover,
315
+ .btn-pill-sm:hover,
316
+ [data-reveal]:not(.is-visible),
317
+ [data-reveal-stagger]:not(.is-visible) > * {
318
+ will-change: transform, opacity;
319
+ }
320
+
321
+ @media (prefers-reduced-motion: reduce) {
322
+ .link-wipe {
323
+ transition: none;
324
+ }
325
+ .page-fade-in,
326
+ .fade-in,
327
+ .stagger-children > *,
328
+ .stagger-item,
329
+ .animate-marquee,
330
+ [data-reveal],
331
+ [data-reveal-stagger] > * {
332
+ animation: none;
333
+ opacity: 1;
334
+ transform: none;
335
+ transition: none;
336
+ }
337
+ }
package/scss/_tokens.scss CHANGED
@@ -8,37 +8,37 @@
8
8
  :root {
9
9
  // Extended primary ramp — neutral indigo default; used for tints,
10
10
  // washes, hovers. Products override --wui-primary* with their brand.
11
- --wui-primary: #4F46E5;
12
- --wui-primary-hover: #6366F1;
13
- --wui-primary-pressed: #4338CA;
14
- --wui-primary-50: #818CF8;
15
- --wui-primary-30: #A5B4FC;
16
- --wui-primary-20: #C7D2FE;
17
- --wui-primary-15: #DDE1FD;
18
- --wui-primary-10: #E0E7FF;
19
- --wui-primary-5: #EEF2FF;
11
+ --wui-primary: #4F46E5;
12
+ --wui-primary-hover: #6366F1;
13
+ --wui-primary-pressed: #4338CA;
14
+ --wui-primary-50: #818CF8;
15
+ --wui-primary-30: #A5B4FC;
16
+ --wui-primary-20: #C7D2FE;
17
+ --wui-primary-15: #DDE1FD;
18
+ --wui-primary-10: #E0E7FF;
19
+ --wui-primary-5: #EEF2FF;
20
20
 
21
21
  // Foreground ramp (cool blue-grey neutrals) — for text hierarchy
22
22
  // beyond Bootstrap's body-color / body-secondary-color.
23
23
  --wui-text-heading: #071437;
24
- --wui-text-1: #252F4A;
25
- --wui-text-2: #4B5675;
24
+ --wui-text-1: #252F4A;
25
+ --wui-text-2: #4B5675;
26
26
  // Tertiary text — hints, captions, labels, timestamps, placeholders. Was #78829D,
27
27
  // which measured 3.5:1 on white and 3.1:1 on a tinted panel: below WCAG AA (4.5:1)
28
28
  // everywhere it was used. A tier meant for readable-but-secondary copy has to BE
29
29
  // readable, so it darkens to 4.55:1 on every neutral surface. It stays clearly
30
30
  // lighter than --wui-text-2; --wui-text-4/5 remain for genuinely decorative use.
31
- --wui-text-3: #687088;
32
- --wui-text-4: #99A1B7;
33
- --wui-text-5: #C4CADA;
31
+ --wui-text-3: #687088;
32
+ --wui-text-4: #99A1B7;
33
+ --wui-text-5: #C4CADA;
34
34
 
35
35
  // Surfaces
36
- --wui-bg-page: #F4F6F8;
37
- --wui-bg-light: #FFFFFF;
38
- --wui-bg-elevated: #F9FBFC;
39
- --wui-bg-muted: #F5F6FA;
36
+ --wui-bg-page: #F4F6F8;
37
+ --wui-bg-light: #FFFFFF;
38
+ --wui-bg-elevated: #F9FBFC;
39
+ --wui-bg-muted: #F5F6FA;
40
40
  --wui-border-light: #EBEDF3;
41
- --wui-border-soft: #F4F6F8;
41
+ --wui-border-soft: #F4F6F8;
42
42
 
43
43
  // Gradients (hero, CTA)
44
44
  --wui-hero-gradient:
@@ -50,18 +50,18 @@
50
50
 
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
- --wui-ease-ui: cubic-bezier(0.32, 0.72, 0, 1);
53
+ --wui-ease-ui: cubic-bezier(0.32, 0.72, 0, 1);
54
54
  // Elastic / spring — overshoots the target then settles (the y > 1 control point).
55
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);
56
+ --wui-ease-elastic: cubic-bezier(0.34, 1.56, 0.64, 1);
57
57
 
58
- // Elevation (TASK-06080). In-page card tokens are flat (none). Overlay
58
+ // Elevation. In-page card tokens are flat (none). Overlay
59
59
  // rungs match $wui-shadow-overlay* in _variables.scss — one ink for menus,
60
60
  // popovers, modals. Do not hand-roll a third shadow.
61
- --wui-shadow-card: none;
62
- --wui-shadow-card-lifted: none;
63
- --wui-shadow-card-lifted-hover: none;
64
- --wui-shadow-overlay: #{$wui-shadow-overlay};
61
+ --wui-shadow-card: none;
62
+ --wui-shadow-card-lifted: none;
63
+ --wui-shadow-card-lifted-hover: none;
64
+ --wui-shadow-overlay: #{$wui-shadow-overlay};
65
65
  --wui-shadow-overlay-lg: #{$wui-shadow-overlay-lg};
66
66
 
67
67
  // One height for every form control. Buttons, inputs and selects share form
@@ -74,19 +74,23 @@
74
74
  --wui-control-height-lg: 52px;
75
75
 
76
76
  // Scrollbar thumb — one quiet grey-blue at three interaction strengths.
77
- --wui-scrollbar: rgba(145, 158, 171, 0.45);
78
- --wui-scrollbar-hover: rgba(145, 158, 171, 0.65);
77
+ --wui-scrollbar: rgba(145, 158, 171, 0.45);
78
+ --wui-scrollbar-hover: rgba(145, 158, 171, 0.65);
79
79
  --wui-scrollbar-active: rgba(145, 158, 171, 0.85);
80
80
 
81
81
  // ---- Marketing surface ramp -------------------------------------
82
82
  // Soft tints + neutral washes used by mega-menu hover, callout cards,
83
83
  // and section accents. Distinct from --wui-bg-* (which are the
84
84
  // platform/portal palette).
85
- --wui-surface-soft: #F2EFFE;
86
- --wui-surface-blue: #ECE4FE;
87
- --wui-surface-blue-soft: #FCFCFC;
88
- --wui-surface-mute: #F9FBFC;
89
- --wui-ink: #000000;
85
+ --wui-surface-soft: #F2EFFE;
86
+ --wui-surface-violet: #ECE4FE;
87
+ --wui-surface-violet-soft: #FCFCFC;
88
+ // Deprecated aliases — the value was always lavender, never blue. Published
89
+ // names, so consumers migrate on their own bump; removed in a future major.
90
+ --wui-surface-blue: var(--wui-surface-violet);
91
+ --wui-surface-blue-soft: var(--wui-surface-violet-soft);
92
+ --wui-surface-mute: #F9FBFC;
93
+ --wui-ink: #000000;
90
94
 
91
95
  // ---- Foreground ramp aliases -----------------------------------
92
96
  // Marketing-side parlance — names mirror the standalone design.
@@ -100,10 +104,10 @@
100
104
 
101
105
  // ---- Neutral ramp ----------------------------------------------
102
106
  // Used by table emphasis, dividers, and mega-menu hover surfaces.
103
- --wui-neutral-border-dark: #EBEDF3;
107
+ --wui-neutral-border-dark: #EBEDF3;
104
108
  --wui-neutral-border-light: #F9FBFC;
105
- --wui-neutral-bg-dark: #F5F6FA;
106
- --wui-neutral-bg-light: #F9FBFC;
109
+ --wui-neutral-bg-dark: #F5F6FA;
110
+ --wui-neutral-bg-light: #F9FBFC;
107
111
 
108
112
  // ---- Accent-dark -----------------------------------------------
109
113
  // Darker primary for pressed/hover accents in CSS (not SCSS $link-hover).
@@ -130,71 +134,76 @@
130
134
  // Canonical info/success/warning/danger scales: `active` (text/icon on
131
135
  // light), the base `100` (vivid fills/dots), then tints 50/30/20/10/5.
132
136
  // Shared by the portal (aliased to --color-*) and marketing alerts/badges.
133
- --wui-info-text: #1460ba;
134
- --wui-info-active: #056EE9;
135
- --wui-info: #1B84FF;
136
- --wui-info-50: #8DC2FF;
137
- --wui-info-30: #BBDBFF;
138
- --wui-info-20: #D1E6FF;
139
- --wui-info-10: #E9F3FF;
140
- --wui-info-5: #F3F9FF;
137
+ --wui-info-text: #1460ba;
138
+ --wui-info-active: #056EE9;
139
+ --wui-info: #1B84FF;
140
+ --wui-info-50: #8DC2FF;
141
+ --wui-info-30: #BBDBFF;
142
+ --wui-info-20: #D1E6FF;
143
+ --wui-info-10: #E9F3FF;
144
+ --wui-info-5: #F3F9FF;
141
145
 
142
146
  // `-text` variants exist because the status colours are FILLS. #F8285A and #04B440
143
147
  // are correct on a button or a badge, and both fail AA as text on a light surface
144
148
  // (3.6:1 and 2.6:1) — which is how a validation error message, of all things, came
145
149
  // to be unreadable. Darkening the base tokens would change every red button and
146
150
  // green badge in the system, so text gets its own value and the fills keep theirs.
147
- --wui-success-text: #0e7531;
151
+ --wui-success-text: #0e7531;
152
+ // Deepened success fill for surfaces that carry WHITE text (progress-bar labels), mirroring
153
+ // --wui-danger-strong below. The brighter --wui-success/-active are far too light for white
154
+ // (2.78:1 / 2.78:1, under AA); this clears it (#0e7531 → 5.86:1). Keep white-on-success off
155
+ // the other tokens so buttons/badges keep the vibrant fill.
156
+ --wui-success-strong: #0e7531;
148
157
  --wui-success-active: #04B440;
149
- --wui-success: #17C653;
150
- --wui-success-50: #8BE3A9;
151
- --wui-success-30: #BAEECC;
152
- --wui-success-20: #D1F4DD;
153
- --wui-success-10: #E8FAEE;
154
- --wui-success-5: #F3FCF6;
155
-
156
- --wui-warning-text: #876100;
158
+ --wui-success: #17C653;
159
+ --wui-success-50: #8BE3A9;
160
+ --wui-success-30: #BAEECC;
161
+ --wui-success-20: #D1F4DD;
162
+ --wui-success-10: #E8FAEE;
163
+ --wui-success-5: #F3FCF6;
164
+
165
+ --wui-warning-text: #876100;
157
166
  --wui-warning-active: #DFA000;
158
- --wui-warning: #F6B100;
159
- --wui-warning-50: #FBD880;
160
- --wui-warning-30: #FDE8B3;
161
- --wui-warning-20: #FDEFCC;
162
- --wui-warning-10: #FFF8E6;
163
- --wui-warning-5: #FEFBF2;
164
-
165
- --wui-danger-text: #b61d42;
167
+ --wui-warning: #F6B100;
168
+ --wui-warning-50: #FBD880;
169
+ --wui-warning-30: #FDE8B3;
170
+ --wui-warning-20: #FDEFCC;
171
+ --wui-warning-10: #FFF8E6;
172
+ --wui-warning-5: #FEFBF2;
173
+
174
+ --wui-danger-text: #b61d42;
166
175
  // Deepened danger fill for surfaces that carry WHITE text (btn-danger). The brighter
167
176
  // --wui-danger/-active below are too light for white (3.86:1 / 4.38:1, under AA); these
168
177
  // clear it — #C81340 → 5.80:1, hover → higher. Keep white-on-danger off the other tokens.
169
- --wui-danger-strong: #C81340;
178
+ --wui-danger-strong: #C81340;
170
179
  --wui-danger-strong-hover: #AB1036;
171
- --wui-danger-active: #E82646;
172
- --wui-danger: #F8285A;
173
- --wui-danger-50: #FC94AD;
174
- --wui-danger-30: #FDBFCE;
175
- --wui-danger-20: #FED4DE;
176
- --wui-danger-10: #FFEAEF;
177
- --wui-danger-5: #FEF4F6;
180
+ --wui-danger-active: #E82646;
181
+ --wui-danger: #F8285A;
182
+ --wui-danger-50: #FC94AD;
183
+ --wui-danger-30: #FDBFCE;
184
+ --wui-danger-20: #FED4DE;
185
+ --wui-danger-10: #FFEAEF;
186
+ --wui-danger-5: #FEF4F6;
178
187
 
179
188
  // Corner radii (DESIGN.md scale) — portal aliases as --radius-*
180
189
  --wui-radius-sm: 6px;
181
- --wui-radius: 8px;
190
+ --wui-radius: 8px;
182
191
  --wui-radius-lg: 12px;
183
192
  --wui-radius-xl: 16px;
184
193
 
185
194
  // ---- Portal dark palette ----------------------------------------
186
195
  // Marketing stays light. Portal flips [data-theme="dark"] and maps
187
196
  // --color-* aliases onto these so dark hexes live in theme/, not the app.
188
- --wui-dark-bg-page: #0f1117;
189
- --wui-dark-bg-elevated: #0d0f15;
190
- --wui-dark-bg-muted: #232733;
191
- --wui-dark-bg-surface: #181b23;
192
- --wui-dark-border: #2a2f3a;
197
+ --wui-dark-bg-page: #0f1117;
198
+ --wui-dark-bg-elevated: #0d0f15;
199
+ --wui-dark-bg-muted: #232733;
200
+ --wui-dark-bg-surface: #181b23;
201
+ --wui-dark-border: #2a2f3a;
193
202
  --wui-dark-border-strong:#3a414f;
194
- --wui-dark-text: #e6e8ee;
195
- --wui-dark-text-muted: #b4b9c6;
196
- --wui-dark-text-subtle: #8a90a0;
197
- --wui-dark-primary: #6B83EC;
203
+ --wui-dark-text: #e6e8ee;
204
+ --wui-dark-text-muted: #b4b9c6;
205
+ --wui-dark-text-subtle: #8a90a0;
206
+ --wui-dark-primary: #6B83EC;
198
207
  --wui-dark-primary-hover:#93A6F2;
199
208
 
200
209
  // ---- Fluid type scale --------------------------------------------
@@ -202,13 +211,17 @@
202
211
  // stepping at Bootstrap's breakpoints. `base` sits close to Bootstrap's own 1rem body
203
212
  // size; every other step is derived at a consistent ratio, up and down. Additive only —
204
213
  // nothing in the shared theme consumes these yet, a product opts in per element.
205
- --wui-fs-xs: clamp(0.75rem, 0.73rem + 0.1vw, 0.8125rem);
206
- --wui-fs-sm: clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem);
207
- --wui-fs-base: clamp(0.95rem, 0.9rem + 0.25vw, 1.05rem);
208
- --wui-fs-lg: clamp(1.05rem, 0.98rem + 0.35vw, 1.25rem);
209
- --wui-fs-xl: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
210
- --wui-fs-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem);
211
- --wui-fs-3xl: clamp(1.875rem, 1.5rem + 1.75vw, 2.75rem);
214
+ --wui-fs-xs: clamp(0.75rem, 0.73rem + 0.1vw, 0.8125rem);
215
+ --wui-fs-sm: clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem);
216
+ --wui-fs-base: clamp(0.95rem, 0.9rem + 0.25vw, 1.05rem);
217
+ --wui-fs-lg: clamp(1.05rem, 0.98rem + 0.35vw, 1.25rem);
218
+ --wui-fs-xl: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
219
+ --wui-fs-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem);
220
+ --wui-fs-3xl: clamp(1.875rem, 1.5rem + 1.75vw, 2.75rem);
221
+
222
+ // ---- Font stacks ---------------------------------------------------
223
+ // Sans-serif body font (Bootstrap's system stack or product override).
224
+ --font-sans: var(--bs-body-font-family);
212
225
 
213
226
  // ---- Display font slot --------------------------------------------
214
227
  // A dedicated slot for a display/heading typeface, kept separate from
@@ -226,19 +239,19 @@
226
239
  // elevated to 1500+ so the shell never covers a dialog. Portalled menus
227
240
  // (select / date / phone / infotip) sit at 1600 so a combobox inside a modal
228
241
  // is not trapped under the dialog. Never invent a third number in component CSS —
229
- // pick a rung from this list (TASK-06078).
230
- --wui-z-dropdown: 1000;
231
- --wui-z-sticky: 1020;
232
- --wui-z-fixed: 1030;
233
- --wui-z-shell: 1040; // splash, shell base chrome
234
- --wui-z-shell-scrim: 1044; // mobile drawer scrim
235
- --wui-z-shell-drawer: 1045; // mobile sidebar drawer
242
+ // pick a rung from this list.
243
+ --wui-z-dropdown: 1000;
244
+ --wui-z-sticky: 1020;
245
+ --wui-z-fixed: 1030;
246
+ --wui-z-shell: 1040; // splash, shell base chrome
247
+ --wui-z-shell-scrim: 1044; // mobile drawer scrim
248
+ --wui-z-shell-drawer: 1045; // mobile sidebar drawer
236
249
  --wui-z-modal-backdrop: 1500;
237
- --wui-z-modal: 1505;
238
- --wui-z-menu: 1600; // portalled select/date/phone menus + infotip
239
- --wui-z-popover: 1600; // same rung as --wui-z-menu (above modal)
240
- --wui-z-tooltip: 1650;
241
- --wui-z-toast: 1700;
250
+ --wui-z-modal: 1505;
251
+ --wui-z-menu: 1600; // portalled select/date/phone menus + infotip
252
+ --wui-z-popover: 1600; // same rung as --wui-z-menu (above modal)
253
+ --wui-z-tooltip: 1650;
254
+ --wui-z-toast: 1700;
242
255
  }
243
256
 
244
257
  // Bootstrap md / xl (not invented px)