ui-style-kit-css 2.7.0 → 2.7.1

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +10 -10
  3. package/dist/ui-style-kit.css +767 -1303
  4. package/dist/ui-style-kit.min.css +2 -2
  5. package/dist/ui-style-kit.visual.css +767 -1303
  6. package/dist/ui-style-kit.visual.min.css +2 -2
  7. package/dist/ui-style-kit.with-bridge.css +767 -1303
  8. package/dist/ui-style-kit.with-bridge.min.css +2 -2
  9. package/dist/visual/art-deco.css +172 -744
  10. package/dist/visual/bauhaus.css +2 -2
  11. package/dist/visual/bento.css +2 -2
  12. package/dist/visual/brutalism.css +131 -708
  13. package/dist/visual/clay.css +2 -2
  14. package/dist/visual/cyberpunk.css +141 -718
  15. package/dist/visual/data-terminal.css +145 -722
  16. package/dist/visual/editorial-luxe.css +148 -725
  17. package/dist/visual/industrial-utility.css +152 -729
  18. package/dist/visual/maximalist.css +152 -729
  19. package/dist/visual/minimal-saas.css +142 -715
  20. package/dist/visual/neo-noir.css +2 -2
  21. package/dist/visual/neumorphism.css +142 -719
  22. package/dist/visual/organic-modern.css +2 -2
  23. package/dist/visual/paper-editorial.css +167 -744
  24. package/dist/visual/retro-glass.css +147 -719
  25. package/dist/visual/retrofuturism.css +141 -718
  26. package/dist/visual/tactile.css +144 -721
  27. package/dist/visual/technical-blueprint.css +156 -729
  28. package/dist/visual/y2k.css +164 -721
  29. package/docs/DEMO-SHOWCASE.md +16 -0
  30. package/docs/ECOSYSTEM.md +2 -2
  31. package/docs/PUBLISHING.md +7 -7
  32. package/docs/RELEASE-2.7.1.md +21 -0
  33. package/manifest.json +1 -1
  34. package/package.json +1 -1
  35. package/styles/art-deco.css +12 -3
  36. package/styles/bauhaus.css +5 -3
  37. package/styles/bento.css +11 -7
  38. package/styles/brutalism.css +5 -3
  39. package/styles/clay.css +12 -7
  40. package/styles/cyberpunk.css +10 -6
  41. package/styles/data-terminal.css +4 -2
  42. package/styles/editorial-luxe.css +3 -2
  43. package/styles/industrial-utility.css +8 -6
  44. package/styles/maximalist.css +3 -1
  45. package/styles/minimal-saas.css +7 -3
  46. package/styles/neo-noir.css +11 -4
  47. package/styles/neumorphism.css +10 -5
  48. package/styles/organic-modern.css +1 -1
  49. package/styles/paper-editorial.css +7 -5
  50. package/styles/retro-glass.css +8 -4
  51. package/styles/retrofuturism.css +4 -3
  52. package/styles/tactile.css +7 -4
  53. package/styles/technical-blueprint.css +8 -4
  54. package/styles/y2k.css +30 -11
@@ -4,6 +4,22 @@ The default `index.html` and `demo/index.html` present one shared library showca
4
4
  semantic components, overview, color tokens, prefixed components, native HTML,
5
5
  the Interactive Surface bridge, and usage examples.
6
6
 
7
+ ## Interaction contrast
8
+
9
+ Hover, press, and keyboard focus must preserve readable labels. A filled action
10
+ keeps its paired foreground and background; an inline-link rule must not recolor
11
+ a button or navigation component. Gloss and grain remain decorative rather than
12
+ replacing the label's underlying paint.
13
+
14
+ The release regressions check 20 presets, 25 named palettes plus native palettes,
15
+ three modes, and five interaction states, including hover on pressed buttons.
16
+ They cover prefixed, semantic, and mixed React class models, anchor buttons,
17
+ native controls, disclosures, navigation, and table labels. The
18
+ contrast probe samples the central label lane and accounts for translucent paint
19
+ and full-face pseudo-elements; positioned dropdown arrows are outside that lane.
20
+
21
+ Recheck the rendered demo after changing palette values in the workbench.
22
+
7
23
  ## Preview state and palette editing
8
24
 
9
25
  Use `?ui=tactile&theme=None&mode=dark` to preview a native palette. The `personality`
package/docs/ECOSYSTEM.md CHANGED
@@ -14,11 +14,11 @@ Before a UI branch or pull request is expected to validate, verify the reviewed
14
14
 
15
15
  | Library | Current aligned version | Owns |
16
16
  |---|---:|---|
17
- | `ui-style-kit-css@2.7.0` | current release candidate | visual identity, color themes, UI paint, native HTML styling, content wrapping, and bridge tokens |
17
+ | `ui-style-kit-css@2.7.1` | current UI version | visual identity, color themes, UI paint, native HTML styling, content wrapping, and bridge tokens |
18
18
  | `interactive-surface-css@1.7.3` | compatible state release | interaction-state primitives, surface behavior, state layers, and input affordances |
19
19
  | `layout-style-css@3.2.3` | compatible structural release | structural wrappers, grids, sections, app shells, and layout recipes |
20
20
 
21
- The current combination is `ui-style-kit-css@2.7.0`, `interactive-surface-css@1.7.3`, and `layout-style-css@3.2.3`. UI Style Kit `2.7.0` is the current release candidate; `2.6.1` remains published on npm. The companion versions are reviewed releases. Layout Style `3.2.3` is the compatible structural release. The validated minimum remains `ui-style-kit-css@2.1.0`, `interactive-surface-css@1.5.0`, and `layout-style-css@3.0.0`.
21
+ The current combination is `ui-style-kit-css@2.7.1`, `interactive-surface-css@1.7.3`, and `layout-style-css@3.2.3`. UI Style Kit `2.7.1` is the current UI version. The companion versions are reviewed releases. Layout Style `3.2.3` is the compatible structural release. The validated minimum remains `ui-style-kit-css@2.1.0`, `interactive-surface-css@1.5.0`, and `layout-style-css@3.0.0`.
22
22
 
23
23
  ## Layout-to-visual pairing matrix
24
24
 
@@ -1,10 +1,10 @@
1
1
  # Publishing Guide
2
2
 
3
- ## 2.7.0 release workflow
3
+ ## 2.7.1 release workflow
4
4
 
5
- The current checkout is a release candidate. [Release preparation notes](RELEASE-2.7.0.md)
5
+ This guide prepares version `2.7.1`. [Release preparation notes](RELEASE-2.7.1.md)
6
6
  record the current local scope and gates; existing visual-QA documents are historical
7
- evidence, not proof that subsequent edits passed browser validation. The 2.7.0
7
+ evidence, not proof that subsequent edits passed browser validation. The 2.7.1
8
8
  changelog entry must carry the approved publication date before tagging.
9
9
 
10
10
  Update the tracked `wiki/` sources with the README and docs. Publishing those pages
