@yahoo/uds-v5-wip 1.13.0 → 1.15.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 (37) hide show
  1. package/dist/config/dist/createConfig.d.ts +70 -20
  2. package/dist/config/dist/createConfig.js +152 -73
  3. package/dist/config/dist/index.d.ts +2 -2
  4. package/dist/config/dist/index.js +2 -1
  5. package/dist/config/dist/preset-merge.js +12 -4
  6. package/dist/config/dist/propertyGroups.d.ts +11 -0
  7. package/dist/config/dist/propertyGroups.js +432 -0
  8. package/dist/config/dist/resolveTokenTypes.d.ts +1 -0
  9. package/dist/config/dist/resolveTokenTypes.js +149 -0
  10. package/dist/config/dist/types.d.ts +31 -64
  11. package/dist/config.d.ts +45 -3842
  12. package/dist/config.js +1 -2
  13. package/dist/foundational-presets/dist/boldVibrant.d.ts +44 -3839
  14. package/dist/foundational-presets/dist/brutalist.d.ts +44 -3839
  15. package/dist/foundational-presets/dist/candy.d.ts +44 -3839
  16. package/dist/foundational-presets/dist/cleanMinimalist.d.ts +44 -3839
  17. package/dist/foundational-presets/dist/corporate.d.ts +44 -3839
  18. package/dist/foundational-presets/dist/darkMoody.d.ts +44 -3839
  19. package/dist/foundational-presets/dist/defaultPreset.d.ts +44 -3839
  20. package/dist/foundational-presets/dist/defaultPreset.js +288 -614
  21. package/dist/foundational-presets/dist/forest.d.ts +44 -3839
  22. package/dist/foundational-presets/dist/highContrast.d.ts +44 -3839
  23. package/dist/foundational-presets/dist/lavender.d.ts +44 -3839
  24. package/dist/foundational-presets/dist/luxury.d.ts +44 -3839
  25. package/dist/foundational-presets/dist/monochrome.d.ts +44 -3839
  26. package/dist/foundational-presets/dist/motion.d.ts +2 -1
  27. package/dist/foundational-presets/dist/neonCyber.d.ts +44 -3839
  28. package/dist/foundational-presets/dist/newspaper.d.ts +44 -3839
  29. package/dist/foundational-presets/dist/ocean.d.ts +44 -3839
  30. package/dist/foundational-presets/dist/slate.d.ts +44 -3839
  31. package/dist/foundational-presets/dist/sunset.d.ts +44 -3839
  32. package/dist/foundational-presets/dist/terminal.d.ts +44 -3839
  33. package/dist/foundational-presets/dist/warmOrganic.d.ts +44 -3839
  34. package/dist/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +3 -3
  36. package/dist/config/dist/resolvedMappings.d.ts +0 -22
  37. package/dist/config/dist/resolvedMappings.js +0 -43
@@ -1,4 +1,5 @@
1
- import { UtilitiesConfig, UtilityDef, VarsConfig } from "./types.js";
1
+ import { PropertyGroupId } from "./propertyGroups.js";
2
+ import { TokenType, VarsConfig } from "./types.js";
2
3
  import { BaseModifierProp, ComponentsConfig, ConfigurableProp, MacroConfig, ModifierProp, MotionPreset, RemotionComponentDef, RemotionConfig, RemotionTransitionDef, StyleProp } from "@uds/types";
3
4
 
4
5
  //#region ../config/dist/createConfig.d.ts
