@webority/theme 0.7.3 → 0.7.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/theme",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
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": {
@@ -153,6 +153,37 @@
153
153
  --bs-btn-disabled-border-color: var(--wui-danger-strong);
154
154
  }
155
155
 
156
+ // Soft neutral tinted button — a quieter alternative to .btn-light for a secondary
157
+ // action that shouldn't compete with a primary/danger CTA in the same row.
158
+ .btn-subtle {
159
+ --bs-btn-bg: var(--color-bg-muted);
160
+ --bs-btn-color: var(--color-text);
161
+ --bs-btn-border-color: transparent;
162
+ --bs-btn-hover-bg: var(--color-border);
163
+ --bs-btn-hover-color: var(--color-text);
164
+ --bs-btn-hover-border-color: transparent;
165
+ --bs-btn-active-bg: var(--color-border-strong);
166
+ --bs-btn-active-color: var(--color-text);
167
+ --bs-btn-active-border-color: transparent;
168
+ }
169
+
170
+ // Light outline for a dark banner/hero CTA, where .btn-outline-primary's brand-coloured
171
+ // border/text would disappear against a dark background. White is used directly (not a
172
+ // token) because it's the one colour that reads on any dark surface, brand-independent.
173
+ .btn-outline-light {
174
+ --bs-btn-color: #fff;
175
+ --bs-btn-bg: transparent;
176
+ --bs-btn-border-color: rgba(255, 255, 255, 0.6);
177
+ --bs-btn-hover-color: #fff;
178
+ --bs-btn-hover-bg: rgba(255, 255, 255, 0.15);
179
+ --bs-btn-hover-border-color: #fff;
180
+ --bs-btn-active-color: #fff;
181
+ --bs-btn-active-bg: rgba(255, 255, 255, 0.25);
182
+ --bs-btn-active-border-color: #fff;
183
+ --bs-btn-disabled-color: rgba(255, 255, 255, 0.5);
184
+ --bs-btn-disabled-border-color: rgba(255, 255, 255, 0.3);
185
+ }
186
+
156
187
  // Themed disabled state (fleet default). Bootstrap's own disabled look is just the
157
188
  // variant's colour at --bs-btn-disabled-opacity (.65) — barely different from the active
158
189
  // button on a tinted brand palette. Give the ordinary case a real desaturated/muted look
@@ -180,9 +211,8 @@
180
211
  // sm/lg are re-asserted so they keep their own rung instead of the base value.
181
212
  .btn {
182
213
  --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.
214
+ // Progressive enhancement only (Chrome/Safari recent). Unsupported engines
215
+ // keep a plain border-radius arc — not a cross-browser design contract.
186
216
  corner-shape: squircle;
187
217
  }
188
218
  .btn-sm,
@@ -358,11 +388,10 @@
358
388
  }
359
389
 
360
390
 
