@yahoo/uds 3.156.2 → 3.157.0

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 (144) 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 +3011 -3038
  6. package/dist/automated-config/dist/generated/generatedConfigs.d.cts +143 -140
  7. package/dist/automated-config/dist/generated/generatedConfigs.d.ts +143 -140
  8. package/dist/automated-config/dist/generated/generatedConfigs.js +3011 -3038
  9. package/dist/automated-config/dist/generated/universalTokensConfigAuto.cjs +1227 -501
  10. package/dist/automated-config/dist/generated/universalTokensConfigAuto.js +1227 -501
  11. package/dist/automated-config/dist/properties.cjs +1 -1
  12. package/dist/automated-config/dist/properties.d.cts +15 -0
  13. package/dist/automated-config/dist/properties.d.ts +15 -0
  14. package/dist/automated-config/dist/properties.js +1 -1
  15. package/dist/automated-config/dist/types/ComponentConfig.d.cts +77 -4
  16. package/dist/automated-config/dist/types/ComponentConfig.d.ts +77 -4
  17. package/dist/automated-config/dist/types/ConfigSchema.d.cts +14 -2
  18. package/dist/automated-config/dist/types/ConfigSchema.d.ts +14 -2
  19. package/dist/automated-config/dist/types/StateAxis.cjs +90 -0
  20. package/dist/automated-config/dist/types/StateAxis.d.cts +70 -0
  21. package/dist/automated-config/dist/types/StateAxis.d.ts +70 -0
  22. package/dist/automated-config/dist/types/StateAxis.js +84 -0
  23. package/dist/automated-config/dist/utils/buildConfigSchema.cjs +98 -82
  24. package/dist/automated-config/dist/utils/buildConfigSchema.d.cts +32 -10
  25. package/dist/automated-config/dist/utils/buildConfigSchema.d.ts +32 -10
  26. package/dist/automated-config/dist/utils/buildConfigSchema.js +99 -83
  27. package/dist/automated-config/dist/utils/canonicalizeStateKey.cjs +32 -0
  28. package/dist/automated-config/dist/utils/canonicalizeStateKey.d.cts +48 -0
  29. package/dist/automated-config/dist/utils/canonicalizeStateKey.d.ts +48 -0
  30. package/dist/automated-config/dist/utils/canonicalizeStateKey.js +31 -0
  31. package/dist/automated-config/dist/utils/getConfigComponentVariant.d.cts +8 -0
  32. package/dist/automated-config/dist/utils/getConfigComponentVariant.d.ts +8 -0
  33. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.cts +3 -3
  34. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.ts +3 -3
  35. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.cjs +12 -5
  36. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.d.cts +8 -1
  37. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.d.ts +8 -1
  38. package/dist/automated-config/dist/utils/getConfigVariantPseudoStates.js +12 -5
  39. package/dist/automated-config/dist/utils/index.cjs +407 -97
  40. package/dist/automated-config/dist/utils/index.d.cts +66 -16
  41. package/dist/automated-config/dist/utils/index.d.ts +66 -16
  42. package/dist/automated-config/dist/utils/index.js +408 -99
  43. package/dist/automated-config/dist/utils/pseudoStateSelectors.cjs +122 -0
  44. package/dist/automated-config/dist/utils/pseudoStateSelectors.d.cts +80 -0
  45. package/dist/automated-config/dist/utils/pseudoStateSelectors.d.ts +80 -0
  46. package/dist/automated-config/dist/utils/pseudoStateSelectors.js +120 -0
  47. package/dist/automated-config/dist/utils/resolvePropertyStates.cjs +131 -0
  48. package/dist/automated-config/dist/utils/resolvePropertyStates.d.cts +49 -0
  49. package/dist/automated-config/dist/utils/resolvePropertyStates.d.ts +49 -0
  50. package/dist/automated-config/dist/utils/resolvePropertyStates.js +130 -0
  51. package/dist/automated-config/dist/utils/resolveSlotByCascade.cjs +118 -0
  52. package/dist/automated-config/dist/utils/resolveSlotByCascade.d.cts +68 -0
  53. package/dist/automated-config/dist/utils/resolveSlotByCascade.d.ts +68 -0
  54. package/dist/automated-config/dist/utils/resolveSlotByCascade.js +117 -0
  55. package/dist/automated-config/dist/utils/variantConfigGuards.d.cts +13 -0
  56. package/dist/automated-config/dist/utils/variantConfigGuards.d.ts +13 -0
  57. package/dist/components/client/Input/Input.cjs +42 -6
  58. package/dist/components/client/Input/Input.d.cts +13 -0
  59. package/dist/components/client/Input/Input.d.ts +13 -0
  60. package/dist/components/client/Input/Input.js +42 -6
  61. package/dist/config/dist/index.cjs +221 -550
  62. package/dist/config/dist/index.js +221 -550
  63. package/dist/css/dist/commands/css.cjs +1 -0
  64. package/dist/css/dist/commands/css.helpers.cjs +6 -0
  65. package/dist/css/dist/commands/css.helpers.js +6 -0
  66. package/dist/css/dist/commands/css.js +1 -0
  67. package/dist/css/dist/css/generate.cjs +4 -2
  68. package/dist/css/dist/css/generate.d.cts +28 -0
  69. package/dist/css/dist/css/generate.d.ts +28 -0
  70. package/dist/css/dist/css/generate.helpers.cjs +5 -1
  71. package/dist/css/dist/css/generate.helpers.js +6 -2
  72. package/dist/css/dist/css/generate.js +4 -2
  73. package/dist/css/dist/css/postcss.cjs +81 -0
  74. package/dist/css/dist/css/postcss.helpers.cjs +60 -0
  75. package/dist/css/dist/css/postcss.helpers.js +59 -1
  76. package/dist/css/dist/css/postcss.js +82 -2
  77. package/dist/css/dist/css/runner.cjs +12 -2
  78. package/dist/css/dist/css/runner.js +12 -2
  79. package/dist/css/dist/css/theme.d.cts +6 -0
  80. package/dist/css/dist/css/theme.d.ts +6 -0
  81. package/dist/css/dist/packages/automated-config/dist/properties.cjs +1 -1
  82. package/dist/css/dist/packages/automated-config/dist/properties.js +1 -1
  83. package/dist/css/dist/packages/automated-config/dist/utils/index.d.cts +6 -0
  84. package/dist/css/dist/packages/automated-config/dist/utils/index.d.ts +6 -0
  85. package/dist/css/dist/packages/config/dist/index.cjs +221 -550
  86. package/dist/css/dist/packages/config/dist/index.js +221 -550
  87. package/dist/css/dist/utils/optimizeCSS.cjs +59 -0
  88. package/dist/css/dist/utils/optimizeCSS.js +59 -0
  89. package/dist/index.cjs +25 -0
  90. package/dist/index.d.cts +10 -3
  91. package/dist/index.d.ts +10 -3
  92. package/dist/index.js +9 -2
  93. package/dist/styles/styler.d.cts +12 -11
  94. package/dist/styles/styler.d.ts +12 -11
  95. package/dist/styles/variants.d.cts +9 -4
  96. package/dist/styles/variants.d.ts +9 -4
  97. package/dist/tailwind-internal/dist/packages/automated-config/dist/generated/generatedConfigs.cjs +3011 -3038
  98. package/dist/tailwind-internal/dist/packages/automated-config/dist/generated/generatedConfigs.js +3011 -3038
  99. package/dist/tailwind-internal/dist/packages/automated-config/dist/properties.cjs +1 -1
  100. package/dist/tailwind-internal/dist/packages/automated-config/dist/properties.js +1 -1
  101. package/dist/tailwind-internal/dist/packages/automated-config/dist/types/StateAxis.cjs +81 -0
  102. package/dist/tailwind-internal/dist/packages/automated-config/dist/types/StateAxis.js +76 -0
  103. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/canonicalizeStateKey.cjs +33 -0
  104. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/canonicalizeStateKey.js +32 -0
  105. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/componentStatePseudoStates.cjs +0 -7
  106. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/componentStatePseudoStates.js +1 -7
  107. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.cjs +354 -97
  108. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.d.cts +6 -0
  109. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.d.ts +6 -0
  110. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/index.js +355 -98
  111. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/pseudoStateSelectors.cjs +122 -0
  112. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/pseudoStateSelectors.js +121 -0
  113. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolvePropertyStates.cjs +132 -0
  114. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolvePropertyStates.js +131 -0
  115. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolveSlotByCascade.cjs +95 -0
  116. package/dist/tailwind-internal/dist/packages/automated-config/dist/utils/resolveSlotByCascade.js +95 -0
  117. package/dist/tailwind-internal/dist/packages/config/dist/index.cjs +221 -550
  118. package/dist/tailwind-internal/dist/packages/config/dist/index.js +221 -550
  119. package/dist/tailwind-internal/dist/plugins/components.cjs +28 -24
  120. package/dist/tailwind-internal/dist/plugins/components.js +28 -24
  121. package/dist/tailwind-internal/dist/utils/composeTailwindPlugins.d.cts +3 -0
  122. package/dist/tailwind-internal/dist/utils/composeTailwindPlugins.d.ts +3 -0
  123. package/dist/tailwind-internal/dist/utils/getShadowStyles.d.cts +2 -2
  124. package/dist/tailwind-internal/dist/utils/getShadowStyles.d.ts +2 -2
  125. package/dist/tokens/automation/index.cjs +25 -0
  126. package/dist/tokens/automation/index.d.cts +9 -2
  127. package/dist/tokens/automation/index.d.ts +9 -2
  128. package/dist/tokens/automation/index.js +9 -2
  129. package/dist/tokens/index.cjs +25 -0
  130. package/dist/tokens/index.d.cts +10 -3
  131. package/dist/tokens/index.d.ts +10 -3
  132. package/dist/tokens/index.js +9 -2
  133. package/dist/tokens/types.d.cts +1 -1
  134. package/dist/tokens/types.d.ts +1 -1
  135. package/dist/uds/generated/componentData.cjs +2202 -2200
  136. package/dist/uds/generated/componentData.js +2202 -2200
  137. package/dist/uds/generated/migrationSchemaVersion.cjs +1 -1
  138. package/dist/uds/generated/migrationSchemaVersion.js +1 -1
  139. package/dist/uds/generated/tailwindPurge.cjs +79 -78
  140. package/dist/uds/generated/tailwindPurge.js +79 -78
  141. package/generated/componentData.json +2720 -2718
  142. package/generated/migrationSchemaVersion.ts +1 -1
  143. package/generated/tailwindPurge.ts +2 -2
  144. package/package.json +1 -1