@@ -47,11 +48,23 @@ interface ArbitraryTokenGroup {
47
48
  type AtomicTokenValue<M extends ModifierNameShape> = {
48
49
  name: string;
49
50
  value: string;
51
+ /**
52
+ * Resolved DTCG-aligned token type. Populated per-token only when the
53
+ * token overrides the group-level `type` (e.g. a `'string'`-typed
54
+ * `transparent` sitting inside a `'color'` namespace).
55
+ */
56
+ type?: TokenType;
50
57
  modifiers?: Partial<Record<M, string>>;
51
58
  };
52
59
  interface AtomicToken<M extends ModifierNameShape = ModifierNameShape> {
53
60
  properties: ConfigurableProp[];
54
61
  tokens: AtomicTokenValue<M>[];
62
+ /**
63
+ * Resolved DTCG-aligned token type for the whole group. Set from
64
+ * `$type` on `defineVars`, or inferred from `defineScopes` / value sniff
65
+ * during config resolution.
66
+ */
67
+ type?: TokenType;
55
68
  /**
56
69
  * Optional CSS variable prefix override
57
70
  *
@@ -76,29 +89,19 @@ interface AtomicToken<M extends ModifierNameShape = ModifierNameShape> {
76
89
  /** Merge two token structures */
77
90
  type MergeTokens<A, B> = { [K in keyof A | keyof B]: K extends keyof B ? K extends keyof A ? A[K] & B[K] : B[K] : K extends keyof A ? A[K] : never };
78
91
  /**
79
- * Given a VarsConfig and a single utility `values` string (e.g. `'{fontWeight}'`),
80
- * extract the token-name keys from the referenced namespace.
81
- */
82
- type ExtractNsTokens<TVars extends VarsConfig, Ref extends string> = Ref extends `{${infer NS}}` ? NS extends keyof TVars ? { [K in keyof TVars[NS]]: string } : Record<string, string> : Record<string, string>;
83
- /** Collapse a union into an intersection so multi-namespace token maps are merged. */
84
- type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never;
85
- /**
86
- * Derive the token map shape (`{ propName: { tokenName: string } }`) that
87
- * `defineUtilities` adds to `TTokens`, based on the accumulated `TVars` and the
88
- * concrete utilities config type.
89
- *
90
- * Only `values: '{namespace}'` references produce typed tokens; literal-values
91
- * and boolean utilities fall through to `Record<string, string>`.
92
+ * Derive token types directly from defineVars namespaces.
93
+ * Each var namespace key becomes a token property with the namespace's token names.
94
+ * An index signature allows property-name access (e.g., `tokens.borderRadius`)
95
+ * in addition to var-namespace access (e.g., `tokens.radius`).
96
+ * Used by `defineScopes` to populate tokens without an explicit utility mapping.
92
97
  */
93
- type ExtractVarsTokens<TVars extends VarsConfig, TUtils extends Record<string, unknown>> = { [P in keyof TUtils]: TUtils[P] extends {
94
- values: infer V;
95
- } ? V extends string ? ExtractNsTokens<TVars, V> : V extends readonly string[] ? UnionToIntersection<ExtractNsTokens<TVars, V[number]>> : Record<string, string> : Record<string, string> };
98
+ type VarsToTokens<TVars extends VarsConfig> = { [K in keyof TVars]: { [T in Exclude<keyof TVars[K], '$type'>]: string } } & Record<string, Record<string, string>>;
96
99
  /** Empty tokens type for initial state */
97
100
  type EmptyTokens = {};
98
101
  /** Empty vars type for initial state */
99
102
  type EmptyVars = {};
100
103
  /** Build a structured token reference object from atomic tokens */
101
- declare function buildTokenReference(atomic: AtomicToken<ModifierNameShape>[], configPrefix: string): Record<ConfigurableProp, Record<string, string>>;
104
+ declare function buildTokenReference(atomic: AtomicToken<ModifierNameShape>[], configPrefix: string): Record<string, Record<string, string>>;
102
105
  /** Build a structured macro reference object for use in defineModifiers context */
103
106
  /** Extract all modifiers from mode groups and check if any are reserved */
104
107
  type CheckForReservedModifiers<T extends readonly ModeGroup[]> = true extends HasReservedModifier<GetModifierName<T>> ? 'ERROR: Cannot use reserved modifier names from ModifierProp. Please use a different modifier name.' : T;
@@ -136,7 +139,18 @@ interface UdsConfigData {
136
139
  examples?: Record<string, ExampleDef>;
137
140
  designPrinciples?: string[];
138
141
  vars?: VarsConfig;
139
- utilities?: UtilitiesConfig;
142
+ /**
143
+ * Map each ConfigurableProp to its generated CSS class prefix.
144
+ * Set via `defineClassNames()`. Falls back to kebab-case of the property name.
145
+ */
146
+ classNames?: Record<string, string>;
147
+ /**
148
+ * Map each var namespace to the property group IDs it feeds.
149
+ * Set via `defineScopes()`. This is the canonical source for
150
+ * "which tokens can this property use" — the Studio UI reads and
151
+ * edits this via draft patches.
152
+ */
153
+ scopes?: Record<string, string[]>;
140
154
  }
141
155
  interface ComponentConfig<TComponents extends ComponentsConfig<string> = ComponentsConfig<string>, TMotion extends MotionPresetsDef | undefined = MotionPresetsDef | undefined> {
142
156
  name?: string;
@@ -205,7 +219,43 @@ interface UdsConfig<TModifier extends ModifierNameShape = ModifierProp, TTokens
205
219
  tokens: TTokens;
206
220
  }) => Record<string, RemotionTransitionDef>)): ConfigResult<TModifier, TTokens, TMotion, TExt, TMacros, TModeModifiers, TVars>;