@@ -13,9 +13,9 @@ documentation generator is configured in this package; reusable JavaScript helpe
13
13
  use JSDoc-compatible comments, and the checked-in build owns generated CSS, manifests,
14
14
  icons, README size measurements, and demo asset hashes.
15
15
 
16
- Prepare `ui-style-kit-css@2.7.0` on its release branch, open a pull request against `main`, and merge only after the fast automated gate is green and any requested manual demo review is complete. The aligned companion releases are `layout-style-css@3.2.3` and `interactive-surface-css@1.7.3`.
16
+ Prepare `ui-style-kit-css@2.7.1` on its release branch, open a pull request against `main`, and merge only after the fast automated gate is green and any requested manual demo review is complete. The aligned companion releases are `layout-style-css@3.2.3` and `interactive-surface-css@1.7.3`.
17
17
 
18
- Do not push `v2.7.0` before the reviewed release commit is on `main`. A pushed version tag runs Release Version Alignment, which validates the tag/package/changelog contract and creates the GitHub Release; publishing that release triggers the protected npm workflow.
18
+ Do not push `v2.7.1` before the reviewed release commit is on `main`. A pushed version tag runs Release Version Alignment, which validates the tag/package/changelog contract and creates the GitHub Release; publishing that release triggers the protected npm workflow.
19
19
 
20
20
  Release Version Alignment owns the fast automated release gate: lint, build, unit checks, the tagged Chromium release-smoke Playwright suite, and packed ecosystem preflight with `--skip-clean-install`. The visual-baseline suite, full UI matrix, and clean-install ecosystem matrix are manual escalation tools, not default push or publish blockers. The protected npm workflow intentionally does not rerun browser gates; it revalidates the immutable tag, package contracts, browser-free compatibility checks, companion commit reachability, the explicit release preflight with `--skip-clean-install`, npm token presence, npm owner authorization, and registry state before publishing.
21
21
 
@@ -76,7 +76,7 @@ npm run check:ecosystem:packs -- --layout-repo ../Layout-Style-CSS --interactive
76
76
  npm publish
77
77
  ```
78
78
 
79
- `prepublishOnly` runs `npm run release:verify`, so a direct `npm publish` still has the default fast release gate. For GitHub releases, push or dispatch the matching package tag, such as `v2.7.0`, only after the release PR is merged. The release workflows verify that `package.json`, `package-lock.json`, `CHANGELOG.md`, generated dist banners, and ecosystem pins are aligned before publishing. Dispatch the protected npm workflow from the current `main` workflow file when recovering publication for a release that has already passed Release Version Alignment.
79
+ `prepublishOnly` runs `npm run release:verify`, so a direct `npm publish` still has the default fast release gate. For GitHub releases, push or dispatch the matching package tag, such as `v2.7.1`, only after the release PR is merged. The release workflows verify that `package.json`, `package-lock.json`, `CHANGELOG.md`, generated dist banners, and ecosystem pins are aligned before publishing. Dispatch the protected npm workflow from the current `main` workflow file when recovering publication for a release that has already passed Release Version Alignment.
80
80
 
81
81
  The repository `NPM_TOKEN` secret must authenticate to npm as a user that appears in `npm owner ls ui-style-kit-css`. If the token belongs to another npm account or lacks package publish rights, npm may report a misleading registry `E404` at publish time.
82
82
 
@@ -88,4 +88,4 @@ npm run release:minor
88
88
  npm run release:major
89
89
  ```
90
90
 
91
- Use patch for compatible fixes, minor for new themes, presets, component capabilities, or browser-support contracts, and major for incompatible public API changes. Version `2.7.0` is a minor candidate because it adds shared component presentation and spacing controls while retaining the existing preset and semantic component APIs. Future feature versioning continues to follow this policy.
91
+ Use patch for compatible fixes, minor for new themes, presets, component capabilities, or browser-support contracts, and major for incompatible public API changes. Version `2.7.1` is a patch release because it repairs interaction paint and contrast while retaining the preset and semantic component APIs. Future feature versioning continues to follow this policy.
@@ -0,0 +1,21 @@
1
+ # v2.7.1 interaction contrast patch
2
+
3
+ This compatible patch preserves the 20 presets, 25 named color themes, native palettes, three modes, and existing public entrypoints.
4
+
5
+ ## Corrections
6
+
7
+ - Neutral hover rules no longer replace filled action backgrounds while retaining their foregrounds.
8
+ - Outlined CTAs retain readable surface ink at rest and their paired primary fill on hover. Neutral defaults exclude these paint-owning modifiers; Clay icon buttons use the control surface beneath their labels.
9
+ - Inline-link presentation excludes prefixed and semantic action classes. The build generates semantic aliases only for selectors that positively select a promoted component; exclusions alone cannot introduce a competing component rule. Variant exclusions also cover mixed prefixed/semantic markup used by React consumers.
10
+ - Primary, danger, warning, and current-navigation labels retain a matched foreground and background. Decorative reflections stay outside the central label lane.
11
+ - Neumorphism table values use readable surface ink, and Bento success and warning badge labels remain readable on translucent tints.
12
+ - Neo Noir warning paint belongs to its skewed face, preserving the component geometry.
13
+ - Y2K cut modifiers preserve primary action paint, and the compact trust seal contains its full number and caption.
14
+
15
+ ## Verification boundary
16
+
17
+ Focused Chromium regressions cover all presets, native plus named palettes, all modes, and rest, hover, active, keyboard-focus, and pressed-hover states. Fixtures include prefixed buttons, both semantic namespaces, mixed React class models, anchor actions, marketing CTAs, icon actions, native fields, disclosures, navigation, and tables. The React paint check asserts the real theme IDs visited, preventing a repeated native palette from masquerading as named-theme coverage.
18
+
19
+ Manual demo interaction uses the connected local Opera browser because Gnomie Browser Commander's runtime is unavailable in this session. Local evidence, protected GitHub release checks, npm publication, and live demo verification remain separate gates. No jsdoc2md generator is configured in this CSS package; JSDoc-compatible comments and the repository build maintain the generated artifacts.
20
+
21
+ Publication follows [PUBLISHING.md](PUBLISHING.md). The aligned companions remain Interactive Surface CSS 1.7.3 and Layout Style CSS 3.2.3.
package/manifest.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "breakingChange": "increment-schemaVersion-before-removing-or-renaming-fields"
6
6
  },
7
7
  "name": "ui-style-kit-css",