361
- // ---- Card "lifted" (shadowless) -------------------------------------
362
- // Previously a soft drop-shadow card; now flat. The `.card-lifted`
363
- // class is preserved so existing call sites don't break — it now
364
- // expresses elevation only via the 1px hover translate (kept as a
365
- // tactile signal, not a depth cue). No box-shadow, here or on hover.
391
+ // ---- Card hover-lift (name is historical) -----------------------------
392
+ // `.card-lifted` does NOT cast a shadow — in-page cards stay flat. It only
393
+ // nudges 1px on hover (tactile signal). API prop stays `lifted` / class
394
+ // `card-lifted` so call sites don't break (TASK-06086).
366
395
  .card-lifted {
367
396
  transition: transform 0.2s var(--wui-ease-out-quart);
368
397
  &:hover {
@@ -398,50 +427,9 @@
398
427
 
399
428
 
400
429
  // ---- Inputs ---------------------------------------------------------
401
- // 5 locked states — default / hover / focus / disabled / error —
402
- // matching the reference `<Input>` component. Bootstrap drives the base
403
- // look via $input-* vars in _variables.scss; these blocks add the
404
- // hover-border-darken cue and the leading/trailing-slot wrapper.
405
- //
406
- // The error state is driven by `aria-invalid="true"` (NOT by Bootstrap's
407
- // .is-invalid class) so accessibility tooling and visual cue stay in
408
- // sync without extra markup.
409
-
410
- .form-control,
411
- .form-select {
412
- // Radius follows the token ladder (see component-radii note) rather than the
413
- // compiled $input-border-radius, so a product's --wui-radius override reaches
414
- // the raw Bootstrap inputs the way it already reaches the .input primitive.
415
- border-radius: var(--wui-radius);
416
- transition: border-color 0.15s ease;
417
- }
418
- .form-control:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]),
419
- .form-select:hover:not(:focus):not(:disabled):not([aria-invalid="true"]) {
420
- border-color: var(--color-border-strong); // --wui-text-3 @ 60%
421
- }
422
- .form-control[aria-invalid="true"],
423
- .form-select[aria-invalid="true"] {
424
- border-color: var(--bs-danger);
425
- }
426
- .form-control[aria-invalid="true"]:focus,
427
- .form-select[aria-invalid="true"]:focus {
428
- border-color: var(--bs-danger);
429
- box-shadow: none;
430
- }
431
-
432
- // Optional helper class for forms that prefer the explicit Bootstrap
433
- // validation pattern. Mirrors aria-invalid styling.
434
- .form-control.is-invalid,
435
- .form-select.is-invalid {
436
- border-color: var(--bs-danger);
437
- background-image: none; // kill Bootstrap's exclamation-mark SVG
438
- padding-right: 0.875rem;
439
- }
440
- .form-control.is-invalid:focus,
441
- .form-select.is-invalid:focus {
442
- border-color: var(--bs-danger);
443
- box-shadow: none;
444
- }
430
+ // Field chrome (height, hover, focus, invalid) lives in wui/_controls.scss on
431
+ // `.form-control` / `.form-select` with `.input` as an alias — one skin, not two
432
+ // (TASK-06079). This partial only keeps the marketing-side input-group shell.
445
433
 
446
434
 
447
435
  // ---- Input group (leading / trailing slots) ------------------------
@@ -464,33 +452,32 @@
464
452
  align-items: center;
465
453
  gap: 0.5rem;
466
454
  padding-inline: 0.875rem;
467
- height: calc(2.5rem + 2px); // 40px box + 1px border each side
468
- background: var(--wui-bg-light, #FFFFFF);
469
- border: 1px solid var(--bs-border-color);
470
- border-radius: var(--radius); // matches the bare input radius
455
+ height: var(--wui-control-height);
456
+ background: var(--color-surface);
457
+ border: 1px solid var(--color-border);
458
+ border-radius: var(--radius);
471
459
  transition: border-color 0.15s ease;
472
-
473
-
474
460
  }
475
461
  .wui-input-group:hover:not(:focus-within):not(.is-disabled) {
476
- border-color: rgba(120, 130, 157, 0.6);
462
+ border-color: var(--color-border-strong);
477
463
  }
478
464
  .wui-input-group:focus-within {
479
- border-color: var(--wui-primary);
465
+ border-color: var(--color-primary);
480
466
  }
481
467
  .wui-input-group.is-disabled,
482
468
  .wui-input-group:has(input:disabled) {
483
- background: var(--wui-bg-muted);
469
+ background: var(--color-bg-muted);
484
470
  cursor: not-allowed;
485
- border-color: var(--bs-border-color);
471
+ border-color: var(--color-border);
486
472
  }
487
473
  .wui-input-group.has-error,
488
474
  .wui-input-group:has(input[aria-invalid="true"]) {
489
- border-color: var(--bs-danger);
475
+ border-color: color-mix(in srgb, var(--color-danger) 45%, var(--color-surface));
490
476
  }
491
477
 
492
478
  // Strip the bare input's own chrome — the wrapper carries it all.