207
221
  defineVars<const TVarsNew extends VarsConfig<[TModeModifiers] extends [never] ? `_${string}` : TModeModifiers>>(vars: TVarsNew): ConfigResult<TModifier, TTokens, TMotion, TExt, TMacros, TModeModifiers, TVars & TVarsNew>;
208
- defineUtilities<const TUtils extends { [K in keyof TUtils]: K extends StyleProp ? UtilityDef : never }>(utilities: TUtils): ConfigResult<TModifier, MergeTokens<TTokens, ExtractVarsTokens<TVars, TUtils>>, TMotion, TExt, TMacros, TModeModifiers, TVars>;
222
+ /**
223
+ * Map each ConfigurableProp to a CSS class prefix.
224
+ * Falls back to kebab-case of the property name when not specified.
225
+ * Typically called once by the foundational preset — consumers rarely touch this.
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * uds.defineClassNames({
230
+ * color: 'text',
231
+ * bg: 'bg',
232
+ * borderColor: 'border-color',
233
+ * spacing: 'p',
234
+ * gap: 'gap',
235
+ * borderRadius: 'rounded',
236
+ * })
237
+ * ```
238
+ */
239
+ defineClassNames(classNames: { [K in StyleProp]?: string }): ConfigResult<TModifier, TTokens, TMotion, TExt, TMacros, TModeModifiers, TVars>;
240
+ /**
241
+ * Map var namespaces to property groups, defining which tokens each property
242
+ * group can reference. This is the canonical scope definition — the Studio UI
243
+ * renders it as checkboxes per token group.
244
+ *
245
+ * @example
246
+ * ```ts
247
+ * uds
248
+ * .defineVars({
249
+ * color: { brand: { value: '#1167f4' } },
250
+ * spacing: { sm: { value: '4px' } },
251
+ * })
252
+ * .defineScopes({
253
+ * color: ['textColor', 'background', 'borderColor', 'ringColor'],
254
+ * spacing: ['padding', 'gap', 'margin'],
255
+ * })
256
+ * ```
257
+ */
258
+ defineScopes(scopes: [keyof TVars] extends [never] ? Record<string, PropertyGroupId[]> : { [K in keyof TVars]?: PropertyGroupId[] }): ConfigResult<TModifier, MergeTokens<TTokens, VarsToTokens<TVars>>, TMotion, TExt, TMacros, TModeModifiers, TVars>;
209
259
  }
210
260
  type AnyUdsConfig = UdsConfig<ModifierNameShape, any, any, any, any, any, any>;
211
261
  /** Extract the raw config data from a builder instance */
@@ -4,19 +4,29 @@ import "../../utils/dist/index.js";
4
4
  import { getConfigurablePropMapping } from "../../core/dist/propMappings.js";
5
5
  import "../../core/dist/index.js";
6
6
  import { buildMotionReference, resolveComponentMotionAliases, validateComponentVariants } from "./component-resolution.js";
7
+ import { expandPropertyGroups } from "./propertyGroups.js";
8
+ import { resolveTokenType, sniffTokenTypeFromValue } from "./resolveTokenTypes.js";
7
9
  import { applyPresetToData, deepMerge, mergeAtomic } from "./preset-merge.js";
8
10
  //#region ../config/dist/createConfig.js
9
11
  /** biome-ignore-all lint/suspicious/noExplicitAny: necessary for dynamic builder to work correctly */
10
12
  /** Build a structured token reference object from atomic tokens */