8
- "version": "2.7.0",
8
+ "version": "2.7.1",
9
9
  "cascadeLayers": [
10
10
  "ui-style-kit.theme_colors",
11
11
  "ui-style-kit.native_elements",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-style-kit-css",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "CSS theme and UI style preset library for accessible websites, dashboards, admin interfaces, and client-facing pages.",
5
5
  "style": "dist/ui-style-kit.css",
6
6
  "main": "dist/ui-style-kit.css",
@@ -438,7 +438,8 @@
438
438
  .deco-card, .deco-panel { background: var(--deco-card-bg); }
439
439
  .deco-toolbar, .deco-table-wrap { background: var(--deco-control-bg); }
440
440
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
441
- .deco-button:where(:not([data-ui-variant])):not(.deco-button-primary):not(.deco-button-secondary):not(.deco-button-danger):not(.deco-button-warning):not(.deco-button-ghost), .deco-icon-button { background: var(--deco-control-bg); }
441
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
442
+ .deco-button:where(:not([data-ui-variant])):not(.deco-button-primary):not(.deco-button-secondary):not(.deco-button-danger):not(.deco-button-warning):not(.deco-button-ghost):not(.deco-button-outline-heavy), .deco-icon-button { background: var(--deco-control-bg); }
442
443
  .deco-button, .deco-icon-button, .deco-badge, .deco-nav-link { font-family: var(--deco-font-control); }
443
444
 
444
445
 
@@ -630,7 +631,7 @@
630
631
  .deco-button.deco-button-outline-heavy,
631
632
  .deco-button-outline-heavy {
632
633
  background: transparent;
633
- color: var(--usk-primary-ink, var(--deco-primary));
634
+ color: var(--usk-primary-ink, var(--deco-text));
634
635
  border-style: solid;
635
636
  border-width: 2px;
636
637
  border-color: var(--deco-primary);
@@ -1058,6 +1059,12 @@
1058
1059
  }
1059
1060
  }
1060
1061
 
1062
+ /** Named palettes preserve their tested primary paint pair instead of tempering only the fill. */
1063
+ [data-ui="art-deco"][data-theme][data-mode] :is(.deco-button-primary, .deco-nav-link[aria-current="page"], .deco-nav-link.is-active) {
1064
+ background-color: var(--deco-primary);
1065
+ color: var(--deco-on-primary);
1066
+ }
1067
+
1061
1068
  /**
1062
1069
  * Lets explicit runtime token edits render exact user-supplied action colors
1063
1070
  * while untouched shared themes keep the tempered Art Deco jewel paint.
@@ -1524,7 +1531,9 @@
1524
1531
  text-indent: 0;
1525
1532
  }
1526
1533
 
1527
- .deco-button:where(:not([data-ui-variant])):not(.deco-button-primary):not(.deco-button-secondary):not(.deco-button-danger):not(.deco-button-warning):not(.deco-button-ghost),
1534
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
1535
+
1536
+ .deco-button:where(:not([data-ui-variant])):not(.deco-button-primary):not(.deco-button-secondary):not(.deco-button-danger):not(.deco-button-warning):not(.deco-button-ghost):not(.deco-button-outline-heavy),
1528
1537
  .deco-icon-button {
1529
1538
  background-color: var(--deco-surface-strong);
1530
1539
  background-image: none;
@@ -464,7 +464,8 @@
464
464
  .bau-card, .bau-panel { background: var(--bau-card-bg); }
465
465
  .bau-toolbar, .bau-table-wrap { background: var(--bau-control-bg); }
466
466
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
467
- .bau-button:where(:not([data-ui-variant])):not(.bau-button-primary):not(.bau-button-secondary):not(.bau-button-danger):not(.bau-button-warning):not(.bau-button-ghost):not(.is-primary):not(.is-secondary):not(.is-danger):not(.is-ghost):not(.is-busy):not(.is-hover), .bau-icon-button { background: var(--bau-control-bg); }
467
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
468
+ .bau-button:where(:not([data-ui-variant])):not(.bau-button-primary):not(.bau-button-secondary):not(.bau-button-danger):not(.bau-button-warning):not(.bau-button-ghost):not(.bau-button-outline-heavy):not(.is-primary):not(.is-secondary):not(.is-danger):not(.is-ghost):not(.is-busy):not(.is-hover), .bau-icon-button { background: var(--bau-control-bg); }
468
469
  .bau-button, .bau-icon-button, .bau-badge, .bau-nav-link { font-family: var(--bau-font-control); }
469
470
 
470
471
 
@@ -662,7 +663,7 @@
662
663
  .bau-button.bau-button-outline-heavy,
663
664
  .bau-button-outline-heavy {
664
665
  background: transparent;
665
- color: var(--usk-primary-ink, var(--bau-primary));
666
+ color: var(--usk-primary-ink, var(--bau-text));
666
667
  border-style: solid;
667
668
  border-width: 2px;
668
669
  border-color: var(--bau-primary);
@@ -950,7 +951,8 @@
950
951
  .bau-button-primary { background: var(--bau-primary); color: var(--bau-on-primary); border-color: var(--bau-primary); }
951
952
  .bau-button-secondary { background: var(--bau-secondary); color: var(--bau-on-secondary); border-color: var(--bau-secondary); }
952
953
  .bau-button-danger { background: var(--bau-danger); color: var(--bau-on-danger); border-color: var(--bau-danger); }
953
- .bau-button-ghost { background: transparent; color: var(--usk-primary-ink, var(--bau-primary)); border-color: transparent; }
954
+ /** Native palettes need surface ink when the primary fill is too dark for an unfilled action. */
955
+ .bau-button-ghost { background: transparent; color: var(--usk-primary-ink, var(--bau-text)); border-color: transparent; }
954
956
  }
955
957
 
