@yahoo/uds 3.156.2 → 3.157.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 (180) hide show
  1. package/dist/automated-config/dist/generated/autoVariants.cjs +9 -4
  2. package/dist/automated-config/dist/generated/autoVariants.d.cts +2 -1
  3. package/dist/automated-config/dist/generated/autoVariants.d.ts +2 -1
  4. package/dist/automated-config/dist/generated/autoVariants.js +9 -4
  5. package/dist/automated-config/dist/generated/generatedConfigs.cjs +3078 -3089
  6. package/dist/automated-config/dist/generated/generatedConfigs.d.cts +158 -141
  7. package/dist/automated-config/dist/generated/generatedConfigs.d.ts +158 -141
  8. package/dist/automated-config/dist/generated/generatedConfigs.js +3078 -3089
  9. package/dist/automated-config/dist/generated/universalTokensConfigAuto.cjs +1217 -491
  10. package/dist/automated-config/dist/generated/universalTokensConfigAuto.d.cts +4 -1
  11. package/dist/automated-config/dist/generated/universalTokensConfigAuto.d.ts +4 -1
  12. package/dist/automated-config/dist/generated/universalTokensConfigAuto.js +1217 -491
  13. package/dist/automated-config/dist/properties.cjs +9 -2
  14. package/dist/automated-config/dist/properties.d.cts +23 -0
  15. package/dist/automated-config/dist/properties.d.ts +23 -0
  16. package/dist/automated-config/dist/properties.js +9 -2
  17. package/dist/automated-config/dist/types/ComponentConfig.d.cts +77 -4
  18. package/dist/automated-config/dist/types/ComponentConfig.d.ts +77 -4
  19. package/dist/automated-config/dist/types/ConfigSchema.d.cts +14 -2
  20. package/dist/automated-config/dist/types/ConfigSchema.d.ts +14 -2
  21. package/dist/automated-config/dist/types/StateAxis.cjs +90 -0
  22. package/dist/automated-config/dist/types/StateAxis.d.cts +70 -0
  23. package/dist/automated-config/dist/types/StateAxis.d.ts +70 -0
  24. package/dist/automated-config/dist/types/StateAxis.js +84 -0
  25. package/dist/automated-config/dist/utils/buildConfigSchema.cjs +105 -82
  26. package/dist/automated-config/dist/utils/buildConfigSchema.d.cts +41 -12
  27. package/dist/automated-config/dist/utils/buildConfigSchema.d.ts +41 -12
  28. package/dist/automated-config/dist/utils/buildConfigSchema.js +106 -83
  29. package/dist/automated-config/dist/utils/canonicalizeStateKey.cjs +32 -0
  30. package/dist/automated-config/dist/utils/canonicalizeStateKey.d.cts +48 -0
  31. package/dist/automated-config/dist/utils/canonicalizeStateKey.d.ts +48 -0
  32. package/dist/automated-config/dist/utils/canonicalizeStateKey.js +31 -0
  33. package/dist/automated-config/dist/utils/getButtonControlHeight.cjs +129 -0
  34. package/dist/automated-config/dist/utils/getButtonControlHeight.d.cts +133 -0
  35. package/dist/automated-config/dist/utils/getButtonControlHeight.d.ts +133 -0
  36. package/dist/automated-config/dist/utils/getButtonControlHeight.js +122 -0
  37. package/dist/automated-config/dist/utils/getButtonControlHeightPx.cjs +114 -0
  38. package/dist/automated-config/dist/utils/getButtonControlHeightPx.d.cts +78 -0
  39. package/dist/automated-config/dist/utils/getButtonControlHeightPx.d.ts +78 -0
  40. package/dist/automated-config/dist/utils/getButtonControlHeightPx.js +108 -0
  41. package/dist/automated-config/dist/utils/getConfigComponentVariant.d.cts +8 -0
  42. package/dist/automated-config/dist/utils/getConfigComponentVariant.d.ts +8 -0
  43. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.cts +3 -3
  44. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.ts +3 -3
  45. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.cjs +12 -5
  46. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.d.cts +8 -1
  47. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.d.ts +8 -1
  48. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.js +12 -5
  49. package/dist/automated-config/dist/utils/iconButtonMatchButtonHeight.cjs +110 -0
  50. package/dist/automated-config/dist/utils/iconButtonMatchButtonHeight.d.cts +19 -0
  51. package/dist/automated-config/dist/utils/iconButtonMatchButtonHeight.d.ts +19 -0
  52. package/dist/automated-config/dist/utils/iconButtonMatchButtonHeight.js +108 -0
  53. package/dist/automated-config/dist/utils/index.cjs +478 -111
  54. package/dist/automated-config/dist/utils/index.d.cts +76 -15
  55. package/dist/automated-config/dist/utils/index.d.ts +76 -15
  56. package/dist/automated-config/dist/utils/index.js +479 -113
  57. package/dist/automated-config/dist/utils/isConfiguratorPropertyVisible.cjs +15 -0
  58. package/dist/automated-config/dist/utils/isConfiguratorPropertyVisible.d.cts +20 -0
  59. package/dist/automated-config/dist/utils/isConfiguratorPropertyVisible.d.ts +20 -0
  60. package/dist/automated-config/dist/utils/isConfiguratorPropertyVisible.js +15 -0
  61. package/dist/automated-config/dist/utils/pseudoStateSelectors.cjs +122 -0
  62. package/dist/automated-config/dist/utils/pseudoStateSelectors.d.cts +80 -0
  63. package/dist/automated-config/dist/utils/pseudoStateSelectors.d.ts +80 -0
  64. package/dist/automated-config/dist/utils/pseudoStateSelectors.js +120 -0
  65. package/dist/automated-config/dist/utils/resolvePropertyStates.cjs +131 -0
  66. package/dist/automated-config/dist/utils/resolvePropertyStates.d.cts +49 -0
  67. package/dist/automated-config/dist/utils/resolvePropertyStates.d.ts +49 -0
  68. package/dist/automated-config/dist/utils/resolvePropertyStates.js +130 -0
  69. package/dist/automated-config/dist/utils/resolveSlotByCascade.cjs +118 -0
  70. package/dist/automated-config/dist/utils/resolveSlotByCascade.d.cts +68 -0
  71. package/dist/automated-config/dist/utils/resolveSlotByCascade.d.ts +68 -0
  72. package/dist/automated-config/dist/utils/resolveSlotByCascade.js +117 -0
  73. package/dist/automated-config/dist/utils/variantConfigGuards.d.cts +13 -0
  74. package/dist/automated-config/dist/utils/variantConfigGuards.d.ts +13 -0
  75. package/dist/components/client/Button.cjs +3 -3
  76. package/dist/components/client/Button.js +3 -3
  77. package/dist/components/client/Input/Input.cjs +42 -6
  78. package/dist/components/client/Input/Input.d.cts +13 -0
  79. package/dist/components/client/Input/Input.d.ts +13 -0
  80. package/dist/components/client/Input/Input.js +42 -6
  81. package/dist/components/client/Menu/Menu.ItemCheckbox.d.cts +1 -1
  82. package/dist/components/client/Menu/Menu.ItemCheckbox.d.ts +1 -1
  83. package/dist/config/dist/index.cjs +224 -553
  84. package/dist/config/dist/index.js +224 -553
  85. package/dist/css/dist/commands/css.cjs +1 -0
  86. package/dist/css/dist/commands/css.helpers.cjs +6 -0
  87. package/dist/css/dist/commands/css.helpers.js +6 -0
  88. package/dist/css/dist/commands/css.js +1 -0
  89. package/dist/css/dist/css/generate.cjs +4 -2
  90. package/dist/css/dist/css/generate.d.cts +28 -0
  91. package/dist/css/dist/css/generate.d.ts +28 -0
  92. package/dist/css/dist/css/generate.helpers.cjs +5 -1
  93. package/dist/css/dist/css/generate.helpers.js +6 -2
  94. package/dist/css/dist/css/generate.js +4 -2
  95. package/dist/css/dist/css/postcss.cjs +81 -0
  96. package/dist/css/dist/css/postcss.helpers.cjs +60 -0
  97. package/dist/css/dist/css/postcss.helpers.js +59 -1
  98. package/dist/css/dist/css/postcss.js +82 -2
  99. package/dist/css/dist/css/runner.cjs +12 -2
  100. package/dist/css/dist/css/runner.js +12 -2
  101. package/dist/css/dist/css/theme.d.cts +6 -0
  102. package/dist/css/dist/css/theme.d.ts +6 -0
  103. package/dist/css/dist/packages/automated-config/dist/properties.cjs +9 -2
  104. package/dist/css/dist/packages/automated-config/dist/properties.js +9 -2
  105. package/dist/css/dist/packages/automated-config/dist/utils/index.d.cts +6 -0
  106. package/dist/css/dist/packages/automated-config/dist/utils/index.d.ts +6 -0
  107. package/dist/css/dist/packages/config/dist/index.cjs +224 -553
  108. package/dist/css/dist/packages/config/dist/index.js +224 -553
  109. package/dist/css/dist/utils/optimizeCSS.cjs +59 -0
  110. package/dist/css/dist/utils/optimizeCSS.js +59 -0
  111. package/dist/css-tokens/dist/index.cjs +2 -0
  112. package/dist/css-tokens/dist/index.d.cts +2 -1
  113. package/dist/css-tokens/dist/index.d.ts +2 -1
  114. package/dist/css-tokens/dist/index.js +2 -1
  115. package/dist/index.cjs +48 -0
  116. package/dist/index.d.cts +15 -4
  117. package/dist/index.d.ts +15 -4
  118. package/dist/index.js +14 -3
  119. package/dist/styles/styler.d.cts +8 -7
  120. package/dist/styles/styler.d.ts +8 -7
  121. package/dist/styles/variants.d.cts +9 -4
  122. package/dist/styles/variants.d.ts +9 -4
  123. package/dist/tailwind-internal/dist/packages/automated-config/dist/generated/generatedConfigs.cjs +3078 -3089
  124. package/dist/tailwind-internal/dist/packages/automated-config/dist/generated/generatedConfigs.js +3078 -3089
  125. package/dist/tailwind-internal/dist/packages/automated-config/dist/properties.cjs +9 -2
  126. package/dist/tailwind-internal/dist/packages/automated-config/dist/properties.js +9 -2
  127. package/dist/tailwind-internal/dist/packages/automated-config/dist/types/StateAxis.cjs +81 -0
  128. package/dist/tailwind-internal/dist/packages/automated-config/dist/types/StateAxis.js +76 -0
  129. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/canonicalizeStateKey.cjs +33 -0
  130. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/canonicalizeStateKey.js +32 -0
  131. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/componentStatePseudoStates.cjs +0 -7
  132. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/componentStatePseudoStates.js +1 -7
  133. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/getButtonControlHeight.cjs +126 -0
  134. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/getButtonControlHeight.js +123 -0
  135. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/iconButtonMatchButtonHeight.cjs +20 -0
  136. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/iconButtonMatchButtonHeight.js +20 -0
  137. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.cjs +422 -111
  138. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.d.cts +6 -0
  139. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.d.ts +6 -0
  140. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.js +423 -112
  141. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/pseudoStateSelectors.cjs +122 -0
  142. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/pseudoStateSelectors.js +121 -0
  143. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolvePropertyStates.cjs +132 -0
  144. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolvePropertyStates.js +131 -0
  145. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolveSlotByCascade.cjs +95 -0
  146. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolveSlotByCascade.js +95 -0
  147. package/dist/tailwind-internal/dist/packages/config/dist/index.cjs +224 -553
  148. package/dist/tailwind-internal/dist/packages/config/dist/index.js +224 -553
  149. package/dist/tailwind-internal/dist/packages/css-tokens/dist/index.cjs +2 -0
  150. package/dist/tailwind-internal/dist/packages/css-tokens/dist/index.js +2 -1
  151. package/dist/tailwind-internal/dist/plugins/components.cjs +28 -24
  152. package/dist/tailwind-internal/dist/plugins/components.js +28 -24
  153. package/dist/tailwind-internal/dist/utils/composeTailwindPlugins.d.cts +3 -0
  154. package/dist/tailwind-internal/dist/utils/composeTailwindPlugins.d.ts +3 -0
  155. package/dist/tailwind-internal/dist/utils/getShadowStyles.d.cts +2 -2
  156. package/dist/tailwind-internal/dist/utils/getShadowStyles.d.ts +2 -2
  157. package/dist/tokens/automation/index.cjs +47 -0
  158. package/dist/tokens/automation/index.d.cts +13 -2
  159. package/dist/tokens/automation/index.d.ts +13 -2
  160. package/dist/tokens/automation/index.js +13 -2
  161. package/dist/tokens/consts/cssTokens.cjs +1 -0
  162. package/dist/tokens/consts/cssTokens.d.cts +2 -2
  163. package/dist/tokens/consts/cssTokens.d.ts +2 -2
  164. package/dist/tokens/consts/cssTokens.js +2 -2
  165. package/dist/tokens/index.cjs +48 -0
  166. package/dist/tokens/index.d.cts +15 -4
  167. package/dist/tokens/index.d.ts +15 -4
  168. package/dist/tokens/index.js +14 -3
  169. package/dist/tokens/types.d.cts +1 -1
  170. package/dist/tokens/types.d.ts +1 -1
  171. package/dist/uds/generated/componentData.cjs +2177 -2172
  172. package/dist/uds/generated/componentData.js +2177 -2172
  173. package/dist/uds/generated/migrationSchemaVersion.cjs +1 -1
  174. package/dist/uds/generated/migrationSchemaVersion.js +1 -1
  175. package/dist/uds/generated/tailwindPurge.cjs +79 -78
  176. package/dist/uds/generated/tailwindPurge.js +79 -78
  177. package/generated/componentData.json +2718 -2713
  178. package/generated/migrationSchemaVersion.ts +1 -1
  179. package/generated/tailwindPurge.ts +2 -2
  180. package/package.json +1 -1