11
13
  function buildTokenReference(atomic, configPrefix) {
12
14
  const result = Object.create(null);
13
- for (const token of atomic) for (const prop of token.properties) {
14
- if (!result[prop]) result[prop] = Object.create(null);
15
+ for (const token of atomic) {
16
+ const varPrefix = token.cssPrefix ?? getConfigurablePropMapping(token.properties[0])?.defaultVarPrefix;
17
+ const tokenEntries = Object.create(null);
15
18
  for (const t of token.tokens) {
16
- const varPrefix = token.cssPrefix ?? getConfigurablePropMapping(prop)?.defaultVarPrefix;
17
19
  const safeName = safeTokenName(t.name);
18
20
  const cssVarName = configPrefix ? `--${configPrefix}-${varPrefix}-${safeName}` : `--${varPrefix}-${safeName}`;
19
- result[prop][t.name] = `var(${cssVarName})`;
21
+ tokenEntries[t.name] = `var(${cssVarName})`;
22
+ }
23
+ for (const prop of token.properties) {
24
+ if (!result[prop]) result[prop] = Object.create(null);
25
+ Object.assign(result[prop], tokenEntries);
26
+ }
27
+ if (varPrefix) {
28
+ if (!result[varPrefix]) result[varPrefix] = Object.create(null);
29
+ Object.assign(result[varPrefix], tokenEntries);
20
30
  }
21
31
  }
22
32
  return result;
@@ -31,84 +41,142 @@ function buildMacroReference(macros) {
31
41
  return result;
32
42
  }
33
43
  /**
34
- * Parse a `'{namespace}'` reference string, returning the namespace name
35
- * or `undefined` if the string is not a valid reference.
44
+ * Convert a VarGroupDef into AtomicToken tokens, extracting modifier overrides.
45
+ *
46
+ * Also extracts `$type` metadata:
47
+ * - A top-level `$type` on the group → `groupType`
48
+ * - A `$type` on an individual token → `type` on that token (overrides group)
49
+ *
50
+ * `$type` is filtered out of the emitted tokens; it is metadata only.
36
51
  */
37
- function parseVarsReference(ref) {
38
- return ref.match(/^\{(.+)\}$/)?.[1];
39
- }
40
- /** Normalize values to an array of string refs, filtering out non-string forms. */
41
- function normalizeValuesRefs(values) {
42
- if (!values) return [];
43
- if (typeof values === "string") return [values];
44
- if (Array.isArray(values)) return values;
45
- return [];
46
- }
47
- /** Convert a VarGroupDef into AtomicToken tokens, extracting modifier overrides. */
48
52
  function varGroupToTokens(varGroup, knownModifiers) {
53
+ let groupType;
49
54
  const tokens = [];
50
- for (const [tokenName, tokenDef] of Object.entries(varGroup)) {
55
+ for (const [tokenName, rawDef] of Object.entries(varGroup)) {
56
+ if (tokenName === "$type") {
57
+ if (typeof rawDef === "string") groupType = rawDef;
58
+ continue;
59
+ }
60
+ if (rawDef == null || typeof rawDef !== "object") continue;
61
+ const tokenDef = rawDef;
51
62
  const modifiers = {};
52
63
  for (const [key, val] of Object.entries(tokenDef)) {
53
- if (key === "value" || val === void 0) continue;
64
+ if (key === "value" || key === "$type" || val === void 0) continue;
54
65
  if (key.startsWith("_") && knownModifiers.has(key)) modifiers[key] = val;
55
66
  }
56
67
  tokens.push({
57
68
  name: tokenName,
58
69
  value: tokenDef.value,
70
+ ...tokenDef.$type ? { type: tokenDef.$type } : {},
59
71
  ...Object.keys(modifiers).length > 0 ? { modifiers } : {}
60
72
  });
61
73
  }
62
- return tokens;
74
+ return {
75
+ tokens,
76
+ groupType
77
+ };
63
78
  }
64
79
  /**
65
- * Bridge: synthesize AtomicToken[] from defineVars + defineUtilities data.
66
- *
67
- * For each utility, resolves its `values` references (single or array) to var
68
- * namespaces. Each (namespace, property) pair produces a separate AtomicToken
69
- * group with `cssPrefix` = namespace name, so CSS custom properties land in
70
- * the correct `--prefix-namespace-token` space.
80
+ * Sort properties so the one matching the cssPrefix comes last.
81
+ * `buildVarToTokenMap` uses "last wins", so this ensures e.g. `color.brand`
82
+ * beats `shadowColor.brand` when both reference `var(--uds-color-brand)`.
83
+ */
84
+ function sortPropertiesByPrefixMatch(properties, namespace, cssPfx) {
85
+ return properties.slice().sort((a, b) => {
86
+ return (a === namespace || kebabCase(a) === cssPfx ? 1 : 0) - (b === namespace || kebabCase(b) === cssPfx ? 1 : 0);
87
+ });
88
+ }
89
+ /**
90
+ * Synthesize AtomicToken[] from defineScopes + defineVars data.
71
91
  *
72
- * When `values` is an array (`['{color}', '{bg}']`), tokens from all referenced
73
- * namespaces are included. Name collisions are resolved by the existing
74
- * `mergeAtomic` logic (last group wins within the same property+cssPrefix key).
92
+ * For each namespace in `scopes`, expands property group IDs into
93
+ * ConfigurableProps, extracts tokens from the matching var namespace,
94
+ * and produces an AtomicToken group.
75
95
  */
76
- function synthesizeAtomicFromVarsAndUtilities(vars, utilities, modes) {
96
+ function synthesizeAtomicFromScopesAndVars(vars, scopes, modes) {
77
97
  const knownModifiers = /* @__PURE__ */ new Set();
78
98
  for (const mode of modes) for (const option of mode.options) knownModifiers.add(option.modifier);
79
- const pairs = /* @__PURE__ */ new Map();
80
- for (const [propName, utilDef] of Object.entries(utilities)) {
81
- if (!utilDef) continue;
82
- const refs = normalizeValuesRefs(utilDef.values);
83
- for (const ref of refs) {
84
- const namespace = parseVarsReference(ref);
85
- if (!namespace || !vars[namespace]) continue;
86
- const key = `${namespace}|${propName}`;
87
- pairs.set(key, {
88
- namespace,
89
- property: propName
90
- });
91
- }
92
- }
93
- const namespaceGroups = /* @__PURE__ */ new Map();
94
- for (const { namespace, property } of pairs.values()) {
95
- const props = namespaceGroups.get(namespace) ?? /* @__PURE__ */ new Set();
96
- props.add(property);
97
- namespaceGroups.set(namespace, props);
98
- }
99
99
  const result = [];
100
- for (const [namespace, propertySet] of namespaceGroups) {
100
+ for (const [namespace, groupIds] of Object.entries(scopes)) {
101
101
  const varGroup = vars[namespace];
102
102
  if (!varGroup) continue;
103
- const tokens = varGroupToTokens(varGroup, knownModifiers);
104
- const cssPfx = (propertySet.has(namespace) ? getConfigurablePropMapping(namespace) : void 0)?.defaultVarPrefix ?? kebabCase(namespace);
105
- const properties = Array.from(propertySet).sort((a, b) => {
106
- return (a === namespace || kebabCase(a) === cssPfx ? 1 : 0) - (b === namespace || kebabCase(b) === cssPfx ? 1 : 0);
103
+ const properties = expandPropertyGroups(groupIds);
104
+ if (properties.length === 0) continue;
105
+ const { tokens, groupType } = varGroupToTokens(varGroup, knownModifiers);
106
+ const cssPfx = (properties.includes(namespace) ? getConfigurablePropMapping(namespace) : void 0)?.defaultVarPrefix ?? kebabCase(namespace);
107
+ const sorted = sortPropertiesByPrefixMatch(properties, namespace, cssPfx);
108
+ const resolvedGroupType = resolveTokenType({
109
+ explicit: groupType,
110
+ scopes: groupIds
107
111
  });
112
+ if (resolvedGroupType && resolvedGroupType !== "string") for (const token of tokens) {
113
+ if (token.type) continue;
114
+ if (sniffTokenTypeFromValue(token.value) === "string") token.type = "string";
115
+ }
108
116
  result.push({
109
- properties,
117
+ properties: sorted,
110
118
  tokens,
111
- cssPrefix: cssPfx
119
+ cssPrefix: cssPfx,
120
+ ...resolvedGroupType ? { type: resolvedGroupType } : {}
121
+ });
122
+ }
123
+ return disambiguateOverlappingProperties(result, scopes);
124
+ }
125
+ /**
126
+ * When multiple scope namespaces reference the **same** property group
127
+ * (e.g. `rotate`, `scale`, `skew`, `translate` all mapping to `['transform']`),
128
+ * each namespace's AtomicToken gets ALL properties from that group, causing
129
+ * last-write-wins collisions in CSS generation.
130
+ *
131
+ * This function partitions those shared-group properties among namespaces
132
+ * using longest camelCase prefix matching. Cross-group overlaps (e.g.
133
+ * `color` and `bg` both including the `bg` property via different groups)
134
+ * are intentional cascading and left untouched.
135
+ */
136
+ function disambiguateOverlappingProperties(tokens, scopes) {
137
+ const groupToNamespaces = /* @__PURE__ */ new Map();
138
+ for (const [ns, groupIds] of Object.entries(scopes)) for (const gid of groupIds) {
139
+ const list = groupToNamespaces.get(gid) ?? [];
140
+ list.push(ns);
141
+ groupToNamespaces.set(gid, list);
142
+ }
143
+ const contestedGroups = /* @__PURE__ */ new Map();
144
+ for (const [gid, nsList] of groupToNamespaces) if (nsList.length > 1) contestedGroups.set(gid, nsList);
145
+ if (contestedGroups.size === 0) return tokens;
146
+ const propOwner = /* @__PURE__ */ new Map();
147
+ for (const [gid, nsList] of contestedGroups) {
148
+ const groupProps = expandPropertyGroups([gid]);
149
+ const assignments = /* @__PURE__ */ new Map();
150
+ const nsWithMatch = /* @__PURE__ */ new Set();
151
+ for (const prop of groupProps) {
152
+ let bestNs = "";
153
+ let bestLen = 0;
154
+ for (const ns of nsList) if (prop === ns || prop.startsWith(ns) && prop.length > ns.length) {
155
+ if (ns.length > bestLen) {
156
+ bestNs = ns;
157
+ bestLen = ns.length;
158
+ }
159
+ }
160
+ if (bestNs) {
161
+ assignments.set(prop, bestNs);
162
+ nsWithMatch.add(bestNs);
163
+ }
164
+ }
165
+ if (nsWithMatch.size < 2) continue;
166
+ for (const [prop, ns] of assignments) propOwner.set(prop, ns);
167
+ }
168
+ if (propOwner.size === 0) return tokens;
169
+ const namespaces = Object.keys(scopes);
170
+ const result = [];
171
+ for (const token of tokens) {
172
+ const ns = namespaces.find((n) => token.cssPrefix === kebabCase(n)) ?? "";
173
+ const filtered = token.properties.filter((prop) => {
174
+ const owner = propOwner.get(prop);
175
+ return !owner || owner === ns;
176
+ });
177
+ if (filtered.length > 0) result.push({
178
+ ...token,
179
+ properties: filtered
112
180
  });
113
181
  }
114
182
  return result;
@@ -133,13 +201,15 @@ function resolveConfig(config) {
133
201
  examples: config.examples,
134
202
  designPrinciples: config.designPrinciples,
135
203
  vars: config.vars,
136
- utilities: config.utilities
204
+ classNames: config.classNames,
205
+ scopes: config.scopes
137
206
  };
138
- if (configData.vars && configData.utilities) {
139
- const synthesized = synthesizeAtomicFromVarsAndUtilities(configData.vars, configData.utilities, configData.modes);
207
+ if (configData.vars && configData.scopes) {
208
+ const synthesized = synthesizeAtomicFromScopesAndVars(configData.vars, configData.scopes, configData.modes);
209
+ const atomic = mergeAtomic(configData.atomic, synthesized, "merge");
140
210
  configData = {
141
211
  ...configData,
142
- atomic: mergeAtomic(configData.atomic, synthesized, "merge")
212
+ atomic
143
213
  };
144
214
  }
145
215
  return configData;
@@ -192,7 +262,8 @@ function resolvePresetData(input) {
192
262
  examples: input.examples,
193
263
  designPrinciples: input.designPrinciples,
194
264
  vars: input.vars,
195
- utilities: input.utilities
265
+ classNames: input.classNames,
266
+ scopes: input.scopes
196
267
  };
197
268
  }
198
269
  function extractVarReferences(value) {
@@ -230,7 +301,10 @@ function collectRequiredTokens(sourceData, components, motion) {
230
301
  }
231
302
  function availableTokenNames(data) {
232
303
  const names = /* @__PURE__ */ new Set();
233
- for (const group of data.atomic) for (const prop of group.properties) for (const token of group.tokens) names.add(`${prop}.${token.name}`);
304
+ for (const group of data.atomic) for (const token of group.tokens) {
305
+ for (const prop of group.properties) names.add(`${prop}.${token.name}`);
306
+ if (group.cssPrefix) names.add(`${group.cssPrefix}.${token.name}`);
307
+ }
234
308
  return names;
235
309
  }
236
310
  function validatePresetRequiredTokens(base, preset) {
@@ -434,8 +508,7 @@ function createConfigBuilder(data, extensions) {
434
508
  arbitraryTokens: input.arbitraryTokens ?? data.arbitraryTokens,
435
509
  examples: input.examples ?? data.examples,
436
510
  designPrinciples: input.designPrinciples ?? data.designPrinciples,
437
- vars: input.vars ?? data.vars,
438
- utilities: input.utilities ?? data.utilities
511
+ vars: input.vars ?? data.vars
439
512
  }, extensions);
440
513
  },
441
514
  extend(ext) {
@@ -515,8 +588,8 @@ function createConfigBuilder(data, extensions) {
515
588
  ...group
516
589
  };
517
590
  const updates = { vars: merged };
518
- if (data.utilities && Object.keys(data.utilities).length > 0) {
519
- const synthesized = synthesizeAtomicFromVarsAndUtilities(merged, data.utilities, data.modes ?? []);
591
+ if (data.scopes && Object.keys(data.scopes).length > 0) {
592
+ const synthesized = synthesizeAtomicFromScopesAndVars(merged, data.scopes, data.modes ?? []);
520
593
  updates.atomic = mergeAtomic(data.atomic, synthesized, "merge");
521
594
  }
522
595
  return createConfigBuilder({
@@ -524,14 +597,20 @@ function createConfigBuilder(data, extensions) {
524
597
  ...updates
525
598
  }, extensions);
526
599
  },
527
- defineUtilities(utilities) {
528
- const merged = {
529
- ...data.utilities,
530
- ...utilities
600
+ defineClassNames(classNames) {
601
+ return next({ classNames: {
602
+ ...data.classNames,
603
+ ...classNames
604
+ } });
605
+ },
606
+ defineScopes(scopes) {
607
+ const mergedScopes = {
608
+ ...data.scopes,
609
+ ...scopes
531
610
  };
532
- const updates = { utilities: merged };
611
+ const updates = { scopes: mergedScopes };
533
612
  if (data.vars && Object.keys(data.vars).length > 0) {
534
- const synthesized = synthesizeAtomicFromVarsAndUtilities(data.vars, merged, data.modes ?? []);
613
+ const synthesized = synthesizeAtomicFromScopesAndVars(data.vars, mergedScopes, data.modes ?? []);
535
614
  updates.atomic = mergeAtomic(data.atomic ?? [], synthesized, "merge");
536
615
  }
537
616
  return createConfigBuilder({
@@ -1,7 +1,7 @@
1
1
  import { defaultColors } from "./consts/defaultColors.js";
2
- import { UtilitiesConfig, UtilityDef, VarGroupDef, VarTokenDef, VarsConfig } from "./types.js";
2
+ import { PropertyGroupId } from "./propertyGroups.js";
3
+ import { TokenType, VarGroupDef, VarTokenDef, VarsConfig } from "./types.js";
3
4
  import { ArbitraryTokenGroup, AtomicToken, BuildOptions, ComponentConfig, DefineComponentInput, DefineComponentMotionInput, ExampleDef, ExampleEntryDef, ExampleLayoutStyles, GlobalStylesDef, InterpolateMarker, ModeGroup, ModifierDef, MotionPresetsDef, UdsConfig, UdsConfigData, buildTokenReference, createConfigBuilder, darker, lighter, resolveConfig } from "./createConfig.js";
4
- import { ResolvedUtilityMapping, resolveUtilityMappings } from "./resolvedMappings.js";
5
5
  import { SerializedConfig, TokenRef, buildReverseMap, deserializeConfig, serializeConfig } from "./serialize.js";
6
6
  import { ComponentsConfig as ComponentsConfig$1 } from "@uds/types";
7
7
  export { type ComponentsConfig$1 as ComponentsConfig };
@@ -1,5 +1,6 @@
1
1
  import "./consts/defaultColors.js";
2
2
  import "./component-resolution.js";
3
+ import "./propertyGroups.js";
4
+ import "./resolveTokenTypes.js";
3
5
  import "./createConfig.js";
4
- import "./resolvedMappings.js";
5
6
  import "./serialize.js";
@@ -14,6 +14,7 @@ function flattenAtomic(atomic) {
14
14
  for (const group of atomic) for (const property of group.properties) for (const token of group.tokens) entries.push({
15
15
  property,
16
16
  cssPrefix: group.cssPrefix,
17
+ groupType: group.type,
17
18
  token
18
19
  });
19
20
  return entries;
@@ -39,11 +40,13 @@ function mergeAtomic(base, incoming, mode) {
39
40
  const existing = regrouped.get(groupKey);
40
41
  if (existing) {
41
42
  existing.tokens.push(entry.token);
43
+ if (entry.groupType) existing.type = entry.groupType;
42
44
  continue;
43
45
  }
44
46
  regrouped.set(groupKey, {
45
47
  properties: [entry.property],
46
48
  ...entry.cssPrefix ? { cssPrefix: entry.cssPrefix } : {},
49
+ ...entry.groupType ? { type: entry.groupType } : {},
47
50
  tokens: [entry.token]
48
51
  });
49
52
  }
@@ -142,9 +145,13 @@ function applyPresetToData(base, preset, strategy) {
142
145
  const mergedPrinciples = base.designPrinciples || preset.designPrinciples ? [...base.designPrinciples ?? [], ...preset.designPrinciples ?? []] : void 0;
143
146
  const mergedArbitraryTokens = base.arbitraryTokens || preset.arbitraryTokens ? [...base.arbitraryTokens ?? [], ...preset.arbitraryTokens ?? []] : void 0;
144
147
  const mergedVars = base.vars || preset.vars ? mergeRecordByKey(base.vars ?? {}, preset.vars ?? {}, "merge") : void 0;
145
- const mergedUtilities = base.utilities || preset.utilities ? {
146
- ...base.utilities,
147
- ...preset.utilities
148
+ const mergedClassNames = base.classNames || preset.classNames ? {
149
+ ...base.classNames,
150
+ ...preset.classNames
151
+ } : void 0;
152
+ const mergedScopes = base.scopes || preset.scopes ? {
153
+ ...base.scopes,
154
+ ...preset.scopes
148
155
  } : void 0;
149
156
  const merged = {
150
157
  ...base,
@@ -163,7 +170,8 @@ function applyPresetToData(base, preset, strategy) {
163
170
  examples: mergedExamples,
164
171
  designPrinciples: mergedPrinciples,
165
172
  vars: mergedVars,
166
- utilities: mergedUtilities
173
+ classNames: mergedClassNames,
174
+ scopes: mergedScopes
167
175
  };
168
176
  validateComponentVariants(merged.components);
169
177
  return merged;
@@ -0,0 +1,11 @@
1
+ import { ConfigurableProp } from "@uds/types";
2
+
3
+ //#region ../config/dist/propertyGroups.d.ts
4
+ //#region src/propertyGroups.d.ts
5
+ type PropertyGroupId = 'textColor' | 'background' | 'borderColor' | 'outlineColor' | 'ringColor' | 'divideColor' | 'shadowColor' | 'svgFill' | 'svgStroke' | 'caretColor' | 'textDecorationColor' | 'opacity' | 'colorOpacity' | 'bgOpacity' | 'borderColorOpacity' | 'divideColorOpacity' | 'outlineColorOpacity' | 'ringColorOpacity' | 'textDecorationColorOpacity' | 'svgFillOpacity' | 'svgStrokeOpacity' | 'shadowColorOpacity' | 'caretColorOpacity' | 'fontFamily' | 'fontSize' | 'fontWeight' | 'lineHeight' | 'letterSpacing' | 'blur' | 'backdropBlur' | 'animation' | 'shadow' | 'textShadow' | 'zIndex' | 'borderWidth' | 'borderRadius' | 'outlineWidth' | 'divideWidth' | 'ringWidth' | 'padding' | 'margin' | 'offset' | 'gap' | 'indent' | 'scrollSnapGap' | 'scrollSnapSpacing' | 'tableBorderSpacing' | 'width' | 'height' | 'aspectRatio' | 'positionPlacement' | 'flex' | 'transform' | 'strokeWidth';
6
+ /**
7
+ * A logical grouping of ConfigurableProps with a human-readable label.
8
+ * Used by the Studio UI to let users scope token groups to specific properties.
9
+ */
10
+ //#endregion
11
+ export { PropertyGroupId };