956
958
  @layer ui-style-kit.bauhaus {
package/styles/bento.css CHANGED
@@ -454,7 +454,8 @@
454
454
  /* Theme-responsive component defaults */
455
455
  .bento-card, .bento-panel { background: var(--bento-card-bg); }
456
456
  .bento-toolbar, .bento-table-wrap { background: var(--bento-control-bg); }
457
- .bento-button:where(:not(.bento-button-primary):not(.bento-button-secondary):not(.bento-button-danger):not(.bento-button-warning):not(.bento-button-ghost):not(.is-primary):not(.is-secondary):not(.is-danger):not(.is-ghost):not(.is-hover):not(.is-busy)), .bento-icon-button { background: var(--bento-control-bg); }
457
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
458
+ .bento-button:where(:not(.bento-button-primary):not(.bento-button-secondary):not(.bento-button-danger):not(.bento-button-warning):not(.bento-button-ghost):not(.bento-button-outline-heavy):not(.is-primary):not(.is-secondary):not(.is-danger):not(.is-ghost):not(.is-hover):not(.is-busy)), .bento-icon-button { background: var(--bento-control-bg); }
458
459
  .bento-button, .bento-icon-button, .bento-badge, .bento-nav-link { font-family: var(--bento-font-control); }
459
460
 
460
461
 
@@ -500,8 +501,9 @@
500
501
  .bento-badge { display: inline-flex; align-items: center; gap: var(--bento-space-1); min-height: 1.75rem; padding: .38rem .72rem; background: var(--bento-tile-secondary-bg); color: var(--bento-text); border: 1px solid var(--bento-tile-edge); border-radius: var(--bento-radius-sm); box-shadow: inset 0 1px 0 rgb(var(--bento-surface-strong-rgb) / .62); font-size: .8125rem; font-weight: 800; line-height: 1; }
501
502
  .bento-badge-primary { background: var(--bento-primary); color: var(--bento-on-primary); border-color: var(--bento-primary); }
502
503
  .bento-badge-secondary { background: rgb(var(--bento-primary-rgb) / .10); color: var(--usk-primary-ink, var(--bento-primary)); border-color: transparent; }
503
- .bento-badge-success { background: rgb(var(--bento-success-rgb) / .14); color: var(--bento-success); border-color: transparent; }
504
- .bento-badge-warning { background: rgb(var(--bento-warning-rgb) / .14); color: var(--bento-warning); border-color: transparent; }
504
+ /** Warning and success tints use surface text rather than their solid signal fill as ink. */
505
+ .bento-badge-success { background: rgb(var(--bento-success-rgb) / .14); color: var(--bento-text); border-color: transparent; }
506
+ .bento-badge-warning { background: rgb(var(--bento-warning-rgb) / .14); color: var(--bento-text); border-color: transparent; }
505
507
  .bento-badge-danger { background: rgb(var(--bento-danger-rgb) / .14); color: var(--bento-danger); border-color: transparent; }
506
508
 
507
509
  .bento-nav { display: flex; flex-wrap: wrap; align-items: center; gap: var(--bento-space-2); padding: var(--bento-space-2); background: var(--bento-card-bg); border: 1px solid var(--bento-tile-edge); border-radius: var(--bento-tile-radius); box-shadow: var(--bento-tile-shadow); }
@@ -674,7 +676,7 @@
674
676
  .bento-button.bento-button-outline-heavy,
675
677
  .bento-button-outline-heavy {
676
678
  background: transparent;
677
- color: var(--usk-primary-ink, var(--bento-primary));
679
+ color: var(--usk-primary-ink, var(--bento-text));
678
680
  border-style: solid;
679
681
  border-width: 2px;
680
682
  border-color: var(--bento-primary);
@@ -817,7 +819,7 @@
817
819
  }
818
820
  .bento-button.bento-button-outline-heavy, .bento-button-outline-heavy {
819
821
  background: rgb(var(--bento-surface-strong-rgb) / var(--bento-panel-alpha));
820
- color: var(--usk-primary-ink, var(--bento-primary));
822
+ color: var(--usk-primary-ink, var(--bento-text));
821
823
  border-width: 1px;
822
824
  border-color: rgb(var(--bento-primary-rgb) / .32);
823
825
  border-radius: var(--bento-control-radius);
@@ -1210,7 +1212,6 @@
1210
1212
 
1211
1213
  [data-ui=bento][data-mode] :is(.bento-button, .bento-icon-button) {
1212
1214
  align-items: center;
1213
- background: var(--bento-mosaic-panel-soft);
1214
1215
  border: 1px solid var(--bento-mosaic-line);
1215
1216
  border-radius: 8px;
1216
1217
  display: inline-flex;
@@ -1221,6 +1222,8 @@
1221
1222
  padding: 0 16px;
1222
1223
  transition: background-color 150ms ease, border-color 150ms ease, box-shadow 150ms ease, transform 150ms ease;
1223
1224
  }
1225
+ /** Mosaic neutral paint yields to the outlined CTA's paired hover fill. */
1226
+ [data-ui=bento][data-mode] :is(.bento-button, .bento-icon-button):where(:not(.bento-button-outline-heavy)) { background: var(--bento-mosaic-panel-soft); }
1224
1227
  [data-ui=bento][data-mode] .bento-button:hover:not(:disabled) { transform: translateY(-1px); }
1225
1228
  [data-ui=bento][data-mode] .bento-button:active:not(:disabled) { transform: translateY(1px); }
1226
1229
  [data-ui=bento][data-mode] .bento-button.is-primary,
@@ -1595,7 +1598,8 @@
1595
1598
  [data-ui=bento][data-mode] .bento-card, [data-ui=bento][data-mode] .bento-panel { padding: 16px; }
1596
1599
  [data-ui=bento][data-mode] .bento-button, [data-ui=bento][data-mode] .bento-icon-button { border-radius: 8px; box-shadow: none; font-weight: 750; text-shadow: none; }
1597
1600
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
1598
- [data-ui=bento][data-mode] .bento-button:where(:not([data-ui-variant])):not(.bento-button-primary):not(.bento-button-secondary):not(.bento-button-danger):not(.bento-button-warning):not(.bento-button-ghost):not(.is-primary):not(.is-secondary):not(.is-danger):not(.is-ghost):not(.is-busy):not(.is-hover) { background: var(--bento-surface-soft); }
1601
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
1602
+ [data-ui=bento][data-mode] .bento-button:where(:not([data-ui-variant])):not(.bento-button-primary):not(.bento-button-secondary):not(.bento-button-danger):not(.bento-button-warning):not(.bento-button-ghost):not(.bento-button-outline-heavy):not(.is-primary):not(.is-secondary):not(.is-danger):not(.is-ghost):not(.is-busy):not(.is-hover) { background: var(--bento-surface-soft); }
1599
1603
  [data-ui=bento][data-mode] .bento-button.bento-button-primary, [data-ui=bento][data-mode] .bento-button.is-primary, [data-ui=bento][data-mode] .bento-button.is-busy { background: var(--bento-primary); color: var(--bento-on-primary); border-color: var(--bento-primary); }
1600
1604
  [data-ui=bento][data-mode] .bento-button.bento-button-secondary, [data-ui=bento][data-mode] .bento-button.is-secondary { background: transparent; color: var(--bento-link); border-color: var(--bento-primary); }
1601
1605
  [data-ui=bento][data-mode] .bento-button.bento-button-danger, [data-ui=bento][data-mode] .bento-button.is-danger { background: var(--bento-mosaic-danger-soft); color: var(--bento-text); border-color: var(--bento-danger); }
@@ -432,7 +432,8 @@
432
432
 
433
433
  [data-ui="brutalism"][data-mode] { background: var(--brutal-theme-bg); background-size: var(--brutal-theme-bg-size); }
434
434
 
435
- [data-ui="brutalism"] a { color: var(--brutal-link); text-decoration-thickness: 0.14em; text-underline-offset: 0.16em; font-weight: 800; }
435
+ /** Inline-link ink must not override the foreground paired with an action surface. */
436
+ [data-ui="brutalism"] a:where(:not(.brutal-button):not(.brutal-icon-button)) { color: var(--brutal-link); text-decoration-thickness: 0.14em; text-underline-offset: 0.16em; font-weight: 800; }
436
437
  [data-ui="brutalism"] ::selection { background: var(--brutal-primary); color: var(--brutal-on-primary); }
437
438
 
438
439
  /* Spinner geometry follows the brutalism preset's blunt mechanical edges. */
@@ -525,7 +526,8 @@
525
526
  .brutal-card, .brutal-panel { background: var(--brutal-card-bg); }
526
527
  .brutal-toolbar, .brutal-table-wrap { background: var(--brutal-control-bg); }
527
528
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
528
- .brutal-button:where(:not([data-ui-variant])):not(.brutal-button-primary):not(.brutal-button-secondary):not(.brutal-button-danger):not(.brutal-button-warning):not(.brutal-button-ghost), .brutal-icon-button { background: var(--brutal-control-bg); }
529
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
530
+ .brutal-button:where(:not([data-ui-variant])):not(.brutal-button-primary):not(.brutal-button-secondary):not(.brutal-button-danger):not(.brutal-button-warning):not(.brutal-button-ghost):not(.brutal-button-outline-heavy), .brutal-icon-button { background: var(--brutal-control-bg); }
529
531
  .brutal-button, .brutal-icon-button, .brutal-badge, .brutal-nav-link { font-family: var(--brutal-font-control); }
530
532
 
531
533
 
@@ -639,7 +641,7 @@
639
641
  .brutal-button.brutal-button-outline-heavy,
640
642
  .brutal-button-outline-heavy {
641
643
  background: transparent;
642
- color: var(--usk-primary-ink, var(--brutal-primary));
644
+ color: var(--usk-primary-ink, var(--brutal-text));
643
645
  border-style: solid;
644
646
  border-width: 2px;
645
647
  border-color: var(--brutal-primary);
package/styles/clay.css CHANGED
@@ -446,7 +446,8 @@
446
446
  .clay-card, .clay-panel { background: var(--clay-card-bg); }
447
447
  .clay-toolbar, .clay-table-wrap { background: var(--clay-control-bg); }
448
448
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
449
- .clay-button:where(:not([data-ui-variant])):not(.clay-button-primary):not(.clay-button-secondary):not(.clay-button-danger):not(.clay-button-warning):not(.clay-button-ghost), .clay-icon-button { background: var(--clay-control-bg); }
449
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
450
+ .clay-button:where(:not([data-ui-variant])):not(.clay-button-primary):not(.clay-button-secondary):not(.clay-button-danger):not(.clay-button-warning):not(.clay-button-ghost):not(.clay-button-outline-heavy), .clay-icon-button { background: var(--clay-control-bg); }
450
451
  .clay-button, .clay-icon-button, .clay-badge, .clay-nav-link { font-family: var(--clay-font-control); }
451
452
 
452
453
 
@@ -638,7 +639,7 @@
638
639
  .clay-button.clay-button-outline-heavy,
639
640
  .clay-button-outline-heavy {
640
641
  background: transparent;
641
- color: var(--usk-primary-ink, var(--clay-primary));
642
+ color: var(--usk-primary-ink, var(--clay-text));
642
643
  border-style: solid;
643
644
  border-width: 2px;
644
645
  border-color: var(--clay-primary);
@@ -895,7 +896,8 @@
895
896
  --clay-molded-track: var(--clay-grain-image), linear-gradient(180deg, var(--clay-material-contact), transparent 46%), var(--clay-material-inset);
896
897
  --clay-molded-raised: var(--clay-grain-image), linear-gradient(176deg, var(--clay-material-highlight), transparent 42%), var(--clay-material-raised);
897
898
  --clay-molded-thumb: var(--clay-grain-image), radial-gradient(circle at 34% 24%, var(--clay-material-highlight), transparent 46%), linear-gradient(176deg, var(--clay-material-highlight), transparent 44%), var(--clay-thumb-material);
898
- --clay-molded-primary: var(--clay-grain-image), linear-gradient(176deg, var(--clay-material-highlight), transparent 42%), var(--clay-pigment-primary);
899
+ /** Raised highlights stay above the label, preserving the pigment's paired ink. */
900
+ --clay-molded-primary: var(--clay-grain-image), linear-gradient(176deg, var(--clay-material-highlight), transparent 20%), var(--clay-pigment-primary);
899
901
  --clay-molded-success: var(--clay-grain-image), linear-gradient(176deg, var(--clay-material-highlight), transparent 42%), var(--clay-pigment-success);
900
902
  --clay-molded-warning: var(--clay-grain-image), linear-gradient(176deg, var(--clay-material-highlight), transparent 42%), var(--clay-pigment-warning);
901
903
  --clay-molded-danger: var(--clay-grain-image), linear-gradient(176deg, var(--clay-material-highlight), transparent 42%), var(--clay-pigment-danger);
@@ -1073,11 +1075,12 @@
1073
1075
  }
1074
1076
 
1075
1077
  /** Gives every button the shallow relief and debossed lettering shown in the reference. */
1078
+ /** Action and icon labels use the control surface; thumb pigment belongs to movable parts. */
1076
1079
  .clay-button, .clay-icon-button,
1077
1080
  [data-ui="clay"][data-mode] :where(button:not([class]), input[type="button"]:not([class]), input[type="submit"]:not([class]), input[type="reset"]:not([class])) {
1078
1081
  min-height: 2rem;
1079
1082
  padding: .34rem .64rem;
1080
- background-color: var(--clay-thumb-material);
1083
+ background-color: var(--clay-control-bg);
1081
1084
  background-image: var(--clay-grain-image), linear-gradient(174deg, var(--clay-material-highlight), transparent 34%);
1082
1085
  background-size: var(--clay-grain-size), auto;
1083
1086
  border: 1px solid var(--clay-material-seam);
@@ -1102,7 +1105,8 @@
1102
1105
  text-shadow: var(--clay-label-relief);
1103
1106
  }
1104
1107
  .clay-button-primary { background: var(--clay-molded-primary); color: var(--clay-pigment-ink); border-color: var(--clay-pigment-primary); }
1105
- .clay-button-primary:hover { background-color: var(--clay-pigment-primary-hover); border-color: var(--clay-pigment-primary-hover); }
1108
+ /** Hover changes the relief and edge without lightening the pigment beneath its label. */
1109
+ .clay-button-primary:hover { background: var(--clay-molded-primary); border-color: var(--clay-pigment-primary-hover); }
1106
1110
  .clay-button-secondary { background: var(--clay-grain-image), linear-gradient(176deg, var(--clay-material-highlight), transparent 42%), var(--clay-pigment-secondary); color: var(--clay-on-secondary); border-color: var(--clay-pigment-secondary); }
1107
1111
  .clay-button-secondary:hover { background-color: var(--clay-pigment-secondary); border-color: var(--clay-pigment-secondary); }
1108
1112
  .clay-button-danger { background: var(--clay-molded-danger); color: rgb(var(--clay-danger-text-rgb)); border-color: var(--clay-pigment-danger); }
@@ -1127,7 +1131,7 @@
1127
1131
  }
1128
1132
  .clay-button.clay-button-outline-heavy, .clay-button-outline-heavy {
1129
1133
  background-color: var(--clay-material-raised);
1130
- color: var(--usk-primary-ink, var(--clay-primary));
1134
+ color: var(--usk-primary-ink, var(--clay-text));
1131
1135
  border: 2px solid var(--clay-primary);
1132
1136
  clip-path: var(--clay-chip-clip);
1133
1137
  }
@@ -1229,7 +1233,8 @@
1229
1233
  .clay-badge-danger { background: var(--clay-grain-image), linear-gradient(176deg, var(--clay-material-highlight), transparent 42%), color-mix(in srgb, var(--clay-pigment-danger) 28%, var(--clay-material-raised)); color: var(--clay-material-ink); border-color: color-mix(in srgb, var(--clay-pigment-danger), var(--clay-material-seam) 52%); }
1230
1234
  }
1231
1235
  .clay-nav { gap: 0; padding: .18rem; background: var(--clay-material-inset); border-color: var(--clay-material-seam); border-radius: .48rem .6rem .52rem .56rem; box-shadow: var(--clay-carved-shadow); clip-path: var(--clay-control-clip); filter: var(--clay-edge-filter); }
1232
- .clay-nav-link { min-height: 2.1rem; padding: .42rem .62rem; border-radius: .3rem .38rem .32rem .36rem; clip-path: var(--clay-control-clip); font-weight: 500; text-shadow: var(--clay-label-relief); }
1236
+ /** Navigation labels retain full surface ink over the textured clay slab. */
1237
+ .clay-nav-link { min-height: 2.1rem; padding: .42rem .62rem; border-radius: .3rem .38rem .32rem .36rem; clip-path: var(--clay-control-clip); font-weight: 500; color: var(--clay-text); text-shadow: var(--clay-label-relief); }
1233
1238
  .clay-nav-link[aria-current="page"], .clay-nav-link.is-active { background: var(--clay-molded-primary); color: var(--clay-pigment-ink); box-shadow: var(--clay-filled-shadow); clip-path: var(--clay-control-clip); filter: var(--clay-edge-filter); }
1234
1239
 
1235
1240
  .clay-alert { padding: .7rem .82rem; background-color: var(--clay-material-raised); background-image: var(--clay-grain-image), linear-gradient(170deg, var(--clay-material-highlight), transparent 42%); background-size: var(--clay-grain-size), auto; border: 1px solid var(--clay-material-seam); border-inline-start-width: .28rem; border-radius: .42rem .55rem .46rem .52rem; box-shadow: var(--clay-relief-shadow); clip-path: var(--clay-control-clip); filter: var(--clay-edge-filter); }
@@ -427,7 +427,8 @@
427
427
  }