@@ -2,21 +2,31 @@
2
2
  import { BUTTON_GAP_VAR, BUTTON_SCALE_EFFECT_HOVER, BUTTON_SCALE_EFFECT_PRESSED, BUTTON_SCALE_EFFECT_REST } from "../../../css-tokens/dist/index.js";
3
3
  import { mapTextVariantFixtureToValue } from "../mapTextVariantFixtureToValue.js";
4
4
  import { configurableProperties } from "../properties.js";
5
- import { filterPseudoStatesByExclusion, getExcludedPseudoStatesForComponentStateOption } from "./componentStatePseudoStates.js";
5
+ import { INTERACTIVE_ATOMICS, MODIFIER_ATOMICS, comparePriority } from "../types/StateAxis.js";
6
+ import { getExcludedPseudoStatesForComponentStateOption } from "./componentStatePseudoStates.js";
6
7
  import { generateSchemaKey } from "./generateSchemaKey.js";
8
+ import "./canonicalizeStateKey.js";
9
+ import { atomicAndCompoundStateKeys, resolvePropertyStates } from "./resolvePropertyStates.js";
7
10
  import { isVariantConfigWithComponentStates, isVariantConfigWithProperties } from "./variantConfigGuards.js";
8
11
  import { findFixtureTypeForValue } from "./buildConfigSchema.js";