493
- .wui-input-group > .form-control {
479
+ .wui-input-group > .form-control,
480
+ .wui-input-group > .input {
494
481
  flex: 1 1 auto;
495
482
  min-width: 0;
496
483
  height: 100%;
@@ -502,7 +489,9 @@
502
489
  border-radius: 0;
503
490
  }
504
491
  .wui-input-group > .form-control:focus,
505
- .wui-input-group > .form-control:hover {
492
+ .wui-input-group > .form-control:hover,
493
+ .wui-input-group > .input:focus,
494
+ .wui-input-group > .input:hover {
506
495
  border: 0;
507
496
  box-shadow: none !important;
508
497
  background: transparent;
@@ -782,39 +771,31 @@ button.wui-input-leading:focus-visible {
782
771
  }
783
772
 
784
773
 
785
- // ---- Refined native scrollbars --------------------------------------
786
- // Mirrors the locked design's always-visible scrollbar treatment:
774
+ // ---- Native scrollbars (document + opt-in) --------------------------
775
+ // Scoped to html/body and `.wui-scroll` — never `*` (TASK-06085). Global
776
+ // `*` restyled every nested scroller (modals, tiny panels) and fought
777
+ // component-local rails. AppScrollbar uses `.wui-scroll` (see wui/_scrollbar).
787
778
  //
788
- // - 8px thin WebKit thumb (also Firefox via `scrollbar-width: thin`)
789
- // - Quiet grey-blue tint always visible at rest (~40% alpha)
790
- // - Brighter on hover (~60% alpha) and active (~80%)
791
- // - Pill thumb with a 2px transparent border (visual cushion)
792
- // - `scrollbar-gutter: stable` on <html>/<body> so the page never
793
- // shifts when scrollable content grows/shrinks. Internal containers
794
- // (avatars, badges, rounded buttons) intentionally skip the gutter
795
- // reservation — applying it everywhere would push centered glyphs
796
- // off-center inside rounded boxes.
797
- //
798
- // `wwwroot/js/scroll-state.js` toggles `html.is-scrolling` for ~800ms
799
- // after each scroll event; the thumb gets an extra opacity bump while
800
- // the class is on, giving subtle "I'm scrolling now" feedback without
801
- // sacrificing the always-visible affordance.
779
+ // `scrollbar-gutter: stable` only on the document so internal boxes do not
780
+ // push centered glyphs off-center. `html.is-scrolling` (scroll-state.js)
781
+ // brightens the document thumb while the page is scrolling.
802
782
  html,
803
783
  body {
804
784
  scrollbar-gutter: stable;
805
- }
806
- * {
807
785
  scrollbar-width: thin;
808
786
  scrollbar-color: var(--wui-scrollbar) transparent;
809
787
  }
810
- *::-webkit-scrollbar {
788
+ html::-webkit-scrollbar,
789
+ body::-webkit-scrollbar {
811
790
  width: 8px;
812
791
  height: 8px;
813
792
  }
814
- *::-webkit-scrollbar-track {
793
+ html::-webkit-scrollbar-track,
794
+ body::-webkit-scrollbar-track {
815
795
  background: transparent;
816
796
  }
817
- *::-webkit-scrollbar-thumb {
797
+ html::-webkit-scrollbar-thumb,
798
+ body::-webkit-scrollbar-thumb {
818
799
  background-color: var(--wui-scrollbar);
819
800
  border-radius: 9999px;
820
801
  border: 2px solid transparent;
@@ -823,29 +804,31 @@ body {
823
804
  min-width: 36px;
824
805
  transition: background-color 220ms ease;
825
806
  }
826
- *::-webkit-scrollbar-thumb:hover {
807
+ html::-webkit-scrollbar-thumb:hover,
808
+ body::-webkit-scrollbar-thumb:hover {
827
809
  background-color: var(--wui-scrollbar-hover);
828
810
  background-clip: padding-box;
829
811
  }
830
- *::-webkit-scrollbar-thumb:active {
812
+ html::-webkit-scrollbar-thumb:active,
813
+ body::-webkit-scrollbar-thumb:active {
831
814
  background-color: var(--wui-scrollbar-active);
832
815
  background-clip: padding-box;
833
816
  }
834
- *::-webkit-scrollbar-corner {
817
+ html::-webkit-scrollbar-corner,
818
+ body::-webkit-scrollbar-corner {
835
819
  background: transparent;
836
820
  }
837
821
 
838
- // While the page is actively scrolling, give the thumb a slight extra
839
- // pop so the user can track its position more easily.
840
- html.is-scrolling * {
822
+ html.is-scrolling,
823
+ html.is-scrolling body {
841
824
  scrollbar-color: var(--wui-scrollbar-hover) transparent;
842
825
  }
843
- html.is-scrolling *::-webkit-scrollbar-thumb {
826
+ html.is-scrolling::-webkit-scrollbar-thumb,
827
+ html.is-scrolling body::-webkit-scrollbar-thumb {
844
828
  background-color: var(--wui-scrollbar-hover);
845
829
  }
846
830
 
847
- // Utility: completely hide a scrollbar (e.g., for marquee/horizontal
848
- // carousels where the rail itself is visually intrusive).
831
+ // Hide a scrollbar (marquee / horizontal carousels).
849
832
  .scrollbar-hide {
850
833
  scrollbar-width: none;
851
834
  }
package/scss/_tokens.scss CHANGED
@@ -55,19 +55,14 @@
55
55
  // Used by the tab ink-bar so a tab switch springs into place rather than sliding flat.
56
56
  --wui-ease-elastic: cubic-bezier(0.34, 1.56, 0.64, 1);
57
57
 
58
- // Elevation — exposes the Bootstrap $box-shadow* SCSS vars (set in
59
- // _variables.scss) as CSS vars so inline styles / JS / the portal
60
- // can reach the same values Bootstrap utilities use.
61
- --wui-shadow-card: #{$box-shadow-sm};
62
- --wui-shadow-card-lifted: #{$box-shadow};
63
- --wui-shadow-card-lifted-hover: #{$box-shadow-lg};
64
-
65
- // Overlay elevation. The rule: in-page surfaces stay FLAT (a card is a
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);
58
+ // Elevation (TASK-06080). 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
@@ -111,10 +106,10 @@
111
106
  --wui-neutral-bg-light: #F9FBFC;
112
107
 
113
108
  // ---- Accent-dark -----------------------------------------------
114
- // Link hover, focused button, deep-purple CTA underline. Matches
115
- // $link-hover-color in _variables.scss but exposed as a CSS
116
- // var so JS / inline styles can reach it.
117
- --wui-accent-dark: #4338CA;
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
@@ -223,16 +218,27 @@
223
218
  --wui-font-display: var(--bs-body-font-family);
224
219
 
225
220
  // ---- 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.
221
+ // One page-level ladder. Local stacking inside a component (1, 5, 10, 40…) is
222
+ // NOT on this scale — only fixed/portalled layers that compete across the page.
223
+ //
224
+ // Why not Bootstrap's raw 1040/1050 for modals? App shell sticky chrome uses
225
+ // 1040–1045; a BS-height modal would paint under the mobile drawer. Modals are
226
+ // elevated to 1500+ so the shell never covers a dialog. Portalled menus
227
+ // (select / date / phone / infotip) sit at 1600 so a combobox inside a modal
228
+ // is not trapped under the dialog. Never invent a third number in component CSS —
229
+ // pick a rung from this list (TASK-06078).
228
230
  --wui-z-dropdown: 1000;
229
231
  --wui-z-sticky: 1020;
230
232
  --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;
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
236
+ --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;
236
242
  }
237
243
 
238
244
  // Bootstrap md / xl (not invented px)
@@ -37,9 +37,9 @@ $theme-colors: (
37
37
  );
38
38
 
39
39
  // Brand helper — set a product's primary ONCE and have the theme-colour map
40
- // (so every .btn-primary / .bg-primary / .text-primary utility) and $link-color
41
- // follow, instead of overriding each in turn. Call it in the product's
42
- // pre-bootstrap variables, BEFORE the `bootstrap/scss/bootstrap` import, e.g.:
40
+ // (so every .btn-primary / .bg-primary / .text-primary utility), $link-color,
41
+ // and $link-hover-color follow, instead of overriding each in turn. Call it in
42
+ // the product's pre-bootstrap variables, BEFORE `bootstrap/scss/bootstrap`, e.g.:
43
43
  //
44
44
  // @include wui-brand-primary(#0EA5E9);
45
45
  // @import "bootstrap/scss/bootstrap";
@@ -51,12 +51,13 @@ $theme-colors: (
51
51
  $primary: $primary !global;
52
52
  $theme-colors: map-merge($theme-colors, ("primary": $primary)) !global;
53
53
  $link-color: $primary !global;
54
+ $link-hover-color: mix(black, $primary, 12%) !global;
54
55
  }
55
56
 
56
57
 
57
58
  // ---- Body / text ----------------------------------------------------
59
+ // $body-bg left at Bootstrap's $white — do not restate pure defaults here.
58
60
  $body-color: #252F4A; // text-1 from locked palette
59
- $body-bg: #FFFFFF;
60
61
  $body-secondary-color: #4B5675;
61
62
  $body-tertiary-color: #687088; // AA on white (matches --wui-text-3); #78829D was only 3.5:1
62
63
  $body-secondary-bg: #F9FBFC;
@@ -70,81 +71,71 @@ $border-color-translucent: rgba(13, 24, 43, 0.08);
70
71
  // One ladder; every component derives from it (Bootstrap's own model). Set a
71
72
  // component's radius by pointing it at a rung, never by hardcoding a px value —
72
73
  // that is what let buttons drift to ~3x Bootstrap's.
73
- $border-radius: 0.5rem; // 8px — buttons, inputs, dropdowns
74
- $border-radius-sm: 0.375rem; // 6px
75
- $border-radius-lg: 0.75rem; // 12px — cards, toasts
76
- $border-radius-xl: 1rem; // 16px — modals
77
- $border-radius-2xl: 1.5rem; // 24px
78
- $border-radius-pill: 9999px;
74
+ // Only rungs that DIFFER from Bootstrap 5.3 are set: BS is 0.375/0.25/0.5/1/2rem;
75
+ // $border-radius-xl (1rem) and the font-weight ladder match BS and are left alone.
76
+ $border-radius: 0.5rem; // 8px — buttons, inputs, dropdowns (BS 0.375)
77
+ $border-radius-sm: 0.375rem; // 6px (BS 0.25)
78
+ $border-radius-lg: 0.75rem; // 12px — cards, toasts (BS 0.5)
79
+ $border-radius-2xl: 1.5rem; // 24px (BS 2rem via $border-radius-xxl)
80
+ $border-radius-pill: 9999px; // same visual as BS 50rem; explicit for token clarity
79
81
 
80
82
 
81
83
  // ---- Typography -----------------------------------------------------
84
+ // Font-size base (1rem) and font-weight 300–700 match Bootstrap — not restated.
85
+ // h1/h2/h6 match Bootstrap's $font-size-base multipliers at 1rem — not restated;
86
+ // only h3–h5 are tighter than Bootstrap's scale.
82
87
  $font-family-sans-serif: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
83
88
  $font-family-monospace: 'JetBrains Mono', SFMono-Regular, Menlo, Monaco, Consolas, monospace;
84
89
 
85
- $font-size-base: 1rem; // 16px
86
- $line-height-base: 1.55;
90
+ $line-height-base: 1.55; // BS 1.5
87
91
 
88
- $font-weight-light: 300;
89
- $font-weight-normal: 400;
90
- $font-weight-medium: 500;
91
- $font-weight-semibold: 600;
92
- $font-weight-bold: 700;
93
-
94
- $headings-font-weight: 600;
95
- $headings-line-height: 1.2;
92
+ $headings-font-weight: 600; // BS 500
96
93
  $headings-letter-spacing: -0.02em;
97
94
  $headings-color: #071437;
98
95
 
99
96
  $display-font-weight: 500;
100
97
  $display-line-height: 1.05;
101
98
 
102
- $h1-font-size: 2.5rem; // 40px
103
- $h2-font-size: 2rem; // 32px
104
- $h3-font-size: 1.5rem; // 24px
105
- $h4-font-size: 1.25rem; // 20px
106
- $h5-font-size: 1.125rem; // 18px
107
- $h6-font-size: 1rem; // 16px
99
+ $h3-font-size: 1.5rem; // 24px (BS 1.75rem)
100
+ $h4-font-size: 1.25rem; // 20px (BS 1.5rem)
101
+ $h5-font-size: 1.125rem; // 18px (BS 1.25rem)
108
102
 
109
103
 
110
104
  // ---- Spacing --------------------------------------------------------
111
- // Bootstrap default ladder is 0/.25/.5/1/1.5/3rem. We add 6/8/10/12
112
- // for hero / section padding so utility classes like `py-8` exist.
113
- $spacer: 1rem;
105
+ // Bootstrap ladder is 0/.25/.5/1/1.5/3rem ($spacer = 1rem). We replace the map
106
+ // with the same 0–5 rungs plus 6/7/8/10/12 for hero / section padding so
107
+ // utilities like `py-8` exist. No need to restate $spacer itself.
114
108
  $spacers: (
115
109
  0: 0,
116
- 1: $spacer * 0.25, // 4px
117
- 2: $spacer * 0.5, // 8px
118
- 3: $spacer, // 16px
119
- 4: $spacer * 1.5, // 24px
120
- 5: $spacer * 3, // 48px
121
- 6: $spacer * 4.5, // 72px
122
- 7: $spacer * 5, // 80px
123
- 8: $spacer * 6, // 96px
124
- 10: $spacer * 8, // 128px
125
- 12: $spacer * 10, // 160px
110
+ 1: 0.25rem, // 4px
111
+ 2: 0.5rem, // 8px
112
+ 3: 1rem, // 16px
113
+ 4: 1.5rem, // 24px
114
+ 5: 3rem, // 48px
115
+ 6: 4.5rem, // 72px
116
+ 7: 5rem, // 80px
117
+ 8: 6rem, // 96px
118
+ 10: 8rem, // 128px
119
+ 12: 10rem, // 160px
126
120
  );
127
121
 
128
-
129
- // ---- Container widths -----------------------------------------------
130
- // The locked design uses a wider canvas (1512px max) with generous side
131
- // padding. Override Bootstrap's narrower default so marketing layouts
132
- // breathe properly on large monitors.
133
- $container-max-widths: (
134
- sm: 540px,
135
- md: 720px,
136
- lg: 960px,
137
- xl: 1140px,
138
- xxl: 1320px,
139
- );
122
+ // $container-max-widths is intentionally NOT overridden — Bootstrap's
123
+ // 540/720/960/1140/1320 map is already what we want. The wider marketing
124
+ // canvas (1512px) is `.container-cloves` + `--wui-container-max` in tokens,
125
+ // not Bootstrap's stepped `.container`.
140
126
 
141
127
 
142
128
  // ---- Shadows --------------------------------------------------------
143
- // Mirrors the `card / card-lifted / card-lifted-hover` shadow tokens
144
- // from the locked design.
145
- $box-shadow-sm: 0 1px 2px rgba(7, 20, 55, 0.04), 0 4px 12px rgba(7, 20, 55, 0.06);
146
- $box-shadow: 0 0 2px 0 rgba(145, 158, 171, 0.20), 0 12px 24px -4px rgba(145, 158, 171, 0.12);
147
- $box-shadow-lg: 0 0 2px 0 rgba(145, 158, 171, 0.20), 0 20px 40px -4px rgba(145, 158, 171, 0.20);
129
+ // Elevation rule (TASK-06080): in-page surfaces are FLAT (borders only).
130
+ // Bootstrap utility ladder (.shadow / .shadow-sm / .shadow-lg) is zeroed so
131
+ // products cannot reintroduce random depth. Floating UI (dropdown / modal /
132
+ // toast) uses the overlay rungs — same values as --wui-shadow-overlay* in tokens.
133
+ $wui-shadow-overlay: 0 8px 28px -6px rgba(7, 20, 55, 0.14);
134
+ $wui-shadow-overlay-lg: 0 24px 60px -16px rgba(7, 20, 55, 0.28);
135
+
136
+ $box-shadow-sm: none;
137
+ $box-shadow: none;
138
+ $box-shadow-lg: none;
148
139
 
149
140
 
150
141
  // ---- Buttons --------------------------------------------------------
@@ -162,34 +153,29 @@ $btn-transition: color 0.15s ease, background-color 0.15s ease, border-c
162
153
 
163
154
 
164
155
  // ---- Forms ----------------------------------------------------------
165
- // Matches the reference `<Input>` component spec (5 locked states:
166
- // default / hover / focus / disabled / error). The border itself is the
167
- // focus cue — no separate box-shadow glow. Disabled gets a muted bg.
168
- // Error is driven by `aria-invalid="true"` (no separate .is-invalid
169
- // class needed for the visual change).
156
+ // Bootstrap `.form-control` is the field skin; post-CSS in wui/_controls.scss
157
+ // layers shared height / hover / focus / invalid on `.form-control` and the
158
+ // `.input` alias together (TASK-06079). Here only $input-* BS vars.
170
159
  $input-bg: #FFFFFF;
171
160
  $input-disabled-bg: #F5F6FA; // --wui-bg-muted
172
161
  $input-border-color: $border-color;
173
162
  // $input-focus-border-color is intentionally NOT overridden. Setting it to $primary here
174
163
  // captures the LIBRARY's $primary (this file compiles before a product's brand override),
175
- // leaking indigo into every product's raw .form-control focus. Bootstrap's !default derives
176
- // it from the brand $primary after the override; the library's own .input:focus uses
177
- // var(--wui-primary), which the product overrides at runtime.
164
+ // leaking indigo into every product. Bootstrap's !default derives it from brand $primary;
165
+ // post-CSS focus uses var(--color-primary) so runtime rebrand tracks.
178
166
  $input-focus-box-shadow: none; // border IS the focus cue
179
167
  $input-focus-color: $body-color;
180
- $input-padding-y: 0.5rem; // 8px → 40px total height with 1rem font + 1.5 line-height
181
- $input-padding-x: 0.875rem; // 14px — comfortable inset for leading icon space
182
- $input-border-radius: $border-radius; // 8px — matches buttons; controls share one corner
168
+ $input-padding-y: 0.5rem; // filler inside --wui-control-height (44px)
169
+ $input-padding-x: 0.875rem; // 14px
170
+ $input-border-radius: $border-radius;
183
171
  $input-placeholder-color: #99A1B7; // --wui-text-4
184
172
 
185
173
 
186
174
  // ---- Cards ----------------------------------------------------------
187
175
  $card-border-color: $border-color;
188
176
  $card-border-radius: $border-radius-lg;
189
- // Flat surfaces — no drop shadow on default .card. Depth is conveyed
190
- // via background tint + hairline border instead. Use .card-lifted
191
- // utility (in _components.scss) only when a card legitimately
192
- // needs to sit "above" the page (currently disabled — set to `none`).
177
+ // Flat surfaces — no drop shadow on default .card. Depth is border + tint.
178
+ // `.card-lifted` is a 1px hover translate only (not a shadow).
193
179
  $card-box-shadow: none;
194
180
  $card-spacer-y: 1.5rem;
195
181
  $card-spacer-x: 1.5rem;
@@ -199,23 +185,26 @@ $card-spacer-x: 1.5rem;
199
185
  $nav-link-font-weight: 500;
200
186
  $dropdown-border-color: $border-color;
201
187
  $dropdown-border-radius: $border-radius;
202
- $dropdown-box-shadow: $box-shadow-lg;
188
+ $dropdown-box-shadow: $wui-shadow-overlay;
203
189
 
204
190
 
205
191
  // ---- Modals + toasts ------------------------------------------------
206
192
  $modal-content-border-color: $border-color;
207
- $modal-content-border-radius: $border-radius-xl;
208
- $modal-content-box-shadow-xs: $box-shadow-lg;
193
+ $modal-content-border-radius: 1rem; // BS $border-radius-xl (1rem) — literal because we do not restate that rung
194
+ $modal-content-box-shadow-xs: $wui-shadow-overlay-lg;
209
195
 
210
196
  $toast-border-color: $border-color;
211
197
  $toast-border-radius: $border-radius-lg;
212
- $toast-box-shadow: $box-shadow-lg;
198
+ $toast-box-shadow: $wui-shadow-overlay-lg;
213
199
 
214
200
 
215
201
  // ---- Links ----------------------------------------------------------
216
- $link-color: $primary;
217
- $link-hover-color: #4338CA; // accent-dark (primary pressed)
218
- $link-decoration: none;
202
+ // $link-color left at Bootstrap's default ($primary) so it tracks brand $primary.
203
+ // Hover is a darkened mix of $primary — never a hard-coded indigo — so a product
204
+ // that sets $primary (or calls wui-brand-primary) gets a matching hover. Approximate
205
+ // of Bootstrap's shift-color($link-color, 20%) without importing BS functions here.
206
+ $link-hover-color: mix(black, $primary, 12%);
207
+ $link-decoration: none; // BS default is underline
219
208
  $link-hover-decoration: underline;
220
209
 
221
210
 
@@ -224,8 +213,8 @@ $link-hover-decoration: underline;
224
213
  // LIBRARY's $primary (this file compiles before a product's brand override), so the ring
225
214
  // leaked indigo into every product. Bootstrap's !default `rgba($primary, $focus-ring-opacity)`
226
215
  // is evaluated after the brand override, so it tracks the brand correctly.
227
- $focus-ring-width: 3px;
228
- $focus-ring-blur: 0;
216
+ // $focus-ring-blur matches BS (0) — not restated.
217
+ $focus-ring-width: 3px; // BS 0.25rem (4px)
229
218
 
230
219
 
231
220
  // ---- Table ----------------------------------------------------------
@@ -97,7 +97,8 @@
97
97
  --color-scrim: color-mix(in srgb, var(--wui-text-heading) 62%, transparent);
98
98
  --color-on-scrim: var(--wui-bg-light);
99
99
 
100
- // Elevation — in-page surfaces flat (borders only); floating overlays lift.
100
+ // Elevation — in-page flat; only floating overlays lift (matches zeroed BS
101
+ // $box-shadow* utilities and --wui-shadow-card* none — TASK-06080).
101
102
  --shadow-sm: none;
102
103
  --shadow-md: none;
103
104
  --shadow-lg: none;
@@ -318,7 +318,7 @@
318
318
  // scrollable modal / table card. Sits above the modal layer.
319
319
  .search-select-menu-fixed {
320
320
  position: fixed;
321
- z-index: 1600;
321
+ z-index: var(--wui-z-menu);
322
322
  right: auto;
323
323
  top: auto;
324
324
  left: auto;
@@ -483,7 +483,7 @@
483
483
  // sits above the modal layer.
484
484
  .datepicker-pop {
485
485
  position: fixed;
486
- z-index: 1600;
486
+ z-index: var(--wui-z-menu);
487
487
  width: 312px;
488
488
  max-width: calc(100vw - 16px);
489
489
  padding: 18px;
@@ -801,7 +801,7 @@ button.datepicker-month:hover {
801
801
  }
802
802
  .daterange-pop {
803
803
  position: fixed;
804
- z-index: 1600;
804
+ z-index: var(--wui-z-menu);
805
805
  width: 620px;
806
806
  max-width: calc(100vw - 16px);
807
807
  padding: 18px;
@@ -998,7 +998,7 @@ button.datepicker-month:hover {
998
998
  // modal layer, matching .search-select-menu-fixed and .datepicker-pop.
999
999
  .country-dropdown-menu {
1000
1000
  position: fixed;
1001
- z-index: 1600;
1001
+ z-index: var(--wui-z-menu);
1002
1002
  width: 320px;
1003
1003
  max-width: calc(100vw - 16px);
1004
1004
  padding: 4px;