428
428
  [data-ui="cyberpunk"] *, [data-ui="cyberpunk"] *::before, [data-ui="cyberpunk"] *::after { box-sizing: border-box; }
429
429
  [data-ui="cyberpunk"][data-mode] { background: var(--cyber-theme-bg); background-size: var(--cyber-theme-bg-size); }
430
- [data-ui="cyberpunk"] a { color: var(--cyber-link); text-decoration-color: rgb(var(--cyber-primary-rgb) / 0.6); text-underline-offset: 0.18em; }
430
+ /** Inline-link ink must not override the foreground paired with an action surface. */
431
+ [data-ui="cyberpunk"] a:where(:not(.cyber-button):not(.cyber-icon-button)) { color: var(--cyber-link); text-decoration-color: rgb(var(--cyber-primary-rgb) / 0.6); text-underline-offset: 0.18em; }
431
432
  [data-ui="cyberpunk"] ::selection { background: var(--cyber-primary); color: var(--cyber-on-primary); }
432
433
 
433
434
  .cyber-page { position: relative; min-height: 100vh; padding: clamp(1rem, 4vw, 3rem); }
@@ -478,7 +479,8 @@
478
479
  .cyber-card, .cyber-panel { background: var(--cyber-card-bg); }
479
480
  .cyber-toolbar, .cyber-table-wrap { background: var(--cyber-control-bg); }