@@ -26,6 +26,7 @@ const showHelp = () => {
26
26
  require_print.print(` ${require_colors.cyan("--silent, -s")} Suppress output logging`);
27
27
  require_print.print(` ${require_colors.cyan("--verbose, -v")} List every scanned file used for purging`);
28
28
  require_print.print(` ${require_colors.cyan("--workers <n>")} Number of parallel workers (default: auto = CPU count - 1)`);
29
+ require_print.print(` ${require_colors.cyan("--emit <mode>")} Emission mode: exhaustive (default) | selective`);
29
30
  require_print.print(` ${require_colors.cyan("--force")} Overwrite existing uds.theme.ts (for init)`);
30
31
  require_print.print(` ${require_colors.cyan("--help, -h")} Show this help message`);
31
32
  require_print.print("");
@@ -21,6 +21,11 @@ const getCssInitOptions = (options) => ({
21
21
  entry: entryOption(options.entry),
22
22
  outputPath: stringOption(options.outFile)
23
23
  });
24
+ const parseEmitOption = (value) => {
25
+ if (value === void 0 || value === null) return;
26
+ if (value === "exhaustive" || value === "selective") return value;
27
+ throw new Error(`Invalid --emit "${String(value)}". Expected 'exhaustive' or 'selective'.`);
28
+ };
24
29
  const getCssRunOptions = (workspaceDir, options) => {
25
30
  return {
26
31
  workspaceDir,
@@ -29,6 +34,7 @@ const getCssRunOptions = (workspaceDir, options) => {
29
34
  scope: stringOption(options.scope),
30
35
  entryOption: entryOption(options.entry),
31
36
  configOption: stringOption(options.config),
37
+ emit: parseEmitOption(options.emit),
32
38
  watch: isTruthyFlag(options.watch) || isTruthyFlag(options.w),
33
39
  silent: isTruthyFlag(options.silent) || isTruthyFlag(options.s),
34
40
  verbose: isTruthyFlag(options.verbose) || isTruthyFlag(options.v),
@@ -21,6 +21,11 @@ const getCssInitOptions = (options) => ({
21
21
  entry: entryOption(options.entry),
22
22
  outputPath: stringOption(options.outFile)
23
23
  });
24
+ const parseEmitOption = (value) => {
25
+ if (value === void 0 || value === null) return;
26
+ if (value === "exhaustive" || value === "selective") return value;
27
+ throw new Error(`Invalid --emit "${String(value)}". Expected 'exhaustive' or 'selective'.`);
28
+ };
24
29
  const getCssRunOptions = (workspaceDir, options) => {
25
30
  return {
26
31
  workspaceDir,
@@ -29,6 +34,7 @@ const getCssRunOptions = (workspaceDir, options) => {
29
34
  scope: stringOption(options.scope),
30
35
  entryOption: entryOption(options.entry),
31
36
  configOption: stringOption(options.config),
37
+ emit: parseEmitOption(options.emit),
32
38
  watch: isTruthyFlag(options.watch) || isTruthyFlag(options.w),
33
39
  silent: isTruthyFlag(options.silent) || isTruthyFlag(options.s),
34
40
  verbose: isTruthyFlag(options.verbose) || isTruthyFlag(options.v),
@@ -26,6 +26,7 @@ const showHelp = () => {
26
26
  print(` ${cyan("--silent, -s")} Suppress output logging`);
27
27
  print(` ${cyan("--verbose, -v")} List every scanned file used for purging`);
28
28
  print(` ${cyan("--workers <n>")} Number of parallel workers (default: auto = CPU count - 1)`);
29
+ print(` ${cyan("--emit <mode>")} Emission mode: exhaustive (default) | selective`);
29
30
  print(` ${cyan("--force")} Overwrite existing uds.theme.ts (for init)`);
30
31
  print(` ${cyan("--help, -h")} Show this help message`);
31
32
  print("");
@@ -47,11 +47,13 @@ const generateCSS = async (safelist, config, options) => {
47
47
  safelist,
48
48
  config,
49
49
  enablePreflight: cssFlags.enablePreflight,
50
- enableFontFaceDeclarations: cssFlags.enableFontFaceDeclarations
50
+ enableFontFaceDeclarations: cssFlags.enableFontFaceDeclarations,
51
+ emit: options?.cssOptions?.emit
51
52
  }),
52
53
  shouldPruneVars: cssFlags.shouldPruneVars,
53
54
  safeVarPrefixes: options?.safeVarPrefixes ?? [],
54
- scopeClass
55
+ scopeClass,
56
+ otherBoundaryScopes: options?.otherBoundaryScopes ?? []
55
57
  });
56
58
  const result = await require_perf.measureAsync("gen:postcss", () => (0, postcss.default)(plugins).process(sourceCSS, { from: void 0 }));
57
59
  let css = await require_perf.measureAsync("gen:scoped-color-fix", () => require_generate_helpers.applyScopedColorModeFix(result.css, scopeClass));
@@ -1,4 +1,5 @@
1
1
 
2
+ import { EmitMode } from "../packages/automated-config/dist/utils/index.cjs";
2
3
  import { TailwindSafelistConfig } from "./generate.helpers.cjs";
3
4
  import { UDSCSSOptimizationOptions } from "./theme.cjs";
4
5
 
@@ -19,6 +20,33 @@ interface UDSCSSOptions {
19
20
  fontFaceDeclarations?: boolean;
20
21
  /** Debounce delay (ms) for watch mode regenerations */
21
22
  watchDebounce?: number;
23
+ /**
24
+ * Emission mode. `exhaustive` (default) emits a rule for every state — safe to
25
+ * coexist with other themes with no extra config. `selective` emits only
26
+ * rest + customized states (smaller) and relies on declared boundaries
27
+ * (`scoped` / `excludeBoundaries`) + the isolation fence for coexistence safety.
28
+ */
29
+ emit?: EmitMode;
30
+ /**
31
+ * Additional selectors whose subtrees this theme's bundles must not match.
32
+ * Every emitted bundle (main and each `scoped:` entry) gets a
33
+ * `:not(:where(:is(<boundaries>) *))` suffix on each theme-driven
34
+ * (token-referencing) rule that targets a NON-REST state (`:hover`,
35
+ * `:focus-within`, `:has(…)`, etc.), so its themed state styles never cross
36
+ * into one of these subtrees. Rest-state token rules resolve their tokens to
37
+ * the nested scope's own values (value-safe), so they're left untouched — as
38
+ * are Tailwind utilities, resets, and other non-token rules. Entries listed
39
+ * here are exclusion-only — they do not produce their own CSS file.
40
+ *
41
+ * Scope classes from `scoped:` entries are already added automatically;
42
+ * this field is the place to declare *non-bundled* boundaries (preview
43
+ * surfaces, opt-out subtrees, etc.) on top of those.
44
+ *
45
+ * Accepts any complete selector (`.class`, `#id`, `[attr]`, `:pseudo`).
46
+ * Bare identifiers are normalized to classes for backward compat with
47
+ * the automatic `scoped:`-derived boundary classes.
48
+ */
49
+ excludeBoundaries?: string[];
22
50
  }
23
51
  /**
24
52
  * Options for generateCSS function
@@ -1,4 +1,5 @@
1
1
 
2
+ import { EmitMode } from "../packages/automated-config/dist/utils/index.js";
2
3
  import { TailwindSafelistConfig } from "./generate.helpers.js";
3
4
  import { UDSCSSOptimizationOptions } from "./theme.js";
4
5
 
@@ -19,6 +20,33 @@ interface UDSCSSOptions {
19
20
  fontFaceDeclarations?: boolean;
20
21
  /** Debounce delay (ms) for watch mode regenerations */
21
22
  watchDebounce?: number;
23
+ /**
24
+ * Emission mode. `exhaustive` (default) emits a rule for every state — safe to
25
+ * coexist with other themes with no extra config. `selective` emits only
26
+ * rest + customized states (smaller) and relies on declared boundaries
27
+ * (`scoped` / `excludeBoundaries`) + the isolation fence for coexistence safety.
28
+ */
29
+ emit?: EmitMode;
30
+ /**
31
+ * Additional selectors whose subtrees this theme's bundles must not match.
32
+ * Every emitted bundle (main and each `scoped:` entry) gets a
33
+ * `:not(:where(:is(<boundaries>) *))` suffix on each theme-driven
34
+ * (token-referencing) rule that targets a NON-REST state (`:hover`,
35
+ * `:focus-within`, `:has(…)`, etc.), so its themed state styles never cross
36
+ * into one of these subtrees. Rest-state token rules resolve their tokens to
37
+ * the nested scope's own values (value-safe), so they're left untouched — as
38
+ * are Tailwind utilities, resets, and other non-token rules. Entries listed
39
+ * here are exclusion-only — they do not produce their own CSS file.
40
+ *
41
+ * Scope classes from `scoped:` entries are already added automatically;
42
+ * this field is the place to declare *non-bundled* boundaries (preview
43
+ * surfaces, opt-out subtrees, etc.) on top of those.
44
+ *
45
+ * Accepts any complete selector (`.class`, `#id`, `[attr]`, `:pseudo`).
46
+ * Bare identifiers are normalized to classes for backward compat with
47
+ * the automatic `scoped:`-derived boundary classes.
48
+ */
49
+ excludeBoundaries?: string[];
22
50
  }
23
51
  /**
24
52
  * Options for generateCSS function
@@ -81,7 +81,8 @@ const createTailwindPlugin = async (options) => {
81
81
  plugins: [require_tailwindPlugin.tailwindPlugin({
82
82
  config: options.config,
83
83
  disableFontFaceDeclarations: !options.enableFontFaceDeclarations,
84
- ignorePluginSafelists: true
84
+ ignorePluginSafelists: true,
85
+ emit: options.emit
85
86
  })]
86
87
  });
87
88
  };
@@ -97,6 +98,8 @@ const buildPostcssPlugins = (options) => {
97
98
  const plugins = [options.tailwindPlugin, (0, autoprefixer.default)()];
98
99
  if (options.shouldPruneVars) plugins.push(...getPruneVarPlugins(options.safeVarPrefixes));
99
100
  if (options.scopeClass) plugins.push((0, postcss_scope.default)(options.scopeClass));
101
+ const otherBoundaryScopes = options.otherBoundaryScopes ?? [];
102
+ if (otherBoundaryScopes.length > 0) plugins.push(require_postcss.appendIsolationExclusionPlugin(otherBoundaryScopes));
100
103
  return plugins;
101
104
  };
102
105
  const applyScopedColorModeFix = async (css, scopeClass) => {
@@ -109,6 +112,7 @@ const optimizeGeneratedCss = async (options) => {
109
112
  removeUnusedFonts: options.optimizationConfig?.removeUnusedFonts,
110
113
  removeEmptyRules: options.optimizationConfig?.removeEmptyRules,
111
114
  aggregateDuplicateSelectors: options.optimizationConfig?.aggregateDuplicateSelectors,
115
+ mergeIdenticalDeclarations: options.optimizationConfig?.mergeIdenticalDeclarations,
112
116
  referenceCss: options.referenceCss,
113
117
  scopeClass: options.scopeClass
114
118
  };
@@ -8,7 +8,7 @@ import { formatCssDuration } from "./runner.helpers.js";
8
8
  import { formatBytes } from "./utils.js";
9
9
  import { iconPruneVarsSafelist } from "../packages/icons/src/safelist.js";
10
10
  import { optimizeCSS } from "../utils/optimizeCSS.js";
11
- import { fixScopedColorModeSelectorsPlugin, fixScopedSelfOrParentSelectorsPlugin } from "./postcss.js";
11
+ import { appendIsolationExclusionPlugin, fixScopedColorModeSelectorsPlugin, fixScopedSelfOrParentSelectorsPlugin } from "./postcss.js";
12
12
  import path from "node:path";
13
13
  import { fileURLToPath } from "node:url";
14
14
  import fs from "node:fs";
@@ -74,7 +74,8 @@ const createTailwindPlugin = async (options) => {
74
74
  plugins: [tailwindPlugin({
75
75
  config: options.config,
76
76
  disableFontFaceDeclarations: !options.enableFontFaceDeclarations,
77
- ignorePluginSafelists: true
77
+ ignorePluginSafelists: true,
78
+ emit: options.emit
78
79
  })]
79
80
  });
80
81
  };
@@ -90,6 +91,8 @@ const buildPostcssPlugins = (options) => {
90
91
  const plugins = [options.tailwindPlugin, autoprefixer()];
91
92
  if (options.shouldPruneVars) plugins.push(...getPruneVarPlugins(options.safeVarPrefixes));
92
93
  if (options.scopeClass) plugins.push(postcssScope(options.scopeClass));
94
+ const otherBoundaryScopes = options.otherBoundaryScopes ?? [];
95
+ if (otherBoundaryScopes.length > 0) plugins.push(appendIsolationExclusionPlugin(otherBoundaryScopes));
93
96
  return plugins;
94
97
  };
95
98
  const applyScopedColorModeFix = async (css, scopeClass) => {
@@ -102,6 +105,7 @@ const optimizeGeneratedCss = async (options) => {
102
105
  removeUnusedFonts: options.optimizationConfig?.removeUnusedFonts,
103
106
  removeEmptyRules: options.optimizationConfig?.removeEmptyRules,
104
107
  aggregateDuplicateSelectors: options.optimizationConfig?.aggregateDuplicateSelectors,
108
+ mergeIdenticalDeclarations: options.optimizationConfig?.mergeIdenticalDeclarations,
105
109
  referenceCss: options.referenceCss,
106
110
  scopeClass: options.scopeClass
107
111
  };
@@ -43,11 +43,13 @@ const generateCSS = async (safelist, config, options) => {
43
43
  safelist,
44
44
  config,
45
45
  enablePreflight: cssFlags.enablePreflight,
46
- enableFontFaceDeclarations: cssFlags.enableFontFaceDeclarations
46
+ enableFontFaceDeclarations: cssFlags.enableFontFaceDeclarations,
47
+ emit: options?.cssOptions?.emit
47
48
  }),
48
49
  shouldPruneVars: cssFlags.shouldPruneVars,
49
50
  safeVarPrefixes: options?.safeVarPrefixes ?? [],
50
- scopeClass
51
+ scopeClass,
52
+ otherBoundaryScopes: options?.otherBoundaryScopes ?? []
51
53
  });
52
54
  const result = await measureAsync("gen:postcss", () => postcss(plugins).process(sourceCSS, { from: void 0 }));
53
55
  let css = await measureAsync("gen:scoped-color-fix", () => applyScopedColorModeFix(result.css, scopeClass));
@@ -49,6 +49,87 @@ const fixScopedColorModeSelectorsPlugin = (scopeClass) => {
49
49
  }
50
50
  };
51
51
  };
52
+ /**
53
+ * True when any declaration in the rule references a UDS token variable
54
+ * (`var(--uds-…)`) — i.e. the rule is theme-driven. See
55
+ * {@link appendIsolationExclusionPlugin} for why only those rules are isolated.
56
+ */
57
+ const ruleReferencesUdsToken = (rule) => {
58
+ let found = false;
59
+ rule.walkDecls((decl) => {
60
+ if (decl.value.includes("var(--uds-")) {
61
+ found = true;
62
+ return false;
63
+ }
64
+ });
65
+ return found;
66
+ };
67
+ /**
68
+ * PostCSS plugin to enforce per-bundle style isolation by appending an
69
+ * exclusion suffix to *theme-driven* rules' selectors.
70
+ *
71
+ * Each qualifying rule's deepest simple selector gets augmented with
72
+ * `:not(:where(:is(.A, .B, …) *))`
73
+ *
74
+ * where `.A`, `.B`, … are the scope classes of OTHER scoped bundles loaded
75
+ * alongside this one (i.e. every `scoped:` entry in `uds.theme.ts` except
76
+ * this bundle's own scope) plus any non-bundled selectors declared via
77
+ * `css.excludeBoundaries`. This prevents this bundle's rules from matching
78
+ * elements that live inside another scoped library's subtree, so each
79
+ * scoped package gets a clean cascade inside its own scope-class wrapper
80
+ * without bleed-through from neighboring bundles.
81
+ *
82
+ * **Why "theme-driven" only:** rules that reference a UDS token variable
83
+ * (`var(--uds-…)` in any declaration value) carry committed-theme values
84
+ * that would wrongly leak into a preview cell when the draft theme leaves
85
+ * a property undefined (cascade order can't undo a value that isn't being
86
+ * overridden). Rules that do NOT reference a token — Tailwind utilities,
87
+ * structural resets, `display: flex` on a layout class, padding helpers,
88
+ * etc. — are theme-independent: blocking them inside a preview boundary
89
+ * is collateral damage that strips needed structural/utility behavior from
90
+ * the rendered content. The plugin therefore leaves non-token-referencing
91
+ * rules untouched (see {@link ruleReferencesUdsToken}).
92
+ *
93
+ * **Why non-rest only:** among token-referencing rules, only non-rest
94
+ * STATE rules (`:hover`, `:focus-within`, `:has(…)`, etc.) need the fence.
95
+ * A rest theme rule that leaks into a nested foreign scope resolves its
96
+ * `var(--uds-*)` to that scope's own tokens (postcss-scope rewrites
97
+ * `:root`→scope), so it is value-safe and fencing it would only add bytes
98
+ * to the wire. The harmful leak is a non-rest state rule whose higher
99
+ * specificity wins over the nested scope's own state rule, overriding a
100
+ * value the nested scope explicitly set. Only those selectors are augmented
101
+ * (see {@link isNonRestStateSelector}).
102
+ *
103
+ * Bundles whose neighbor list is empty (no other scoped libraries and no
104
+ * `excludeBoundaries` entries are configured) get the selector unchanged —
105
+ * the plugin is a no-op there and the bundle is byte-identical to its
106
+ * pre-isolation output.
107
+ *
108
+ * `:where()` contributes (0, 0, 0) specificity, so the augmented selector
109
+ * preserves the original's specificity exactly.
110
+ *
111
+ * NOTE: A single CSS `@scope (.<scope>) to (.A, .B, …)` declaration at the
112
+ * top of the bundle would be the cleaner expression of this isolation — one
113
+ * declaration instead of N selector tweaks — but `@scope` lacked sufficient
114
+ * browser support when this was implemented (May 2026 — requires Chrome
115
+ * 118+, Safari 17.4+, Firefox 128+). When the support floor moves up, this
116
+ * plugin can be replaced with an `@scope` wrapper around the bundle,
117
+ * dropping the per-rule suffix.
118
+ */
119
+ const appendIsolationExclusionPlugin = (otherBoundaryScopes) => {
120
+ return {
121
+ postcssPlugin: "append-isolation-exclusion",
122
+ Once(root) {
123
+ if (otherBoundaryScopes.length === 0) return;
124
+ root.walkRules((rule) => {
125
+ if (!ruleReferencesUdsToken(rule)) return;
126
+ const nextSelectors = rule.selectors.map((selector) => require_postcss_helpers.isNonRestStateSelector(selector) ? require_postcss_helpers.injectIsolationExclusion(selector, otherBoundaryScopes) : selector);
127
+ if (nextSelectors.some((selector, idx) => selector !== rule.selectors[idx])) rule.selectors = nextSelectors;
128
+ });
129
+ }
130
+ };
131
+ };
52
132
  //#endregion
133
+ exports.appendIsolationExclusionPlugin = appendIsolationExclusionPlugin;
53
134
  exports.fixScopedColorModeSelectorsPlugin = fixScopedColorModeSelectorsPlugin;
54
135
  exports.fixScopedSelfOrParentSelectorsPlugin = fixScopedSelfOrParentSelectorsPlugin;
@@ -33,6 +33,66 @@ const getScopedSelfOrParentAlternativeSelector = (selector, scopeClass) => {
33
33
  if (/^\.uds-color-mode-(?:dark|light)(?:$|[.#[:])/.test(scopedTarget)) return null;
34
34
  return `${scopeClass}${scopedTarget}${targetMatch[2]}`;
35
35
  };
36
+ /**
37
+ * Insert the isolation-exclusion suffix
38
+ * `:not(:where(:is(.A, .B, …) *))` into a single selector. The list is the
39
+ * set of scope classes belonging to OTHER scoped bundles loaded on the page;
40
+ * the suffix causes this bundle's rule to skip elements whose ancestor chain
41
+ * contains any of those scope classes, preventing rule bleed-through into
42
+ * another scoped library's subtree.
43
+ *
44
+ * For selectors ending in a pseudo-element (`.x::placeholder`, `.x::before`,
45
+ * etc.), the suffix is inserted *before* the pseudo-element marker, since
46
+ * pseudo-elements must remain at the very end of a selector and the
47
+ * exclusion semantically applies to the host element.
48
+ *
49
+ * PRECONDITION: a pseudo-element, when present, is the trailing token of the
50
+ * selector — true for every UDS/Tailwind-generated rule this plugin runs on.
51
+ * Detection is a simple `lastIndexOf('::')`, so a pseudo-element in a non-final
52
+ * compound (`.x::before .y`) or a literal `::` inside an attribute value would
53
+ * be mis-handled; neither shape occurs in the generated CSS this processes.
54
+ *
55
+ * Returns the selector unchanged when `otherBoundaryScopes` is empty —
56
+ * there's nothing to exclude.
57
+ */
58
+ const injectIsolationExclusion = (selector, otherBoundaryScopes) => {
59
+ if (otherBoundaryScopes.length === 0) return selector;
60
+ const suffix = `:not(:where(:is(${otherBoundaryScopes.map((scope) => {
61
+ const trimmed = scope.trim();
62
+ return /^[.#[:]/.test(trimmed) ? trimmed : `.${trimmed}`;
63
+ }).join(", ")}) *))`;
64
+ const pseudoElementIdx = selector.lastIndexOf("::");
65
+ if (pseudoElementIdx === -1) return `${selector}${suffix}`;
66
+ return `${selector.slice(0, pseudoElementIdx)}${suffix}${selector.slice(pseudoElementIdx)}`;
67
+ };
68
+ /**
69
+ * State pseudo-classes UDS emits for non-rest states: interactive
70
+ * (`:hover`, `:active`, `:focus`, `:focus-within`, `:focus-visible`,
71
+ * `:visited`, legacy `:disabled`) and `:has(…)` — which UDS uses only for the
72
+ * `invalid`/`readonly`/`placeholder-shown`/`autofill`/`disabled` atoms. The
73
+ * rest baseline carries none of these. Pseudo-ELEMENTS (`::placeholder`,
74
+ * `::before`) use `::` so the single-colon matcher skips them; the structural
75
+ * `:where()`/`:is()`/`:not()` (hover blockers and the isolation suffix itself)
76
+ * are not states.
77
+ *
78
+ * MAINTENANCE: `readonly`/`placeholder-shown`/`autofill` (and `invalid`/
79
+ * `disabled`) are intentionally absent as direct terms — UDS emits them wrapped
80
+ * in `:has(…)`, so the `:has\(` branch already catches them. If a component ever
81
+ * emits one of those atoms as a DIRECT pseudo-class (e.g. `:read-only`), add it
82
+ * here: a missed state selector is a false negative (state bleed — the leak this
83
+ * fence prevents), whereas an over-match would only fence a value-safe rest rule.
84
+ */
85
+ const STATE_PSEUDO_RE = /:(?:hover|active|visited|disabled|focus(?:-within|-visible)?)\b|:has\(/;
86
+ /**
87
+ * True when a selector targets a non-rest (state) rule. Only non-rest theme
88
+ * rules need the isolation fence: a rest theme rule leaking into a nested
89
+ * foreign scope resolves its `var(--uds-*)` to that scope's own tokens
90
+ * (postcss-scope rewrites `:root`→scope), so it is value-safe and fencing it
91
+ * would only add bytes to the wire.
92
+ */
93
+ const isNonRestStateSelector = (selector) => STATE_PSEUDO_RE.test(selector);
36
94
  //#endregion
37
95
  exports.buildScopedColorModeSelectorList = buildScopedColorModeSelectorList;
38
96
  exports.getScopedSelfOrParentAlternativeSelector = getScopedSelfOrParentAlternativeSelector;
97
+ exports.injectIsolationExclusion = injectIsolationExclusion;
98
+ exports.isNonRestStateSelector = isNonRestStateSelector;
@@ -33,5 +33,63 @@ const getScopedSelfOrParentAlternativeSelector = (selector, scopeClass) => {
33
33
  if (/^\.uds-color-mode-(?:dark|light)(?:$|[.#[:])/.test(scopedTarget)) return null;
34
34
  return `${scopeClass}${scopedTarget}${targetMatch[2]}`;
35
35
  };
36
+ /**
37
+ * Insert the isolation-exclusion suffix
38
+ * `:not(:where(:is(.A, .B, …) *))` into a single selector. The list is the
39
+ * set of scope classes belonging to OTHER scoped bundles loaded on the page;
40
+ * the suffix causes this bundle's rule to skip elements whose ancestor chain
41
+ * contains any of those scope classes, preventing rule bleed-through into
42
+ * another scoped library's subtree.
43
+ *
44
+ * For selectors ending in a pseudo-element (`.x::placeholder`, `.x::before`,
45
+ * etc.), the suffix is inserted *before* the pseudo-element marker, since
46
+ * pseudo-elements must remain at the very end of a selector and the
47
+ * exclusion semantically applies to the host element.
48
+ *
49
+ * PRECONDITION: a pseudo-element, when present, is the trailing token of the
50
+ * selector — true for every UDS/Tailwind-generated rule this plugin runs on.
51
+ * Detection is a simple `lastIndexOf('::')`, so a pseudo-element in a non-final
52
+ * compound (`.x::before .y`) or a literal `::` inside an attribute value would
53
+ * be mis-handled; neither shape occurs in the generated CSS this processes.
54
+ *
55
+ * Returns the selector unchanged when `otherBoundaryScopes` is empty —
56
+ * there's nothing to exclude.
57
+ */
58
+ const injectIsolationExclusion = (selector, otherBoundaryScopes) => {
59
+ if (otherBoundaryScopes.length === 0) return selector;
60
+ const suffix = `:not(:where(:is(${otherBoundaryScopes.map((scope) => {
61
+ const trimmed = scope.trim();
62
+ return /^[.#[:]/.test(trimmed) ? trimmed : `.${trimmed}`;
63
+ }).join(", ")}) *))`;
64
+ const pseudoElementIdx = selector.lastIndexOf("::");
65
+ if (pseudoElementIdx === -1) return `${selector}${suffix}`;
66
+ return `${selector.slice(0, pseudoElementIdx)}${suffix}${selector.slice(pseudoElementIdx)}`;
67
+ };
68
+ /**
69
+ * State pseudo-classes UDS emits for non-rest states: interactive
70
+ * (`:hover`, `:active`, `:focus`, `:focus-within`, `:focus-visible`,
71
+ * `:visited`, legacy `:disabled`) and `:has(…)` — which UDS uses only for the
72
+ * `invalid`/`readonly`/`placeholder-shown`/`autofill`/`disabled` atoms. The
73
+ * rest baseline carries none of these. Pseudo-ELEMENTS (`::placeholder`,
74
+ * `::before`) use `::` so the single-colon matcher skips them; the structural
75
+ * `:where()`/`:is()`/`:not()` (hover blockers and the isolation suffix itself)
76
+ * are not states.
77
+ *
78
+ * MAINTENANCE: `readonly`/`placeholder-shown`/`autofill` (and `invalid`/
79
+ * `disabled`) are intentionally absent as direct terms — UDS emits them wrapped
80
+ * in `:has(…)`, so the `:has\(` branch already catches them. If a component ever
81
+ * emits one of those atoms as a DIRECT pseudo-class (e.g. `:read-only`), add it
82
+ * here: a missed state selector is a false negative (state bleed — the leak this
83
+ * fence prevents), whereas an over-match would only fence a value-safe rest rule.
84
+ */
85
+ const STATE_PSEUDO_RE = /:(?:hover|active|visited|disabled|focus(?:-within|-visible)?)\b|:has\(/;
86
+ /**
87
+ * True when a selector targets a non-rest (state) rule. Only non-rest theme
88
+ * rules need the isolation fence: a rest theme rule leaking into a nested
89
+ * foreign scope resolves its `var(--uds-*)` to that scope's own tokens
90
+ * (postcss-scope rewrites `:root`→scope), so it is value-safe and fencing it
91
+ * would only add bytes to the wire.
92
+ */
93
+ const isNonRestStateSelector = (selector) => STATE_PSEUDO_RE.test(selector);
36
94
  //#endregion
37
- export { buildScopedColorModeSelectorList, getScopedSelfOrParentAlternativeSelector };
95
+ export { buildScopedColorModeSelectorList, getScopedSelfOrParentAlternativeSelector, injectIsolationExclusion, isNonRestStateSelector };
@@ -1,5 +1,5 @@
1
1
  /*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
2
- import { buildScopedColorModeSelectorList, getScopedSelfOrParentAlternativeSelector } from "./postcss.helpers.js";
2
+ import { buildScopedColorModeSelectorList, getScopedSelfOrParentAlternativeSelector, injectIsolationExclusion, isNonRestStateSelector } from "./postcss.helpers.js";
3
3
  //#region ../css/dist/css/postcss.mjs
4
4
  /*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
5
5
  const fixScopedSelfOrParentSelectorsPlugin = (scopeClass) => {
@@ -49,5 +49,85 @@ const fixScopedColorModeSelectorsPlugin = (scopeClass) => {
49
49
  }
50
50
  };
51
51
  };
52
+ /**
53
+ * True when any declaration in the rule references a UDS token variable
54
+ * (`var(--uds-…)`) — i.e. the rule is theme-driven. See
55
+ * {@link appendIsolationExclusionPlugin} for why only those rules are isolated.
56
+ */
57
+ const ruleReferencesUdsToken = (rule) => {
58
+ let found = false;
59
+ rule.walkDecls((decl) => {
60
+ if (decl.value.includes("var(--uds-")) {
61
+ found = true;
62
+ return false;
63
+ }
64
+ });
65
+ return found;
66
+ };
67
+ /**
68
+ * PostCSS plugin to enforce per-bundle style isolation by appending an
69
+ * exclusion suffix to *theme-driven* rules' selectors.
70
+ *
71
+ * Each qualifying rule's deepest simple selector gets augmented with
72
+ * `:not(:where(:is(.A, .B, …) *))`
73
+ *
74
+ * where `.A`, `.B`, … are the scope classes of OTHER scoped bundles loaded
75
+ * alongside this one (i.e. every `scoped:` entry in `uds.theme.ts` except
76
+ * this bundle's own scope) plus any non-bundled selectors declared via
77
+ * `css.excludeBoundaries`. This prevents this bundle's rules from matching
78
+ * elements that live inside another scoped library's subtree, so each
79
+ * scoped package gets a clean cascade inside its own scope-class wrapper
80
+ * without bleed-through from neighboring bundles.
81
+ *
82
+ * **Why "theme-driven" only:** rules that reference a UDS token variable
83
+ * (`var(--uds-…)` in any declaration value) carry committed-theme values
84
+ * that would wrongly leak into a preview cell when the draft theme leaves
85
+ * a property undefined (cascade order can't undo a value that isn't being
86
+ * overridden). Rules that do NOT reference a token — Tailwind utilities,
87
+ * structural resets, `display: flex` on a layout class, padding helpers,
88
+ * etc. — are theme-independent: blocking them inside a preview boundary
89
+ * is collateral damage that strips needed structural/utility behavior from
90
+ * the rendered content. The plugin therefore leaves non-token-referencing
91
+ * rules untouched (see {@link ruleReferencesUdsToken}).
92
+ *
93
+ * **Why non-rest only:** among token-referencing rules, only non-rest
94
+ * STATE rules (`:hover`, `:focus-within`, `:has(…)`, etc.) need the fence.
95
+ * A rest theme rule that leaks into a nested foreign scope resolves its
96
+ * `var(--uds-*)` to that scope's own tokens (postcss-scope rewrites
97
+ * `:root`→scope), so it is value-safe and fencing it would only add bytes
98
+ * to the wire. The harmful leak is a non-rest state rule whose higher
99
+ * specificity wins over the nested scope's own state rule, overriding a
100
+ * value the nested scope explicitly set. Only those selectors are augmented
101
+ * (see {@link isNonRestStateSelector}).
102
+ *
103
+ * Bundles whose neighbor list is empty (no other scoped libraries and no
104
+ * `excludeBoundaries` entries are configured) get the selector unchanged —
105
+ * the plugin is a no-op there and the bundle is byte-identical to its
106
+ * pre-isolation output.
107
+ *
108
+ * `:where()` contributes (0, 0, 0) specificity, so the augmented selector
109
+ * preserves the original's specificity exactly.
110
+ *
111
+ * NOTE: A single CSS `@scope (.<scope>) to (.A, .B, …)` declaration at the
112
+ * top of the bundle would be the cleaner expression of this isolation — one
113
+ * declaration instead of N selector tweaks — but `@scope` lacked sufficient
114
+ * browser support when this was implemented (May 2026 — requires Chrome
115
+ * 118+, Safari 17.4+, Firefox 128+). When the support floor moves up, this
116
+ * plugin can be replaced with an `@scope` wrapper around the bundle,
117
+ * dropping the per-rule suffix.
118
+ */
119
+ const appendIsolationExclusionPlugin = (otherBoundaryScopes) => {
120
+ return {
121
+ postcssPlugin: "append-isolation-exclusion",
122
+ Once(root) {
123
+ if (otherBoundaryScopes.length === 0) return;
124
+ root.walkRules((rule) => {
125
+ if (!ruleReferencesUdsToken(rule)) return;
126
+ const nextSelectors = rule.selectors.map((selector) => isNonRestStateSelector(selector) ? injectIsolationExclusion(selector, otherBoundaryScopes) : selector);
127
+ if (nextSelectors.some((selector, idx) => selector !== rule.selectors[idx])) rule.selectors = nextSelectors;
128
+ });
129
+ }
130
+ };
131
+ };
52
132
  //#endregion
53
- export { fixScopedColorModeSelectorsPlugin, fixScopedSelfOrParentSelectorsPlugin };
133
+ export { appendIsolationExclusionPlugin, fixScopedColorModeSelectorsPlugin, fixScopedSelfOrParentSelectorsPlugin };
@@ -340,11 +340,17 @@ const runThemeMode = async (options, context) => {
340
340
  ...require_safelist.getInternalSafelistClasses()
341
341
  ]);
342
342
  const allMotionComponents = [...inheritedComponents];
343
+ const emit = options.emit ?? themeConfig.css?.emit ?? "exhaustive";
344
+ const allBoundarySelectors = [...emit === "selective" ? scopedPackageTargets.map((target) => target.scopeClass) : [], ...themeConfig.css?.excludeBoundaries ?? []];
343
345
  require_perf.captureMemory("before-gen");
344
346
  const mainCssResult = await require_perf.measureAsync("gen", () => require_generate.generateCSS([...themeConfig.css?.safelist ?? [], ...mainSafelist], appConfig, {
345
347
  scope: options.scope,
346
348
  contentDir: entryDirs,
347
- cssOptions: themeConfig.css,
349
+ cssOptions: {
350
+ ...themeConfig.css,
351
+ emit
352
+ },
353
+ otherBoundaryScopes: allBoundarySelectors,
348
354
  safeVarPrefixes: [
349
355
  ...require_utils.getMotionVarPrefixes(context.componentData, allMotionComponents),
350
356
  ...require_utils.getConfigurableCssVariables(),
@@ -375,7 +381,11 @@ const runThemeMode = async (options, context) => {
375
381
  const scopedCssResult = await require_generate.generateCSS([...themeConfig.css?.safelist ?? [], ...packageSafelist], packageConfig, {
376
382
  scope: scopedPackageTarget.scopeClass,
377
383
  contentDir: scopedPackageTarget.entryDirs,
378
- cssOptions: themeConfig.css,
384
+ cssOptions: {
385
+ ...themeConfig.css,
386
+ emit
387
+ },
388
+ otherBoundaryScopes: allBoundarySelectors.filter((selector) => selector !== scopedPackageTarget.scopeClass),
379
389
  referenceCss: themeConfig.css?.optimization?.deduplicateScopedCss === false ? void 0 : mainCssResult.css,
380
390
  safeVarPrefixes: [
381
391
  ...require_utils.getMotionVarPrefixes(context.componentData, [...packageScanResult.components]),
@@ -338,11 +338,17 @@ const runThemeMode = async (options, context) => {
338
338
  ...getInternalSafelistClasses()
339
339
  ]);
340
340
  const allMotionComponents = [...inheritedComponents];
341
+ const emit = options.emit ?? themeConfig.css?.emit ?? "exhaustive";
342
+ const allBoundarySelectors = [...emit === "selective" ? scopedPackageTargets.map((target) => target.scopeClass) : [], ...themeConfig.css?.excludeBoundaries ?? []];
341
343
  captureMemory("before-gen");
342
344
  const mainCssResult = await measureAsync("gen", () => generateCSS([...themeConfig.css?.safelist ?? [], ...mainSafelist], appConfig, {
343
345
  scope: options.scope,
344
346
  contentDir: entryDirs,
345
- cssOptions: themeConfig.css,
347
+ cssOptions: {
348
+ ...themeConfig.css,
349
+ emit
350
+ },
351
+ otherBoundaryScopes: allBoundarySelectors,
346
352
  safeVarPrefixes: [
347
353
  ...getMotionVarPrefixes(context.componentData, allMotionComponents),
348
354
  ...getConfigurableCssVariables(),
@@ -373,7 +379,11 @@ const runThemeMode = async (options, context) => {
373
379
  const scopedCssResult = await generateCSS([...themeConfig.css?.safelist ?? [], ...packageSafelist], packageConfig, {
374
380
  scope: scopedPackageTarget.scopeClass,
375
381
  contentDir: scopedPackageTarget.entryDirs,
376
- cssOptions: themeConfig.css,
382
+ cssOptions: {
383
+ ...themeConfig.css,
384
+ emit
385
+ },
386
+ otherBoundaryScopes: allBoundarySelectors.filter((selector) => selector !== scopedPackageTarget.scopeClass),
377
387
  referenceCss: themeConfig.css?.optimization?.deduplicateScopedCss === false ? void 0 : mainCssResult.css,
378
388
  safeVarPrefixes: [
379
389
  ...getMotionVarPrefixes(context.componentData, [...packageScanResult.components]),
@@ -29,6 +29,12 @@ interface UDSCSSOptimizationOptions {
29
29
  removeEmptyRules?: boolean;
30
30
  /** Aggregate duplicate selectors (default: true) */
31
31
  aggregateDuplicateSelectors?: boolean;
32
+ /**
33
+ * Merge adjacent rules sharing an identical declaration block into a single
34
+ * comma-separated selector group (default: true). Cascade-safe: only
35
+ * consecutive sibling rules are merged.
36
+ */
37
+ mergeIdenticalDeclarations?: boolean;
32
38
  /**
33
39
  * Remove duplicate content from scoped CSS that already exists in main CSS (default: true)
34
40
  * Currently deduplicates @font-face declarations. Reduces bundle size when scoped packages
@@ -29,6 +29,12 @@ interface UDSCSSOptimizationOptions {
29
29
  removeEmptyRules?: boolean;
30
30
  /** Aggregate duplicate selectors (default: true) */
31
31
  aggregateDuplicateSelectors?: boolean;
32
+ /**
33
+ * Merge adjacent rules sharing an identical declaration block into a single
34
+ * comma-separated selector group (default: true). Cascade-safe: only
35
+ * consecutive sibling rules are merged.
36
+ */
37
+ mergeIdenticalDeclarations?: boolean;
32
38
  /**
33
39
  * Remove duplicate content from scoped CSS that already exists in main CSS (default: true)
34
40
  * Currently deduplicates @font-face declarations. Reduces bundle size when scoped packages
@@ -8,7 +8,7 @@ const require_index$1 = require("../../motion-tokens/dist/index.cjs");
8
8
  /*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
9
9
  /*! © 2026 Yahoo, Inc. UDS Default Config v0.0.0-development */
10
10
  const isInputWrapperCtx = (context) => {
11
- return context.componentName === "input" && context.layer === "inputWrapper";
11
+ return context.componentName === "input" && (context.layer === "inputWrapper" || context.layer === "inputWrapperDynamic");
12
12
  };
13
13
  const hexToRgb = (hex) => {
14
14
  hex = hex.replace("#", "");