9
12
  import { generateDefaultClassName } from "./generateDefaultClassName.js";
13
+ import { applyButtonControlHeightDeclarations, applyIconButtonControlHeightDeclarations, getButtonContentLineHeight } from "./getButtonControlHeight.js";
14
+ import "./iconButtonMatchButtonHeight.js";
15
+ import "./getButtonControlHeightPx.js";
16
+ import { getStateDocsClass, getStateSelector } from "./pseudoStateSelectors.js";
17
+ import { resolveSlotByCascade } from "./resolveSlotByCascade.js";
10
18
  import "./cartesianProduct.js";
11
19
  import "./subcomponents.js";
12
20
  import "./coalesceConfigVariant.js";
13
21
  import "./defaults.js";
14
22
  import "./generateKeyFromFlatConfigPath.js";
23
+ import "./getConfigComponentVariant.js";
15
24
  import "./getConfigVariantComponentStatesMatrix.js";
16
25
  import "./getConfigVariantProperties.js";
17
26
  import "./getConfigVariantPseudoStates.js";
18
27
  import "./getConfigVariants.js";
19
28
  import "./getPaginationControlWidthPx.js";
29
+ import "./isConfiguratorPropertyVisible.js";
20
30
  import { isFunction, mergeWith } from "lodash-es";
21
31
  //#region ../automated-config/dist/utils/index.js
22
32
  /*! © 2026 Yahoo, Inc. UDS Default Config v0.0.0-development */
@@ -70,47 +80,59 @@ const COMPONENTS_WITH_SHADOW_BORDERS = [
70
80
  }
71
81
  ];
72
82
  /**
73
- * Map our "states" to pseudo-classes (hover => :hover, pressed => :active, etc.).
74
- * Adjust or extend as needed.
83
+ * Legacy state keys still used by unmigrated component configs via per-property
84
+ * `pseudoStates` arrays. They aren't in the new `StateAxis` taxonomy (so the
85
+ * resolver/StateBuilder don't auto-compose them) but they must remain in
86
+ * `KNOWN_STATE_KEYS` so the docs-mode map / configurator safelist still cover
87
+ * configs that emit them. Drop entries from this list as components migrate.
75
88
  */
76
- const statePseudoMap = {
77
- rest: "",
78
- hover: ":hover:not(:active, :disabled, :has(:disabled))",
79
- pressed: ":active:not(:disabled, :has(:disabled))",
80
- disabled: ":is(:disabled, :has(:disabled))",
81
- visited: ":visited",
82
- focused: ":focus",
83
- "focus-within": ":focus-within",
84
- "focused-keyboard": ":focus-visible",
85
- readonly: ":has(input:read-only)",
86
- invalid: ":has(input[aria-invalid=\"true\"])",
87
- "invalid&hover": ":has(input[aria-invalid=\"true\"]):hover:not(:active, :disabled, :has(:disabled))",
88
- "invalid&pressed": ":has(input[aria-invalid=\"true\"]):active:not(:disabled, :has(:disabled))"
89
- };
90
- const statePseudoMapDocsMode = {
91
- rest: "",
92
- hover: "hover",
93
- pressed: "active",
94
- disabled: "has-disabled",
95
- visited: "visited",
96
- focused: "focus",
97
- "focus-within": "focus-within",
98
- "focused-keyboard": "focus-visible",
99
- readonly: "input-readonly",
100
- invalid: "has-input-invalid",
101
- "invalid&hover": "has-input-invalid-and-hover",
102
- "invalid&pressed": "has-input-invalid-and-active"
103
- };
89
+ const LEGACY_STATE_KEYS = [
90
+ "disabled",
91
+ "focused",
92
+ "focused-keyboard"
93
+ ];
104
94
  /**
105
- * Generates button line-height with icon size minimum.
106
- * Ensures button line-height is at least as tall as the icon to prevent clipping.
107
- * @returns Enhanced line-height CSS value with max(), or original value if icon size unavailable
95
+ * Enumerates every canonical state key the system supports: rest, every atom,
96
+ * every modifier-subset (up to {@link MAX_MODIFIERS_PER_COMPOUND}) on its own
97
+ * and combined with each interactive, plus any legacy keys still in use.
98
+ *
99
+ * Mirrors `resolvePropertyStates` exactly so what the resolver pre-emits in
100
+ * the schema, what the StateBuilder lets users compose, and what the docs
101
+ * map / configurator safelist preserve are all the same set.
108
102
  */