480
481
  /** Semantic variant attributes take precedence over the legacy neutral-control fallback. */
481
- .cyber-button:where(:not([data-ui-variant])):not(.cyber-button-primary):not(.cyber-button-secondary):not(.cyber-button-danger):not(.cyber-button-warning):not(.cyber-button-ghost), .cyber-icon-button { background: var(--cyber-control-bg); }
482
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
483
+ .cyber-button:where(:not([data-ui-variant])):not(.cyber-button-primary):not(.cyber-button-secondary):not(.cyber-button-danger):not(.cyber-button-warning):not(.cyber-button-ghost):not(.cyber-button-outline-heavy), .cyber-icon-button { background: var(--cyber-control-bg); }
482
484
  .cyber-button, .cyber-icon-button, .cyber-badge, .cyber-nav-link { font-family: var(--cyber-font-control); }
483
485
 
484
486
  /* Spinner geometry follows the cyberpunk preset's clipped neon readouts. */
@@ -580,7 +582,7 @@
580
582
  .cyber-button.cyber-button-outline-heavy,
581
583
  .cyber-button-outline-heavy {
582
584
  background: transparent;
583
- color: var(--usk-primary-ink, var(--cyber-primary));
585
+ color: var(--usk-primary-ink, var(--cyber-text));
584
586
  border-style: solid;
585
587
  border-width: 2px;
586
588
  border-color: var(--cyber-primary);
@@ -1047,7 +1049,7 @@
1047
1049
  .cyber-button:hover,
1048
1050
  .cyber-icon-button:hover {
1049
1051
  border-color: var(--cyber-primary);
1050
- color: var(--usk-primary-ink, var(--cyber-primary));
1052
+ color: var(--usk-primary-ink, var(--cyber-text));
1051
1053
  box-shadow: 0 0 .75rem rgb(var(--cyber-primary-rgb) / .2);
1052
1054
  }
1053
1055
 
@@ -1618,7 +1620,8 @@
1618
1620
  border-color: transparent;
1619
1621
  background-color: transparent;
1620
1622
  background-image: none;
1621
- color: var(--usk-primary-ink, var(--cyber-primary));
1623
+ /** Unfilled native controls use readable surface ink rather than neon fill pigment. */
1624
+ color: var(--usk-primary-ink, var(--cyber-text));
1622
1625
  box-shadow: none;
1623
1626
  }
1624
1627
 
@@ -1627,7 +1630,8 @@
1627
1630
  border-color: var(--cyber-danger);
1628
1631
  background-color: rgb(var(--cyber-danger-rgb) / .12);
1629
1632
  background-image: linear-gradient(135deg, rgb(var(--cyber-danger-rgb) / .16), transparent 62%);
1630
- color: var(--cyber-danger);
1633
+ /** The signal tint is decorative; its label uses the contrasting surface foreground. */
1634
+ color: var(--cyber-text);
1631
1635
  box-shadow: var(--cyber-template-danger-glow);
1632
1636
  }
1633
1637
 
@@ -438,7 +438,9 @@
438
438
  .terminal-card, .terminal-panel { background: var(--terminal-card-bg); }
439
439
  .terminal-toolbar, .terminal-table-wrap { background: var(--terminal-control-bg); }
