@webority/theme 0.7.3 → 0.7.5
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 +108 -6
- package/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_components.scss +158 -547
- package/scss/_marketing.scss +337 -0
- package/scss/_tokens.scss +119 -108
- package/scss/_variables.scss +126 -132
- package/scss/_wui-aliases.scss +103 -102
- package/scss/wui/_choice.scss +4 -4
- package/scss/wui/_controls.scss +167 -251
- package/scss/wui/_data.scss +39 -0
- package/scss/wui/_display.scss +88 -0
- package/scss/wui/_navigation.scss +27 -0
- package/scss/wui/_overlays.scss +2 -2
- package/scss/wui/_scrollbar.scss +13 -11
- package/scss/wui/_shell.scss +3 -3
- package/scss/wui/_utilities.scss +139 -1
|
@@ -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:
|
|
12
|
-
--wui-primary-hover:
|
|
13
|
-
--wui-primary-pressed:
|
|
14
|
-
--wui-primary-50:
|
|
15
|
-
--wui-primary-30:
|
|
16
|
-
--wui-primary-20:
|
|
17
|
-
--wui-primary-15:
|
|
18
|
-
--wui-primary-10:
|
|
19
|
-
--wui-primary-5:
|
|
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:
|
|
25
|
-
--wui-text-2:
|
|
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:
|
|
32
|
-
--wui-text-4:
|
|
33
|
-
--wui-text-5:
|
|
31
|
+
--wui-text-3: #687088;
|
|
32
|
+
--wui-text-4: #99A1B7;
|
|
33
|
+
--wui-text-5: #C4CADA;
|
|
34
34
|
|
|
35
35
|
// Surfaces
|
|
36
|
-
--wui-bg-page:
|
|
37
|
-
--wui-bg-light:
|
|
38
|
-
--wui-bg-elevated:
|
|
39
|
-
--wui-bg-muted:
|
|
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:
|
|
41
|
+
--wui-border-soft: #F4F6F8;
|
|
42
42
|
|
|
43
43
|
// Gradients (hero, CTA)
|
|
44
44
|
--wui-hero-gradient:
|
|
@@ -50,24 +50,19 @@
|
|
|
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:
|
|
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:
|
|
57
|
-
|
|
58
|
-
// Elevation
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
--wui-shadow-card:
|
|
62
|
-
--wui-shadow-card-lifted:
|
|
63
|
-
--wui-shadow-card-lifted-hover:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// border, not a shadow); only things that float above the page lift —
|
|
67
|
-
// menus, popovers, modals. One ink (the heading navy) so every overlay
|
|
68
|
-
// casts the same light; components must never hand-roll a shadow.
|
|
69
|
-
--wui-shadow-overlay: 0 8px 28px -6px rgba(7, 20, 55, 0.14);
|
|
70
|
-
--wui-shadow-overlay-lg: 0 24px 60px -16px rgba(7, 20, 55, 0.28);
|
|
56
|
+
--wui-ease-elastic: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
57
|
+
|
|
58
|
+
// Elevation. In-page card tokens are flat (none). Overlay
|
|
59
|
+
// rungs match $wui-shadow-overlay* in _variables.scss — one ink for menus,
|
|
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};
|
|
65
|
+
--wui-shadow-overlay-lg: #{$wui-shadow-overlay-lg};
|
|
71
66
|
|
|
72
67
|
// One height for every form control. Buttons, inputs and selects share form
|
|
73
68
|
// rows, so they must line up — they were 46.8px and 45px, which reads as a
|
|
@@ -79,19 +74,19 @@
|
|
|
79
74
|
--wui-control-height-lg: 52px;
|
|
80
75
|
|
|
81
76
|
// Scrollbar thumb — one quiet grey-blue at three interaction strengths.
|
|
82
|
-
--wui-scrollbar:
|
|
83
|
-
--wui-scrollbar-hover:
|
|
77
|
+
--wui-scrollbar: rgba(145, 158, 171, 0.45);
|
|
78
|
+
--wui-scrollbar-hover: rgba(145, 158, 171, 0.65);
|
|
84
79
|
--wui-scrollbar-active: rgba(145, 158, 171, 0.85);
|
|
85
80
|
|
|
86
81
|
// ---- Marketing surface ramp -------------------------------------
|
|
87
82
|
// Soft tints + neutral washes used by mega-menu hover, callout cards,
|
|
88
83
|
// and section accents. Distinct from --wui-bg-* (which are the
|
|
89
84
|
// platform/portal palette).
|
|
90
|
-
--wui-surface-soft:
|
|
91
|
-
--wui-surface-blue:
|
|
85
|
+
--wui-surface-soft: #F2EFFE;
|
|
86
|
+
--wui-surface-blue: #ECE4FE;
|
|
92
87
|
--wui-surface-blue-soft: #FCFCFC;
|
|
93
|
-
--wui-surface-mute:
|
|
94
|
-
--wui-ink:
|
|
88
|
+
--wui-surface-mute: #F9FBFC;
|
|
89
|
+
--wui-ink: #000000;
|
|
95
90
|
|
|
96
91
|
// ---- Foreground ramp aliases -----------------------------------
|
|
97
92
|
// Marketing-side parlance — names mirror the standalone design.
|
|
@@ -105,16 +100,16 @@
|
|
|
105
100
|
|
|
106
101
|
// ---- Neutral ramp ----------------------------------------------
|
|
107
102
|
// Used by table emphasis, dividers, and mega-menu hover surfaces.
|
|
108
|
-
--wui-neutral-border-dark:
|
|
103
|
+
--wui-neutral-border-dark: #EBEDF3;
|
|
109
104
|
--wui-neutral-border-light: #F9FBFC;
|
|
110
|
-
--wui-neutral-bg-dark:
|
|
111
|
-
--wui-neutral-bg-light:
|
|
105
|
+
--wui-neutral-bg-dark: #F5F6FA;
|
|
106
|
+
--wui-neutral-bg-light: #F9FBFC;
|
|
112
107
|
|
|
113
108
|
// ---- Accent-dark -----------------------------------------------
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
--wui-accent-dark:
|
|
109
|
+
// Darker primary for pressed/hover accents in CSS (not SCSS $link-hover).
|
|
110
|
+
// Aliased onto --wui-primary-pressed so a product that rebrands the pressed
|
|
111
|
+
// token once also updates this name (was a frozen indigo hex).
|
|
112
|
+
--wui-accent-dark: var(--wui-primary-pressed);
|
|
118
113
|
|
|
119
114
|
// ---- Container width + responsive padding ----------------------
|
|
120
115
|
// The standalone design uses a 1512px max canvas with generous side
|
|
@@ -135,71 +130,76 @@
|
|
|
135
130
|
// Canonical info/success/warning/danger scales: `active` (text/icon on
|
|
136
131
|
// light), the base `100` (vivid fills/dots), then tints 50/30/20/10/5.
|
|
137
132
|
// Shared by the portal (aliased to --color-*) and marketing alerts/badges.
|
|
138
|
-
--wui-info-text:
|
|
139
|
-
--wui-info-active:
|
|
140
|
-
--wui-info:
|
|
141
|
-
--wui-info-50:
|
|
142
|
-
--wui-info-30:
|
|
143
|
-
--wui-info-20:
|
|
144
|
-
--wui-info-10:
|
|
145
|
-
--wui-info-5:
|
|
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;
|
|
146
141
|
|
|
147
142
|
// `-text` variants exist because the status colours are FILLS. #F8285A and #04B440
|
|
148
143
|
// are correct on a button or a badge, and both fail AA as text on a light surface
|
|
149
144
|
// (3.6:1 and 2.6:1) — which is how a validation error message, of all things, came
|
|
150
145
|
// to be unreadable. Darkening the base tokens would change every red button and
|
|
151
146
|
// green badge in the system, so text gets its own value and the fills keep theirs.
|
|
152
|
-
--wui-success-text:
|
|
147
|
+
--wui-success-text: #0e7531;
|
|
148
|
+
// Deepened success fill for surfaces that carry WHITE text (progress-bar labels), mirroring
|
|
149
|
+
// --wui-danger-strong below. The brighter --wui-success/-active are far too light for white
|
|
150
|
+
// (2.78:1 / 2.78:1, under AA); this clears it (#0e7531 → 5.86:1). Keep white-on-success off
|
|
151
|
+
// the other tokens so buttons/badges keep the vibrant fill.
|
|
152
|
+
--wui-success-strong: #0e7531;
|
|
153
153
|
--wui-success-active: #04B440;
|
|
154
|
-
--wui-success:
|
|
155
|
-
--wui-success-50:
|
|
156
|
-
--wui-success-30:
|
|
157
|
-
--wui-success-20:
|
|
158
|
-
--wui-success-10:
|
|
159
|
-
--wui-success-5:
|
|
160
|
-
|
|
161
|
-
--wui-warning-text:
|
|
154
|
+
--wui-success: #17C653;
|
|
155
|
+
--wui-success-50: #8BE3A9;
|
|
156
|
+
--wui-success-30: #BAEECC;
|
|
157
|
+
--wui-success-20: #D1F4DD;
|
|
158
|
+
--wui-success-10: #E8FAEE;
|
|
159
|
+
--wui-success-5: #F3FCF6;
|
|
160
|
+
|
|
161
|
+
--wui-warning-text: #876100;
|
|
162
162
|
--wui-warning-active: #DFA000;
|
|
163
|
-
--wui-warning:
|
|
164
|
-
--wui-warning-50:
|
|
165
|
-
--wui-warning-30:
|
|
166
|
-
--wui-warning-20:
|
|
167
|
-
--wui-warning-10:
|
|
168
|
-
--wui-warning-5:
|
|
169
|
-
|
|
170
|
-
--wui-danger-text:
|
|
163
|
+
--wui-warning: #F6B100;
|
|
164
|
+
--wui-warning-50: #FBD880;
|
|
165
|
+
--wui-warning-30: #FDE8B3;
|
|
166
|
+
--wui-warning-20: #FDEFCC;
|
|
167
|
+
--wui-warning-10: #FFF8E6;
|
|
168
|
+
--wui-warning-5: #FEFBF2;
|
|
169
|
+
|
|
170
|
+
--wui-danger-text: #b61d42;
|
|
171
171
|
// Deepened danger fill for surfaces that carry WHITE text (btn-danger). The brighter
|
|
172
172
|
// --wui-danger/-active below are too light for white (3.86:1 / 4.38:1, under AA); these
|
|
173
173
|
// clear it — #C81340 → 5.80:1, hover → higher. Keep white-on-danger off the other tokens.
|
|
174
|
-
--wui-danger-strong:
|
|
174
|
+
--wui-danger-strong: #C81340;
|
|
175
175
|
--wui-danger-strong-hover: #AB1036;
|
|
176
|
-
--wui-danger-active:
|
|
177
|
-
--wui-danger:
|
|
178
|
-
--wui-danger-50:
|
|
179
|
-
--wui-danger-30:
|
|
180
|
-
--wui-danger-20:
|
|
181
|
-
--wui-danger-10:
|
|
182
|
-
--wui-danger-5:
|
|
176
|
+
--wui-danger-active: #E82646;
|
|
177
|
+
--wui-danger: #F8285A;
|
|
178
|
+
--wui-danger-50: #FC94AD;
|
|
179
|
+
--wui-danger-30: #FDBFCE;
|
|
180
|
+
--wui-danger-20: #FED4DE;
|
|
181
|
+
--wui-danger-10: #FFEAEF;
|
|
182
|
+
--wui-danger-5: #FEF4F6;
|
|
183
183
|
|
|
184
184
|
// Corner radii (DESIGN.md scale) — portal aliases as --radius-*
|
|
185
185
|
--wui-radius-sm: 6px;
|
|
186
|
-
--wui-radius:
|
|
186
|
+
--wui-radius: 8px;
|
|
187
187
|
--wui-radius-lg: 12px;
|
|
188
188
|
--wui-radius-xl: 16px;
|
|
189
189
|
|
|
190
190
|
// ---- Portal dark palette ----------------------------------------
|
|
191
191
|
// Marketing stays light. Portal flips [data-theme="dark"] and maps
|
|
192
192
|
// --color-* aliases onto these so dark hexes live in theme/, not the app.
|
|
193
|
-
--wui-dark-bg-page:
|
|
194
|
-
--wui-dark-bg-elevated:
|
|
195
|
-
--wui-dark-bg-muted:
|
|
196
|
-
--wui-dark-bg-surface:
|
|
197
|
-
--wui-dark-border:
|
|
193
|
+
--wui-dark-bg-page: #0f1117;
|
|
194
|
+
--wui-dark-bg-elevated: #0d0f15;
|
|
195
|
+
--wui-dark-bg-muted: #232733;
|
|
196
|
+
--wui-dark-bg-surface: #181b23;
|
|
197
|
+
--wui-dark-border: #2a2f3a;
|
|
198
198
|
--wui-dark-border-strong:#3a414f;
|
|
199
|
-
--wui-dark-text:
|
|
200
|
-
--wui-dark-text-muted:
|
|
201
|
-
--wui-dark-text-subtle:
|
|
202
|
-
--wui-dark-primary:
|
|
199
|
+
--wui-dark-text: #e6e8ee;
|
|
200
|
+
--wui-dark-text-muted: #b4b9c6;
|
|
201
|
+
--wui-dark-text-subtle: #8a90a0;
|
|
202
|
+
--wui-dark-primary: #6B83EC;
|
|
203
203
|
--wui-dark-primary-hover:#93A6F2;
|
|
204
204
|
|
|
205
205
|
// ---- Fluid type scale --------------------------------------------
|
|
@@ -207,13 +207,13 @@
|
|
|
207
207
|
// stepping at Bootstrap's breakpoints. `base` sits close to Bootstrap's own 1rem body
|
|
208
208
|
// size; every other step is derived at a consistent ratio, up and down. Additive only —
|
|
209
209
|
// nothing in the shared theme consumes these yet, a product opts in per element.
|
|
210
|
-
--wui-fs-xs:
|
|
211
|
-
--wui-fs-sm:
|
|
212
|
-
--wui-fs-base: clamp(0.95rem,
|
|
213
|
-
--wui-fs-lg:
|
|
214
|
-
--wui-fs-xl:
|
|
215
|
-
--wui-fs-2xl:
|
|
216
|
-
--wui-fs-3xl:
|
|
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
217
|
|
|
218
218
|
// ---- Display font slot --------------------------------------------
|
|
219
219
|
// A dedicated slot for a display/heading typeface, kept separate from
|
|
@@ -223,16 +223,27 @@
|
|
|
223
223
|
--wui-font-display: var(--bs-body-font-family);
|
|
224
224
|
|
|
225
225
|
// ---- Z-index scale -------------------------------------------------
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
--wui-z-
|
|
226
|
+
// One page-level ladder. Local stacking inside a component (1, 5, 10, 40…) is
|
|
227
|
+
// NOT on this scale — only fixed/portalled layers that compete across the page.
|
|
228
|
+
//
|
|
229
|
+
// Why not Bootstrap's raw 1040/1050 for modals? App shell sticky chrome uses
|
|
230
|
+
// 1040–1045; a BS-height modal would paint under the mobile drawer. Modals are
|
|
231
|
+
// elevated to 1500+ so the shell never covers a dialog. Portalled menus
|
|
232
|
+
// (select / date / phone / infotip) sit at 1600 so a combobox inside a modal
|
|
233
|
+
// is not trapped under the dialog. Never invent a third number in component CSS —
|
|
234
|
+
// pick a rung from this list.
|
|
235
|
+
--wui-z-dropdown: 1000;
|
|
236
|
+
--wui-z-sticky: 1020;
|
|
237
|
+
--wui-z-fixed: 1030;
|
|
238
|
+
--wui-z-shell: 1040; // splash, shell base chrome
|
|
239
|
+
--wui-z-shell-scrim: 1044; // mobile drawer scrim
|
|
240
|
+
--wui-z-shell-drawer: 1045; // mobile sidebar drawer
|
|
241
|
+
--wui-z-modal-backdrop: 1500;
|
|
242
|
+
--wui-z-modal: 1505;
|
|
243
|
+
--wui-z-menu: 1600; // portalled select/date/phone menus + infotip
|
|
244
|
+
--wui-z-popover: 1600; // same rung as --wui-z-menu (above modal)
|
|
245
|
+
--wui-z-tooltip: 1650;
|
|
246
|
+
--wui-z-toast: 1700;
|
|
236
247
|
}
|
|
237
248
|
|
|
238
249
|
// Bootstrap md / xl (not invented px)
|