109
- function getButtonLineHeightWithIconMinimum({ lineHeight, schema, iconLayerKey, state, theme }) {
110
- const iconSizeValue = schema.variables?.[iconLayerKey]?.size?.[state]?.value;
111
- if (iconSizeValue) return `max(${lineHeight}, ${theme(configurableProperties.iconSize.twThemePath("iconSizes", iconSizeValue))})`;
112
- return lineHeight;
103
+ function enumerateAllStateKeys() {
104
+ const states = ["rest"];
105
+ const atoms = [...INTERACTIVE_ATOMICS, ...MODIFIER_ATOMICS].sort(comparePriority);
106
+ for (const stateKey of atomicAndCompoundStateKeys(atoms)) states.push(stateKey);
107
+ for (const legacy of LEGACY_STATE_KEYS) states.push(legacy);
108
+ return states;
109
+ }
110
+ const KNOWN_STATE_KEYS = enumerateAllStateKeys();
111
+ /**
112
+ * Lookup of every known state key to its index in `KNOWN_STATE_KEYS` — i.e. the
113
+ * resolver's emission order. The CSS generator iterates property-state maps
114
+ * in this order (instead of the alphabetical insertion order produced by
115
+ * `syncYosConfig`'s `normalizeConfig`), so cascade tie-breaking under the
116
+ * uniform per-atom specificity actually follows the priority chain. Unknown
117
+ * keys sort to the end.
118
+ */
119
+ const KNOWN_STATE_RANK = new Map(KNOWN_STATE_KEYS.map((key, index) => [key, index]));
120
+ function compareStateKeys(a, b) {
121
+ const ra = KNOWN_STATE_RANK.get(a) ?? Number.POSITIVE_INFINITY;
122
+ const rb = KNOWN_STATE_RANK.get(b) ?? Number.POSITIVE_INFINITY;
123
+ if (ra === rb) return a < b ? -1 : a > b ? 1 : 0;
124
+ return ra - rb;
125
+ }
126
+ function buildStateRecord(transform) {
127
+ const record = {};
128
+ for (const key of KNOWN_STATE_KEYS) record[key] = transform(key);
129
+ return record;
113
130
  }
131
+ const statePseudoMapDocsMode = buildStateRecord(getStateDocsClass);
132
+ /**
133
+ * Layout line-height for button root: icon size only (not textVariant line-height).
134
+ * @see getButtonContentLineHeight
135
+ */
114
136
  /**
115
137
  * Shared pagination control content size: max(icon size, text line-height).
116
138
  * Keeps page numbers and prev/next icons vertically centered when typography exceeds icon height.
@@ -243,7 +265,65 @@ const getTheCssPropertyValue = (schema, theme, propertyName, schemaStateValue, e
243
265
  if (propertyConfig.concatenate && existingValue) return `${existingValue}${propertyConfig.concatenationDelimiter || ", "}${newValue}`;
244
266
  return newValue;
245
267
  };
246
- function generateDeclaration({ componentName, subComponentName, variantKey, variantValue, layer, layerOptionalPseudoSelector, componentStateKey, componentStateValue, schema, propertyKey, originalPropertyDefinition, theme, currentStyles, previewOptions, excludedPseudoStates = [] }) {
268
+ /**
269
+ * Builds the per-state resolution map for a per-componentState OVERRIDE in
270
+ * exhaustive mode: the universal layer's state map with the override's ENABLED
271
+ * (and `rest`) slots layered on top. An unset or disabled override slot defers
272
+ * to the universal layer's value, so `resolveSlotByCascade` picks the
273
+ * most-specific *enabled* slot across both layers (override winning ties) rather
274
+ * than collapsing every unset state to the override's own `rest`.
275
+ */
276
+ function mergeUniversalUnderOverride(universal, override) {
277
+ const merged = { ...universal };
278
+ const overrideRecord = override;
279
+ for (const state of Object.keys(overrideRecord)) {
280
+ const slot = overrideRecord[state];
281
+ if (state === "rest" && slot?.isEnabled !== false || slot?.isEnabled === true) merged[state] = slot;
282
+ }
283
+ return merged;
284
+ }
285
+ /**
286
+ * Replays SELECTIVE emission for a STANDALONE atomicStates layer (no universal
287
+ * fallback) and returns the sparse set of slots it would emit: `rest`, every
288
+ * explicitly-enabled state, and every cascade-correction. Mirrors the inline
289
+ * selective decision in `generateDeclaration` — keep the two in lock-step.
290
+ *
291
+ * The override path seeds its own correction accumulator with this for the
292
+ * UNIVERSAL layer. Universal layers are emitted in a separate pass at the SAME
293
+ * specificity tier as their per-option overrides (see `bumpRootSpecificity`), so
294
+ * at runtime a universal rule wins wherever the override emits nothing. Without
295
+ * this seed, an override that flattens a state to `rest` (e.g. a `filled` help
296
+ * text that's `muted` everywhere) silently inherits the universal's brighter
297
+ * rule for that state in selective mode — a divergence from exhaustive, where
298
+ * the override emits every state explicitly. Seeding makes the override emit a
299
+ * correction whenever the universal would otherwise render a different value.
300
+ */
301
+ function selectiveEmittedStates(stateMap, orderedStateKeys, emittableStates) {
302
+ const emitted = { rest: stateMap.rest };
303
+ for (const stateKey of orderedStateKeys) {
304
+ if (stateKey === "rest" || !emittableStates.has(stateKey)) continue;
305
+ const value = resolveSlotByCascade(stateKey, stateMap);
306
+ if (!value) continue;
307
+ if (!(stateMap[stateKey]?.isEnabled === true)) {
308
+ if (resolveSlotByCascade(stateKey, emitted)?.value === value.value) continue;
309
+ }
310
+ emitted[stateKey] = {
311
+ ...value,
312
+ isEnabled: true
313
+ };
314
+ }
315
+ return emitted;
316
+ }
317
+ function generateDeclaration({ componentName, subComponentName, variantKey, variantValue, layer, layerOptionalPseudoSelector, componentStateKey, componentStateValue, schema, propertyKey, originalPropertyDefinition, declaredStates, layerUsesAtomicStates, theme, currentStyles, previewOptions, excludedPseudoStates = [], emit, fallbackStateMap, bumpRootSpecificity, buttonHeightReferenceSchema }) {
318
+ const shouldUseExhaustive = (emit ?? "exhaustive") === "exhaustive" && layerUsesAtomicStates;
319
+ const { cssProperties: configuredCssProperties, extendedProperties: configuredExtendedGetter } = configurableProperties[originalPropertyDefinition.name];
320
+ const configuredExtendedProperties = isFunction(configuredExtendedGetter) ? configuredExtendedGetter({
321
+ componentName,
322
+ subComponentName,
323
+ layer,
324
+ propertyKey
325
+ }) : configuredExtendedGetter;
326
+ if (!(typeof configuredCssProperties === "string" && configuredCssProperties.length > 0 || Array.isArray(configuredCssProperties) && configuredCssProperties.length > 0 || Array.isArray(configuredExtendedProperties) && configuredExtendedProperties.length > 0)) return {};
247
327
  const schemaKey = generateSchemaKey({
248
328
  variantKey,
249
329
  variantValue,
@@ -264,14 +344,29 @@ function generateDeclaration({ componentName, subComponentName, variantKey, vari
264
344
  const propertyStateMap = schema.variables?.[schemaKey]?.[propertyKey];
265
345
  if (!propertyStateMap) throw new Error(`Prop definition (${componentName} - ${schemaKey} - ${propertyKey}) not found, this is not expected, please report the bug to the UDS team`);
266
346
  const styles = {};
267
- const skipRestForProperty = originalPropertyDefinition.skipRestState === true;
268
- const declaredPseudoStates = filterPseudoStatesByExclusion(originalPropertyDefinition.pseudoStates, excludedPseudoStates);
269
- const declaredPropertyStates = new Set([...skipRestForProperty ? [] : ["rest"], ...declaredPseudoStates]);
270
- for (const propStateStr in propertyStateMap) {
271
- if (!declaredPropertyStates.has(propStateStr)) continue;
347
+ const excludedStateSet = new Set(excludedPseudoStates);
348
+ const emittableStates = new Set([...declaredStates].filter((state) => !excludedStateSet.has(state)));
349
+ const orderedStateKeys = (shouldUseExhaustive || layerUsesAtomicStates ? [...emittableStates] : Object.keys(propertyStateMap)).sort(compareStateKeys);
350
+ const resolutionStates = fallbackStateMap ? mergeUniversalUnderOverride(fallbackStateMap, propertyStateMap) : propertyStateMap;
351
+ const emittedForSelective = layerUsesAtomicStates && !shouldUseExhaustive ? fallbackStateMap ? selectiveEmittedStates(fallbackStateMap, orderedStateKeys, emittableStates) : { rest: resolutionStates.rest } : {};
352
+ for (const propStateStr of orderedStateKeys) {
353
+ if (!emittableStates.has(propStateStr)) continue;
272
354
  const propertyState = propStateStr;
273
- const propertyStateSelector = statePseudoMap[propertyState] ?? "";
274
- const schemaValueForState = propertyStateMap[propertyState];
355
+ const propertyStateSelector = getStateSelector(propertyState);
356
+ const schemaValueForState = shouldUseExhaustive || layerUsesAtomicStates ? resolveSlotByCascade(propStateStr, resolutionStates) : propertyStateMap[propertyState];
357
+ if (!schemaValueForState) continue;
358
+ if (!shouldUseExhaustive) {
359
+ if (layerUsesAtomicStates) {
360
+ const isExplicitlyEnabled = propertyStateMap[propertyState]?.isEnabled === true;
361
+ if (propStateStr !== "rest" && !isExplicitlyEnabled) {
362
+ if (resolveSlotByCascade(propStateStr, emittedForSelective)?.value === schemaValueForState.value) continue;
363
+ }
364
+ emittedForSelective[propStateStr] = {
365
+ ...schemaValueForState,
366
+ isEnabled: true
367
+ };
368
+ } else if (schemaValueForState.isEnabled === false) continue;
369
+ }
275
370
  let fullClassName = ``;
276
371
  if (layer === "root") if (componentStateKey && componentStateValue) {
277
372
  const rootVariantClass = generateClassName({
@@ -290,7 +385,7 @@ function generateDeclaration({ componentName, subComponentName, variantKey, vari
290
385
  componentStateValue
291
386
  });
292
387
  fullClassName = `.${rootVariantClass}${propertyStateSelector}.${className}`;
293
- } else fullClassName = `.${className}${propertyStateSelector}`;
388
+ } else fullClassName = bumpRootSpecificity ? `.${className}.${className}${propertyStateSelector}` : `.${className}${propertyStateSelector}`;
294
389
  else {
295
390
  const rootVariantClassName = generateClassName({
296
391
  componentName,
@@ -317,7 +412,17 @@ function generateDeclaration({ componentName, subComponentName, variantKey, vari
317
412
  subComponentName
318
413
  });
319
414
  fullClassName = `.${rootVariantClassName}${propertyStateSelector}.${rootVariantClassNameWithComponentState} .${className}`;
320
- } else fullClassName = `.${rootVariantClassName}${propertyStateSelector} .${className}`;
415
+ } else {
416
+ className = generateClassName({
417
+ componentName,
418
+ variantKey,
419
+ variantValue,
420
+ layer,
421
+ layerOptionalPseudoSelector,
422
+ subComponentName
423
+ });
424
+ fullClassName = bumpRootSpecificity ? `.${rootVariantClassName}.${rootVariantClassName}${propertyStateSelector} .${className}` : `.${rootVariantClassName}${propertyStateSelector} .${className}`;
425
+ }
321
426
  }
322
427
  const { cssProperties, extendedProperties: extendedPropertiesGetter } = configurableProperties[originalPropertyDefinition.name];
323
428
  const cssDeclarations = {};
@@ -353,6 +458,28 @@ function generateDeclaration({ componentName, subComponentName, variantKey, vari
353
458
  */
354
459
  if (componentName === "button" && layer === "root") {
355
460
  if (propertyKey === CSS_GAP) cssDeclarations[BUTTON_GAP_VAR] = cssDeclarations[CSS_GAP];
461
+ if (propertyKey === "spacingVertical") {
462
+ const paddingVertical = cssDeclarations["padding-top"];
463
+ if (paddingVertical) applyButtonControlHeightDeclarations({
464
+ cssDeclarations,
465
+ paddingVertical,
466
+ schema,
467
+ rootLayerKey: generateSchemaKey({
468
+ variantKey,
469
+ variantValue,
470
+ layer: "root",
471
+ subComponentName
472
+ }),
473
+ iconLayerKey: generateSchemaKey({
474
+ variantKey,
475
+ variantValue,
476
+ layer: "icon",
477
+ subComponentName
478
+ }),
479
+ state: propertyState,
480
+ theme
481
+ });
482
+ }
356
483
  if (propertyKey === "scaleEffect" && propertyState === "hover") {
357
484
  const rootClassName = `.${className}`;
358
485
  const renderValue = (schemaValue) => configurableProperties.scaleEffect.customValueRenderer?.(schemaValue, schema, () => "") ?? "1";
@@ -361,17 +488,39 @@ function generateDeclaration({ componentName, subComponentName, variantKey, vari
361
488
  if (propertyStateMap.hover) styles[rootClassName][BUTTON_SCALE_EFFECT_HOVER] = renderValue(propertyStateMap.hover);
362
489
  if (propertyStateMap.pressed) styles[rootClassName][BUTTON_SCALE_EFFECT_PRESSED] = renderValue(propertyStateMap.pressed);
363
490
  }
364
- if (cssDeclarations[CSS_LINE_HEIGHT]) {
365
- const iconLayerKey = generateSchemaKey({
491
+ if (cssDeclarations[CSS_LINE_HEIGHT]) cssDeclarations[CSS_LINE_HEIGHT] = getButtonContentLineHeight({
492
+ schema,
493
+ rootLayerKey: generateSchemaKey({
494
+ variantKey,
495
+ variantValue,
496
+ layer: "root",
497
+ subComponentName
498
+ }),
499
+ iconLayerKey: generateSchemaKey({
366
500
  variantKey,
367
501
  variantValue,
368
502
  layer: "icon",
369
503
  subComponentName
370
- });
371
- cssDeclarations[CSS_LINE_HEIGHT] = getButtonLineHeightWithIconMinimum({
372
- lineHeight: cssDeclarations[CSS_LINE_HEIGHT],
373
- schema,
374
- iconLayerKey,
504
+ }),
505
+ state: propertyState,
506
+ theme
507
+ });
508
+ }
509
+ if (componentName === "iconbutton" && layer === "root") {
510
+ if (propertyKey === "spacing") {
511
+ const paddingVertical = cssDeclarations[CSS_PADDING];
512
+ if (paddingVertical) applyIconButtonControlHeightDeclarations({
513
+ cssDeclarations,
514
+ paddingVertical,
515
+ iconButtonSchema: schema,
516
+ buttonHeightReferenceSchema,
517
+ variantValue,
518
+ iconLayerKey: generateSchemaKey({
519
+ variantKey,
520
+ variantValue,
521
+ layer: "icon",
522
+ subComponentName
523
+ }),
375
524
  state: propertyState,
376
525
  theme
377
526
  });
@@ -459,7 +608,7 @@ function generateDeclaration({ componentName, subComponentName, variantKey, vari
459
608
  ...styles[fullClassName]
460
609
  };
461
610
  if (previewOptions?.generatePseudoStateClassModifier) {
462
- const pseudoPrefixClass = statePseudoMapDocsMode[propertyState] ?? "";
611
+ const pseudoPrefixClass = getStateDocsClass(propertyState);
463
612
  if (pseudoPrefixClass.length) {
464
613
  const importantCssDeclarations = Object.fromEntries(Object.entries(cssDeclarations).map(([prop, value]) => {
465
614
  const strValue = String(value).trimEnd().replace(/;$/, "");
@@ -475,12 +624,139 @@ function generateDeclaration({ componentName, subComponentName, variantKey, vari
475
624
  }
476
625
  return styles;
477
626
  }
478
- function generateConfigStyles(config, schema, theme, previewOptions) {
627
+ /**
628
+ * Emit CSS for per-state schema entries that target a variant's universal
629
+ * layers — the "override" path. Per-state schema entries can be authored by
630
+ * the Configurator (via `setConfigPseudoStateVariable`) or written by data
631
+ * migrations; they aren't declared in source configs.
632
+ *
633
+ * Iterates the cartesian product of `componentStates.X.options × universal
634
+ * layers × universal properties`. For each combination, calls
635
+ * `generateDeclaration` with the universal property's metadata plus the
636
+ * componentState key/value — the existing schema lookup inside
637
+ * `generateDeclaration` picks up the override slot if one exists and skips
638
+ * the property otherwise.
639
+ */
640
+ function emitSchemaOverrideStyles({ componentName, subComponentName, variantKey, variantOption, variantConfig, schema, theme, previewOptions, emit, currentStyles }) {
641
+ let styles = currentStyles;
642
+ const universalLayers = variantConfig.layers;
643
+ const componentStates = variantConfig.componentStates;
644
+ if (!universalLayers || !componentStates) return styles;
645
+ const variables = schema.variables;
646
+ if (!variables) return styles;
647
+ for (const componentStateKey in componentStates) {
648
+ const componentState = componentStates[componentStateKey];
649
+ for (const componentStateOption of componentState.options) for (const layerKey in universalLayers) {
650
+ const universalLayer = universalLayers[layerKey];
651
+ const schemaEntry = variables[generateSchemaKey({
652
+ variantKey,
653
+ variantValue: variantOption,
654
+ componentStateKey,
655
+ componentStateValue: componentStateOption,
656
+ layer: layerKey,
657
+ subComponentName
658
+ })];
659
+ if (!schemaEntry) continue;
660
+ for (const propertyKey in universalLayer.properties) {
661
+ if (!schemaEntry[propertyKey]) continue;
662
+ const originalPropertyDefinition = universalLayer.properties[propertyKey];
663
+ const fallbackStateMap = variables[generateSchemaKey({
664
+ variantKey,
665
+ variantValue: variantOption,
666
+ layer: layerKey,
667
+ subComponentName
668
+ })]?.[propertyKey];
669
+ const declarations = generateDeclaration({
670
+ componentName,
671
+ subComponentName,
672
+ variantKey,
673
+ variantValue: variantOption,
674
+ componentStateKey,
675
+ componentStateValue: componentStateOption,
676
+ layer: layerKey,
677
+ layerOptionalPseudoSelector: universalLayer.pseudoSelector,
678
+ propertyKey,
679
+ originalPropertyDefinition,
680
+ declaredStates: new Set(resolvePropertyStates(universalLayer, originalPropertyDefinition)),
681
+ layerUsesAtomicStates: (universalLayer.atomicStates?.length ?? 0) > 0,
682
+ theme,
683
+ schema,
684
+ currentStyles: styles,
685
+ previewOptions,
686
+ emit,
687
+ fallbackStateMap
688
+ });
689
+ styles = deepMerge(styles, declarations);
690
+ }
691
+ }
692
+ }
693
+ return styles;
694
+ }
695
+ /**
696
+ * Emit CSS for a set of layers without a component-state segment in the
697
+ * selector. Used by both `VariantConfigWithProperties` (where layers are the
698
+ * only source of styles) and `VariantConfigWithComponentStates` (where these
699
+ * layers express the variant's universal baseline, with per-state overrides
700
+ * winning via class-compound specificity).
701
+ */
702
+ function emitUniversalLayers({ componentName, subComponentName, variantKey, variantOption, layers, schema, theme, previewOptions, emit, currentStyles, bumpRootSpecificity, buttonHeightReferenceSchema }) {
703
+ let styles = currentStyles;
704
+ const variables = schema.variables;
705
+ for (const layerKey in layers) {
706
+ const layer = layers[layerKey];
707
+ const schemaKey = generateSchemaKey({
708
+ variantKey,
709
+ variantValue: variantOption,
710
+ layer: layerKey,
711
+ subComponentName
712
+ });
713
+ const schemaEntry = variables?.[schemaKey];
714
+ for (const propertyKey in layer.properties) {
715
+ if (!schemaEntry || !schemaEntry[propertyKey]) continue;
716
+ const originalPropertyDefinition = layer.properties[propertyKey];
717
+ const declarations = generateDeclaration({
718
+ componentName,
719
+ subComponentName,
720
+ variantKey,
721
+ variantValue: variantOption,
722
+ layer: layerKey,
723
+ layerOptionalPseudoSelector: layer.pseudoSelector,
724
+ propertyKey,
725
+ originalPropertyDefinition,
726
+ declaredStates: new Set(resolvePropertyStates(layer, originalPropertyDefinition)),
727
+ layerUsesAtomicStates: (layer.atomicStates?.length ?? 0) > 0,
728
+ theme,
729
+ schema,
730
+ currentStyles: styles,
731
+ previewOptions,
732
+ emit,
733
+ bumpRootSpecificity,
734
+ buttonHeightReferenceSchema
735
+ });
736
+ styles = deepMerge(styles, declarations);
737
+ }
738
+ }
739
+ return styles;
740
+ }
741
+ function generateConfigStyles(config, schema, theme, previewOptions, emit, generateStylesContext) {
479
742
  const componentName = config.label.toLowerCase();
480
743
  let styles = {};
481
744
  for (const variantKey in config.variants) {
482
745
  const variantConfig = config.variants[variantKey];
483
746
  for (const variantOption of variantConfig.options) if (isVariantConfigWithComponentStates(variantConfig)) {
747
+ if (variantConfig.layers) styles = emitUniversalLayers({
748
+ componentName,
749
+ variantKey,
750
+ variantOption,
751
+ layers: variantConfig.layers,
752
+ schema,
753
+ theme,
754
+ previewOptions,
755
+ emit,
756
+ currentStyles: styles,
757
+ bumpRootSpecificity: true,
758
+ buttonHeightReferenceSchema: generateStylesContext?.buttonHeightReferenceSchema
759
+ });
484
760
  const componentStates = variantConfig.componentStates;
485
761
  for (const componentStateKey in componentStates) {
486
762
  const componentState = componentStates[componentStateKey];
@@ -500,46 +776,64 @@ function generateConfigStyles(config, schema, theme, previewOptions) {
500
776
  layerOptionalPseudoSelector: layer.pseudoSelector,
501
777
  propertyKey,
502
778
  originalPropertyDefinition,
779
+ declaredStates: new Set(resolvePropertyStates(layer, originalPropertyDefinition)),
780
+ layerUsesAtomicStates: (layer.atomicStates?.length ?? 0) > 0,
503
781
  theme,
504
782
  schema,
505
783
  currentStyles: styles,
506
784
  previewOptions,
507
- excludedPseudoStates
785
+ excludedPseudoStates,
786
+ emit,
787
+ buttonHeightReferenceSchema: generateStylesContext?.buttonHeightReferenceSchema
508
788
  });
509
789
  styles = deepMerge(styles, declarations);
510
790
  }
511
791
  }
512
792
  }
513
793
  }
514
- } else if (isVariantConfigWithProperties(variantConfig)) {
515
- const layers = variantConfig.layers;
516
- for (const layerKey in layers) {
517
- const layer = layers[layerKey];
518
- for (const propertyKey in layer.properties) {
519
- const originalPropertyDefinition = layer.properties[propertyKey];
520
- const declarations = generateDeclaration({
521
- componentName,
522
- variantKey,
523
- variantValue: variantOption,
524
- layer: layerKey,
525
- layerOptionalPseudoSelector: layer.pseudoSelector,
526
- propertyKey,
527
- originalPropertyDefinition,
528
- theme,
529
- schema,
530
- currentStyles: styles,
531
- previewOptions
532
- });
533
- styles = deepMerge(styles, declarations);
534
- }
535
- }
536
- }
794
+ styles = emitSchemaOverrideStyles({
795
+ componentName,
796
+ variantKey,
797
+ variantOption,
798
+ variantConfig,
799
+ schema,
800
+ theme,
801
+ previewOptions,
802
+ emit,
803
+ currentStyles: styles
804
+ });
805
+ } else if (isVariantConfigWithProperties(variantConfig)) styles = emitUniversalLayers({
806
+ componentName,
807
+ variantKey,
808
+ variantOption,
809
+ layers: variantConfig.layers,
810
+ schema,
811
+ theme,
812
+ previewOptions,
813
+ emit,
814
+ currentStyles: styles,
815
+ buttonHeightReferenceSchema: generateStylesContext?.buttonHeightReferenceSchema
816
+ });
537
817
  }
538
818
  if (config.subComponents) {
539
819
  const { subComponents } = config;
540
820
  for (const subComponentKey in subComponents) for (const variantKey in subComponents[subComponentKey].variants) {
541
821
  const variantConfig = subComponents[subComponentKey].variants[variantKey];
542
822
  for (const variantOption of variantConfig.options) if (isVariantConfigWithComponentStates(variantConfig)) {
823
+ if (variantConfig.layers) styles = emitUniversalLayers({
824
+ componentName,
825
+ subComponentName: subComponentKey,
826
+ variantKey,
827
+ variantOption,
828
+ layers: variantConfig.layers,
829
+ schema,
830
+ theme,
831
+ previewOptions,
832
+ emit,
833
+ currentStyles: styles,
834
+ bumpRootSpecificity: true,
835
+ buttonHeightReferenceSchema: generateStylesContext?.buttonHeightReferenceSchema
836
+ });
543
837
  const componentStates = variantConfig.componentStates;
544
838
  for (const componentStateKey in componentStates) {
545
839
  const componentState = componentStates[componentStateKey];
@@ -560,41 +854,46 @@ function generateConfigStyles(config, schema, theme, previewOptions) {
560
854
  layerOptionalPseudoSelector: layer.pseudoSelector,
561
855
  propertyKey,
562
856
  originalPropertyDefinition,
857
+ declaredStates: new Set(resolvePropertyStates(layer, originalPropertyDefinition)),
858
+ layerUsesAtomicStates: (layer.atomicStates?.length ?? 0) > 0,
563
859
  theme,
564
860
  schema,
565
861
  currentStyles: styles,
566
862
  previewOptions,
567
- excludedPseudoStates
863
+ excludedPseudoStates,
864
+ emit,
865
+ buttonHeightReferenceSchema: generateStylesContext?.buttonHeightReferenceSchema
568
866
  });
569
867
  styles = deepMerge(styles, declarations);
570
868
  }
571
869
  }
572
870
  }
573
871
  }
574
- } else if (isVariantConfigWithProperties(variantConfig)) {
575
- const layers = variantConfig.layers;
576
- for (const layerKey in layers) {
577
- const layer = layers[layerKey];
578
- for (const propertyKey in layer.properties) {
579
- const originalPropertyDefinition = layer.properties[propertyKey];
580
- const declarations = generateDeclaration({
581
- componentName,
582
- subComponentName: subComponentKey,
583
- variantKey,
584
- variantValue: variantOption,
585
- layer: layerKey,
586
- layerOptionalPseudoSelector: layer.pseudoSelector,
587
- propertyKey,
588
- originalPropertyDefinition,
589
- theme,
590
- schema,
591
- currentStyles: styles,
592
- previewOptions
593
- });
594
- styles = deepMerge(styles, declarations);
595
- }
596
- }
597
- }
872
+ styles = emitSchemaOverrideStyles({
873
+ componentName,
874
+ subComponentName: subComponentKey,
875
+ variantKey,
876
+ variantOption,
877
+ variantConfig,
878
+ schema,
879
+ theme,
880
+ previewOptions,
881
+ emit,
882
+ currentStyles: styles
883
+ });
884
+ } else if (isVariantConfigWithProperties(variantConfig)) styles = emitUniversalLayers({
885
+ componentName,
886
+ subComponentName: subComponentKey,
887
+ variantKey,
888
+ variantOption,
889
+ layers: variantConfig.layers,
890
+ schema,
891
+ theme,
892
+ previewOptions,
893
+ emit,
894
+ currentStyles: styles,
895
+ buttonHeightReferenceSchema: generateStylesContext?.buttonHeightReferenceSchema
896
+ });
598
897
  }
599
898
  }
600
899
  /**
@@ -627,8 +926,8 @@ function generateConfigStyles(config, schema, theme, previewOptions) {
627
926
  * @param previewOptions - Options for generating preview mode class modifiers
628
927
  * @returns CSS styles object with grouped selectors for default aliases
629
928
  */
630
- const generateStyles = (config, schema, theme, previewOptions) => {
631
- const styles = generateConfigStyles(config, schema, theme, previewOptions);
929
+ const generateStylesInner = (config, schema, theme, previewOptions, generateStylesContext) => {
930
+ const styles = generateConfigStyles(config, schema, theme, previewOptions, previewOptions.emit ?? "exhaustive", generateStylesContext);
632
931
  if (!config.variants && !config.subComponents) return styles;
633
932
  const componentName = config.label.toLowerCase();
634
933
  if (componentName !== "button" && componentName !== "iconbutton") return styles;
@@ -636,14 +935,15 @@ const generateStyles = (config, schema, theme, previewOptions) => {
636
935
  /**
637
936
  * Helper: Extract all layer keys from a variant configuration
638
937
  * Handles both simple variants (with direct layers) and variants with component states
938
+ * (which may additionally declare top-level universal layers).
639
939
  */
640
940
  const getLayerKeys = (variantConfig) => {
641
- if (isVariantConfigWithProperties(variantConfig)) return Object.keys(variantConfig.layers);
642
- else if (isVariantConfigWithComponentStates(variantConfig)) {
941
+ if (isVariantConfigWithComponentStates(variantConfig)) {
643
942
  const layerKeysSet = /* @__PURE__ */ new Set();
644
- for (const componentStateConfig of Object.values(variantConfig.componentStates)) Object.keys(componentStateConfig.layers).forEach((key) => layerKeysSet.add(key));
943
+ for (const componentStateConfig of Object.values(variantConfig.componentStates)) Object.keys(componentStateConfig.layers ?? {}).forEach((key) => layerKeysSet.add(key));
944
+ if (variantConfig.layers) Object.keys(variantConfig.layers).forEach((key) => layerKeysSet.add(key));
645
945
  return Array.from(layerKeysSet);
646
- }
946
+ } else if (isVariantConfigWithProperties(variantConfig)) return Object.keys(variantConfig.layers);
647
947
  return [];
648
948
  };
649
949
  /**
@@ -693,7 +993,39 @@ const generateStyles = (config, schema, theme, previewOptions) => {
693
993
  }
694
994
  return finalStyles;
695
995
  };
996
+ /**
997
+ * Public entry point. Generates a component's CSS, emitting each declared state
998
+ * as its own rule (one selector per rule) so the cascade is carried entirely by
999
+ * per-rule specificity — which CSS minifiers preserve.
1000
+ *
1001
+ * An earlier optimization merged rules sharing a declaration block into a single
1002
+ * comma-separated selector list (to avoid repeating invariant declarations per
1003
+ * state). It was removed: a minifier's forgiving-`:is()` rewrite — applied to a
1004
+ * comma-separated list that contains `:has()` when targeting browsers without
1005
+ * native `:has()` support — collapses the list to its single highest
1006
+ * specificity, inflating the weakest selector (`rest`) above genuinely
1007
+ * customized states and corrupting the cascade. Separate rules never form such a
1008
+ * list, so minifiers leave their specificity intact. (gzip already collapses the
1009
+ * repeated declaration blocks, so the shipped-size cost is minimal.)
1010
+ */
1011
+ const generateStyles = (config, schema, theme, previewOptions, generateStylesContext) => generateStylesInner(config, schema, theme, previewOptions, generateStylesContext);
1012
+ /**
1013
+ * Throws if `values` contains a duplicate entry. Runs at config-import time so
1014
+ * a typo like `atomicStates: ['hover', 'hover']` fails the UDS build instead
1015
+ * of silently degrading the configurator's sidebar (where the resolver would
1016
+ * emit two entries for the same state).
1017
+ */
1018
+ function assertNoDuplicates(values, field, ownerLabel) {
1019
+ if (!values) return;
1020
+ const seen = /* @__PURE__ */ new Set();
1021
+ for (const v of values) {
1022
+ if (seen.has(v)) throw new Error(`Duplicate "${v}" in ${field} on ${ownerLabel}. Each entry must appear at most once.`);
1023
+ seen.add(v);
1024
+ }
1025
+ }
696
1026
  function createConfigurableProperty(prop) {
1027
+ assertNoDuplicates(prop.pseudoStates, "pseudoStates", `property "${prop.label}"`);
1028
+ assertNoDuplicates(prop.excludeAtomics, "excludeAtomics", `property "${prop.label}"`);
697
1029
  return prop;
698
1030
  }
699
1031
  function createVariantConfig(config) {
@@ -709,10 +1041,44 @@ function createComponentStates(config) {
709
1041
  return config;
710
1042
  }
711
1043
  function createLayerConfig(config) {
1044
+ assertNoDuplicates(config.atomicStates, "atomicStates", `layer "${config.label}"`);
1045
+ assertNoSkipRestStateOnAtomicLayer(config);
712
1046
  return config;
713
1047
  }
1048
+ /**
1049
+ * Disallows pairing `skipRestState: true` properties with a layer that uses
1050
+ * `atomicStates`. The combination breaks exhaustive emission's cascade
1051
+ * resolution:
1052
+ *
1053
+ * Under `atomicStates`, `generateDeclaration` emits every declared state and
1054
+ * resolves each non-customized compound through `resolveSlotByCascade`, so a
1055
+ * deliberate atom customization (e.g. `hover`) flows into every compound that
1056
+ * inherits from it (`invalid&hover`, …). That inheritance is what keeps the
1057
+ * higher-specificity compound rules from cancelling a lower-specificity atom
1058
+ * customization.
1059
+ *
1060
+ * But `buildPropertyStateMap` force-marks EVERY slot of a `skipRestState`
1061
+ * property `isEnabled: true` (there's no `rest` fall-through, so each state must
1062
+ * carry its own value). `resolveSlotByCascade` returns an enabled slot as-is,
1063
+ * so each force-enabled compound emits the property's *default* value instead of
1064
+ * inheriting a sibling atom's customization — and the compound's higher
1065
+ * specificity then silently overrides that customization (e.g. a customized
1066
+ * `hover` is cancelled by an unmodified `invalid&hover` carrying the default).
1067
+ *
1068
+ * Resolution path when this becomes a real need (currently the only
1069
+ * `skipRestState` properties are Button's `opacity` and `scaleEffect`, and
1070
+ * Button is on legacy `pseudoStates`):
1071
+ * teach `generateDeclaration` to skip compound state emission entirely for
1072
+ * `skipRestState` properties — atom rules cascade naturally, and designers
1073
+ * who want fine compound control simply drop `skipRestState` for that
1074
+ * property.
1075
+ */
1076
+ function assertNoSkipRestStateOnAtomicLayer(config) {
1077
+ if (!config.atomicStates || config.atomicStates.length === 0) return;
1078
+ for (const [propertyKey, property] of Object.entries(config.properties)) if (property.skipRestState) throw new Error(`Property "${propertyKey}" on layer "${config.label}" uses skipRestState: true, but the layer declares atomicStates. This combination isn't currently supported — see the comment on assertNoSkipRestStateOnAtomicLayer for the trade-offs and resolution path. For now, either drop skipRestState on this property or keep the layer on legacy per-property pseudoStates.`);
1079
+ }
714
1080
  function createSubComponentConfig(config) {
715
1081
  return config;
716
1082
  }
717
1083
  //#endregion
718
- export { applyBoxShadowBorder, createComponentStates, createConfigurableProperty, createLayerConfig, createSubComponentConfig, createVariantConfig, createVariantConfigWithComponentStates, createVariantConfigWithProperties, generateClassName, generateConfigStyles, generateDeclaration, generateStyles, statePseudoMapDocsMode };
1084
+ export { applyBoxShadowBorder, createComponentStates, createConfigurableProperty, createLayerConfig, createSubComponentConfig, createVariantConfig, createVariantConfigWithComponentStates, createVariantConfigWithProperties, generateClassName, generateConfigStyles, generateDeclaration, generateStyles, mergeUniversalUnderOverride, statePseudoMapDocsMode };