440
440
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
441
- .terminal-button:where(:not([data-ui-variant])):not(.terminal-button-primary):not(.terminal-button-secondary):not(.terminal-button-danger):not(.terminal-button-warning):not(.terminal-button-ghost):not(.is-active):not([aria-pressed="true"]),
441
+ /** Native press state bypasses the neutral fallback just like persistent pressed hooks. */
442
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
443
+ .terminal-button:where(:not([data-ui-variant])):not(.terminal-button-primary):not(.terminal-button-secondary):not(.terminal-button-danger):not(.terminal-button-warning):not(.terminal-button-ghost):not(.terminal-button-outline-heavy):not(.is-active):not([aria-pressed="true"]):not(:active),
442
444
  .terminal-icon-button:not(.is-active):not([aria-pressed="true"]) { background: var(--terminal-control-bg); }
443
445
  .terminal-button, .terminal-icon-button, .terminal-badge, .terminal-nav-link { font-family: var(--terminal-font-control); }
444
446
 
@@ -631,7 +633,7 @@
631
633
  .terminal-button.terminal-button-outline-heavy,
632
634
  .terminal-button-outline-heavy {
633
635
  background: transparent;
634
- color: var(--usk-primary-ink, var(--terminal-primary));
636
+ color: var(--usk-primary-ink, var(--terminal-text));
635
637
  border-style: solid;
636
638
  border-width: 2px;
637
639
  border-color: var(--terminal-primary);
@@ -438,7 +438,8 @@
438
438
  .luxe-card, .luxe-panel { background: var(--luxe-card-bg); }
439
439
  .luxe-toolbar, .luxe-table-wrap { background: var(--luxe-control-bg); }
440
440
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
441
- .luxe-button:where(:not([data-ui-variant])):not(.luxe-button-primary):not(.luxe-button-secondary):not(.luxe-button-danger):not(.luxe-button-warning):not(.luxe-button-ghost), .luxe-icon-button { background: var(--luxe-control-bg); }
441
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
442
+ .luxe-button:where(:not([data-ui-variant])):not(.luxe-button-primary):not(.luxe-button-secondary):not(.luxe-button-danger):not(.luxe-button-warning):not(.luxe-button-ghost):not(.luxe-button-outline-heavy), .luxe-icon-button { background: var(--luxe-control-bg); }
442
443
  .luxe-button, .luxe-icon-button, .luxe-badge, .luxe-nav-link { font-family: var(--luxe-font-control); }
443
444
 
444
445
 
@@ -630,7 +631,7 @@
630
631
  .luxe-button.luxe-button-outline-heavy,
631
632
  .luxe-button-outline-heavy {
632
633
  background: transparent;
633
- color: var(--usk-primary-ink, var(--luxe-primary));
634
+ color: var(--usk-primary-ink, var(--luxe-text));
634
635
  border-style: solid;
635
636
  border-width: 2px;
636
637
  border-color: var(--luxe-primary);
@@ -438,7 +438,8 @@
438
438
  .utility-card, .utility-panel { background: var(--utility-card-bg); }
439
439
  .utility-toolbar, .utility-table-wrap { background: var(--utility-control-bg); }
440
440
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
441
- .utility-button:where(:not([data-ui-variant])):not(.utility-button-primary):not(.utility-button-secondary):not(.utility-button-danger):not(.utility-button-warning):not(.utility-button-ghost):not(.utility-button-guarded), .utility-icon-button { background: var(--utility-control-bg); }
441
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
442
+ .utility-button:where(:not([data-ui-variant])):not(.utility-button-primary):not(.utility-button-secondary):not(.utility-button-danger):not(.utility-button-warning):not(.utility-button-ghost):not(.utility-button-outline-heavy):not(.utility-button-guarded), .utility-icon-button { background: var(--utility-control-bg); }
442
443
  .utility-button, .utility-icon-button, .utility-badge, .utility-nav-link { font-family: var(--utility-font-control); }
443
444
 
444
445
 
@@ -630,7 +631,7 @@
630
631
  .utility-button.utility-button-outline-heavy,
631
632
  .utility-button-outline-heavy {
632
633
  background: transparent;
633
- color: var(--usk-primary-ink, var(--utility-primary));
634
+ color: var(--usk-primary-ink, var(--utility-text));
634
635
  border-style: solid;
635
636
  border-width: 2px;
636
637
  border-color: var(--utility-primary);
@@ -944,7 +945,8 @@
944
945
  @media print {
945
946
  [data-ui="industrial-utility"] { background: #fff !important; color: #000 !important; }
946
947
  [data-ui="industrial-utility"] * { box-shadow: none !important; text-shadow: none !important; }
947
- [data-ui="industrial-utility"] a { color: #000 !important; text-decoration: underline; }
948
+ /** Inline-link ink must not override the foreground paired with an action surface. */
949
+ [data-ui="industrial-utility"] a { color: #000 !important; text-decoration: underline; }
948
950
  }
949
951
  }
950
952
 
@@ -1096,15 +1098,15 @@
1096
1098
  outline-offset: 2px;
1097
1099
  }
1098
1100
 
1099
- [data-ui="industrial-utility"] a {
1101
+ [data-ui="industrial-utility"] a:where(:not(.utility-button):not(.utility-icon-button):not(.utility-nav-link)) {
1100
1102
  color: var(--utility-material-lamp-blue);
1101
1103
  font-weight: 700;
1102
1104
  text-decoration-thickness: 1px;
1103
1105
  text-underline-offset: 3px;
1104
1106
  }
1105
1107
 
1106
- [data-ui="industrial-utility"] a:hover,
1107
- [data-ui="industrial-utility"] a.is-hover {
1108
+ [data-ui="industrial-utility"] a:where(:not(.utility-button):not(.utility-icon-button):not(.utility-nav-link)):hover,
1109
+ [data-ui="industrial-utility"] a:where(:not(.utility-button):not(.utility-icon-button):not(.utility-nav-link)).is-hover {
1108
1110
  color: var(--utility-material-warning);
1109
1111
  }
1110
1112
 
@@ -440,7 +440,8 @@
440
440
  .max-button:hover, .max-icon-button:hover { transform: translate(-2px, -2px); border-color: var(--max-ink); box-shadow: .4rem .4rem 0 rgb(var(--max-secondary-rgb) / .76); }
441
441
  .max-button:active, .max-icon-button:active, .max-button[aria-pressed="true"], .max-icon-button[aria-pressed="true"], .max-button.is-active, .max-icon-button.is-active { transform: translate(.12rem, .12rem); box-shadow: .12rem .12rem 0 var(--max-ink); }
442
442
  .max-button-primary { background: var(--max-poster-fill); color: var(--max-on-primary); border-color: var(--max-ink); }
443
- .max-button-primary:hover { background: linear-gradient(110deg, var(--max-primary-hover), var(--max-danger), var(--max-accent)); border-color: var(--max-ink); }
443
+ /** A single foreground can span only gradient stops from its paired color role. */
444
+ .max-button-primary:hover { background: linear-gradient(110deg, var(--max-primary-hover), var(--max-primary)); border-color: var(--max-ink); }
444
445
  .max-button-secondary { background: var(--max-secondary); color: var(--max-on-secondary); border-color: var(--max-secondary); }
445
446
  .max-button-secondary:hover { background: var(--max-secondary-hover); border-color: var(--max-secondary-hover); }
446
447
  .max-button-danger { background: var(--max-danger); color: var(--max-on-danger); border-color: var(--max-ink); }
@@ -452,6 +453,7 @@
452
453
  .max-card, .max-panel { background: var(--max-card-bg); }
453
454
  .max-toolbar, .max-table-wrap { background: var(--max-control-bg); }
454
455
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
456
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
455
457
  .max-button:where(:not([data-ui-variant])):not(.max-button-primary):not(.max-button-secondary):not(.max-button-danger):not(.max-button-warning):not(.max-button-ghost):not(.max-button-outline-heavy), .max-icon-button { background: var(--max-control-bg); }
456
458
  .max-button, .max-icon-button, .max-badge, .max-nav-link { font-family: var(--max-font-control); }
457
459
 
@@ -430,7 +430,10 @@
430
430
  font: inherit; font-weight: 700; line-height: 1; text-decoration: none; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; user-select: none;
431
431
  transition: color var(--saas-transition-fast), background var(--saas-transition-fast), border-color var(--saas-transition-fast), box-shadow var(--saas-transition-fast), transform var(--saas-transition-fast);
432
432
  }
433
- .saas-button:hover, .saas-icon-button:hover { transform: none; border-color: rgb(var(--saas-primary-rgb) / .48); background: var(--saas-surface-soft); }
433
+ /** Neutral hover paint must not replace a filled variant's paired background. */
434
+ .saas-button:hover, .saas-icon-button:hover { transform: none; border-color: rgb(var(--saas-primary-rgb) / .48); }
435
+ .saas-button:where(:not(.saas-button-primary):not(.saas-button-danger):not(.saas-button-warning)):hover,
436
+ .saas-icon-button:hover { background: var(--saas-surface-soft); color: var(--saas-text); }
434
437
  .saas-button:active, .saas-icon-button:active, .saas-button[aria-pressed="true"], .saas-icon-button[aria-pressed="true"], .saas-button.is-active, .saas-icon-button.is-active { transform: translateY(1px); box-shadow: inset 0 1px 2px rgb(var(--saas-text-rgb) / .12); }
435
438
  .saas-button-primary { background: linear-gradient(135deg, var(--saas-primary), var(--saas-brand-2)); color: var(--saas-on-primary); border-color: var(--saas-primary); }
436
439
  .saas-button-primary:hover { background: linear-gradient(135deg, var(--saas-brand-2), var(--saas-primary)); border-color: var(--saas-primary-hover); }
@@ -445,7 +448,8 @@
445
448
  .saas-card, .saas-panel { background: var(--saas-card-bg); }
446
449
  .saas-toolbar, .saas-table-wrap { background: var(--saas-control-bg); }
447
450
  /** Attribute-selected variants bypass the legacy neutral-only paint fallback. */
448
- .saas-button:where(:not([data-ui-variant])):not(.saas-button-primary):not(.saas-button-secondary):not(.saas-button-danger):not(.saas-button-warning):not(.saas-button-ghost), .saas-icon-button { background: var(--saas-control-bg); }
451
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
452
+ .saas-button:where(:not([data-ui-variant])):not(.saas-button-primary):not(.saas-button-secondary):not(.saas-button-danger):not(.saas-button-warning):not(.saas-button-ghost):not(.saas-button-outline-heavy), .saas-icon-button { background: var(--saas-control-bg); }
449
453
  .saas-button, .saas-icon-button, .saas-badge, .saas-nav-link { font-family: var(--saas-font-control); }
450
454
 
451
455
 
@@ -652,7 +656,7 @@
652
656
  .saas-button.saas-button-outline-heavy,
653
657
  .saas-button-outline-heavy {
654
658
  background: transparent;
655
- color: var(--usk-primary-ink, var(--saas-primary));
659
+ color: var(--usk-primary-ink, var(--saas-text));
656
660
  border-style: solid;
657
661
  border-width: 2px;
658
662
  border-color: var(--saas-primary);
@@ -437,7 +437,8 @@
437
437
  /* Theme-responsive component defaults */
438
438
  .noir-card, .noir-panel { background: var(--noir-card-bg); }
439
439
  .noir-toolbar, .noir-table-wrap { background: var(--noir-control-bg); }
440
- .noir-button:where(:not(.noir-button-primary):not(.noir-button-secondary):not(.noir-button-danger):not(.noir-button-warning):not(.noir-button-ghost)), .noir-icon-button { background: var(--noir-control-bg); }
440
+ /** Neutral defaults exclude outlined actions so their hover fill and ink remain paired. */
441
+ .noir-button:where(:not(.noir-button-primary):not(.noir-button-secondary):not(.noir-button-danger):not(.noir-button-warning):not(.noir-button-ghost):not(.noir-button-outline-heavy)), .noir-icon-button { background: var(--noir-control-bg); }
441
442
  .noir-button, .noir-icon-button, .noir-badge, .noir-nav-link { font-family: var(--noir-font-control); }
442
443
 
443
444
 
@@ -629,17 +630,20 @@
629
630
  .noir-button.noir-button-outline-heavy,
630
631
  .noir-button-outline-heavy {
631
632
  background: transparent;
632
- color: var(--usk-primary-ink, var(--noir-primary));
633
+ color: var(--usk-primary-ink, var(--noir-text));
633
634
  border-style: solid;
634
635
  border-width: 2px;
635
636
  border-color: var(--noir-primary);
636
637
  box-shadow: none;
637
638
  }
639
+ /** Outlined hover paints the skewed face together with its on-primary label. */
638
640
  .noir-button.noir-button-outline-heavy:hover,
639
641
  .noir-button-outline-heavy:hover {
640
- background: var(--noir-primary);
642
+ background: transparent;
641
643
  color: var(--noir-on-primary);
642
644
  border-color: var(--noir-primary-hover);
645
+ --noir-action-fill: var(--noir-primary);
646
+ --noir-action-line: var(--noir-primary-hover);
643
647
  }
644
648
 
645
649
  .noir-badge-seal {
@@ -1300,7 +1304,10 @@
1300
1304
  [data-ui="neo-noir"][data-mode="contrast"] { --noir-grain: none; --noir-corner: none; --noir-border-alpha: 1; }
1301
1305
  /** Warning actions inherit preset geometry and use the paired semantic warning colors. */
1302
1306
  [data-ui="neo-noir"][data-mode] .noir-button.noir-button-warning {
1303
- background: var(--noir-warning);
1307
+ /** The skewed face owns the warning fill; a rectangular host fill sits behind it. */
1308
+ --noir-action-fill: var(--noir-warning);
1309
+ --noir-action-line: var(--noir-warning);
1310
+ background: transparent;
1304
1311
  color: var(--noir-on-warning);
1305
1312
  border-color: var(--noir-warning);
1306
1313
  text-shadow: none;