@yahoo/uds-create-config 4.53.2 → 4.55.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 (64) hide show
  1. package/dist/capture/capture-to-spec.js +22 -6
  2. package/dist/capture/class-grammar.js +2 -3
  3. package/dist/configs/platform.d.ts +8 -1
  4. package/dist/configs/react-native-system.d.ts +21 -9
  5. package/dist/configs/react-native-system.js +1 -1
  6. package/dist/configs/system.js +3 -1
  7. package/dist/css/value-type.js +24 -0
  8. package/dist/entities/documentation/Documentation.d.ts +1 -1
  9. package/dist/entities/native/NativeStyleProperty.d.ts +14 -9
  10. package/dist/entities/native/NativeStyleProperty.js +23 -2
  11. package/dist/entities/native/NativeToken.d.ts +10 -3
  12. package/dist/entities/native/NativeToken.js +39 -4
  13. package/dist/entities/system/Component.js +2 -1
  14. package/dist/entities/system/GlobalStyle.d.ts +2 -2
  15. package/dist/entities/system/GlobalStyle.js +2 -1
  16. package/dist/entities/system/Modifier.d.ts +3 -1
  17. package/dist/entities/system/Modifier.js +3 -1
  18. package/dist/entities/system/Settings.d.ts +9 -1
  19. package/dist/entities/system/Settings.js +6 -2
  20. package/dist/entities/system/StyleProperty.d.ts +39 -3
  21. package/dist/entities/system/StyleProperty.js +141 -18
  22. package/dist/entities/system/Token.d.ts +14 -1
  23. package/dist/entities/system/Token.js +21 -3
  24. package/dist/entities/system/calc.d.ts +28 -0
  25. package/dist/entities/system/calc.js +121 -0
  26. package/dist/entities/system/color.d.ts +29 -4
  27. package/dist/entities/system/color.js +61 -8
  28. package/dist/entities/system/component-module.js +15 -4
  29. package/dist/entities/system/composed-values.d.ts +3 -2
  30. package/dist/entities/system/composed-values.js +20 -1
  31. package/dist/entities/system/defineComponent.d.ts +3 -3
  32. package/dist/entities/system/link-plan.js +10 -1
  33. package/dist/entities/system/linked-token-values.d.ts +34 -0
  34. package/dist/entities/system/linked-token-values.js +135 -0
  35. package/dist/entities/system/shadow.d.ts +9 -7
  36. package/dist/entities/system/shadow.js +8 -7
  37. package/dist/entities/system/style-bag.d.ts +1 -1
  38. package/dist/entities/system/token-index.js +45 -2
  39. package/dist/entities/system/token-values.d.ts +3 -2
  40. package/dist/entities/system/token-values.js +9 -9
  41. package/dist/framework/Config.d.ts +14 -18
  42. package/dist/framework/Config.js +105 -55
  43. package/dist/framework/class-names.d.ts +47 -7
  44. package/dist/framework/class-names.js +115 -14
  45. package/dist/framework/defineEntity.d.ts +3 -1
  46. package/dist/framework/defineEntity.js +3 -1
  47. package/dist/framework/inline-sources.d.ts +3 -3
  48. package/dist/framework/inline-sources.js +1 -1
  49. package/dist/framework/projections.d.ts +84 -3
  50. package/dist/framework/projections.js +315 -20
  51. package/dist/framework/ref-graph.js +1 -1
  52. package/dist/framework/registry.d.ts +7 -0
  53. package/dist/framework/registry.js +2 -1
  54. package/dist/framework/schema-version.d.ts +1 -1
  55. package/dist/framework/schema-version.js +5 -5
  56. package/dist/framework/utils/group.d.ts +2 -1
  57. package/dist/framework/utils/refs.d.ts +19 -2
  58. package/dist/framework/utils/refs.js +100 -12
  59. package/dist/index.d.ts +12 -10
  60. package/dist/index.js +10 -8
  61. package/dist/migrations/20261001162046_oklch_and_emit_fields.d.ts +25 -0
  62. package/dist/migrations/20261001162046_oklch_and_emit_fields.js +10 -0
  63. package/dist/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +2 -2
@@ -53,8 +53,26 @@ declare function heldLinked({
53
53
  * The group segment is kebab-cased and the leaf is not, which is not a stylistic choice: it's the
54
54
  * shape the emitted stylesheet declares (`--{prefix}-{kebabCase(namespace)}-{safeTokenName(name)}`).
55
55
  * Deriving it any other way produces a `var()` that resolves to nothing for any camelCase group.
56
+ *
57
+ * Under the config's own naming, a token's own `cssVariable` is its whole name (`--spacing`), or
58
+ * the exact name when it starts with `--` (`--mantine-color-primary`, whatever the prefix), and
59
+ * otherwise the nearest token group that declares a `cssPrefix` spells its part of the name:
60
+ * `fontSize/sm` under `fontSize: { cssPrefix: 'text' }` is `--text-sm`, and an empty `cssPrefix`
61
+ * drops the group's segment. An empty config prefix drops its own.
56
62
  */
57
63
  declare function cssVar(config: Config, _kind: string, path: string, varPrefix?: VarNaming): string;
64
+ /**
65
+ * Whether a token is declared as a custom property: its own `cssVariable`, else the nearest group's
66
+ * `cssVariables`, else yes. One that isn't is written into each value that uses it, as Tailwind's
67
+ * `@theme inline` does.
68
+ */
69
+ declare function declaresCssVariable(config: Config, path: string): boolean;
70
+ /**
71
+ * How CSS reaches a token: `var(--…)` when it declares a variable, else its value written in place,
72
+ * as the stylesheet writes it. For anything that paints a token outside a style prop, a swatch, an
73
+ * effect colour, a background.
74
+ */
75
+ declare function tokenReference(config: Config, path: string): string;
58
76
  /** `var(--…)` — the CSS reference to {@link cssVar}. */
59
77
  declare function cssVarRef(config: Config, kind: string, path: string, varPrefix?: VarNaming): string;
60
78
  /** The utility/class name for an entity — `<prefix>-<path-dashed>`. A class is one identifier, so
@@ -238,6 +256,13 @@ declare function resolveFieldValue(config: Config, kind: string, path: string, f
238
256
  /** A token's value with alias refs chased to the base literal — {@link resolveFieldValue} for the one
239
257
  * field every token consumer asks about, narrowed to the scalar a display or a stylesheet can use. */
240
258
  declare function resolveTokenValue(config: Config, path: string, seen?: Set<string>): string | number | undefined;
259
+ /**
260
+ * A stored value as the literal it comes to: a string or a number as itself, an OKLCH colour as its
261
+ * CSS, arithmetic evaluated over its operands' literals (`multiply(Token.ref('spacing'), 4)` with
262
+ * `spacing` at `0.25rem` is `1rem`), and a token ref as that token's literal. `undefined` for a value
263
+ * that is no single literal (a shadow, a gradient) or a ref that resolves to nothing.
264
+ */
265
+ declare function literalValueOf(config: Config, value: unknown): string | number | undefined;
241
266
  /**
242
267
  * The literal a token resolves to under an override condition: its own override there when it holds
243
268
  * one, else its base value, with a ref along the way chased under the same condition. That is how the
@@ -245,6 +270,20 @@ declare function resolveTokenValue(config: Config, path: string, seen?: Set<stri
245
270
  * for a dark cell paints what dark will show. `null` is the base, the same as {@link resolveTokenValue}.
246
271
  */
247
272
  declare function resolveTokenValueUnder(config: Config, path: string, condition: string | null, seen?: Set<string>): string | number | undefined;
273
+ /**
274
+ * A string's interpolated refs written as their tokens: `blur({token:blur/sm})` is
275
+ * `blur(var(--blur-sm))`. A ref opening a `var(` is the variable's name, so a fallback can follow it:
276
+ * `var({token:tw/leading}, 1.5)` is `var(--tw-leading, 1.5)`.
277
+ */
278
+ declare function renderInterpolated({
279
+ config,
280
+ text,
281
+ varPrefix
282
+ }: {
283
+ readonly config: Config;
284
+ readonly text: string;
285
+ readonly varPrefix?: VarNaming;
286
+ }): string;
248
287
  /** A style-bag value → its CSS string: a token ref (or a bare token path) becomes a
249
288
  * `var(--…)` reference (so mode/theme overrides still flow), anything else is the literal.
250
289
  *
@@ -254,7 +293,9 @@ declare function resolveTokenValueUnder(config: Config, path: string, condition:
254
293
  * ordinary call — it is `config`'s own prefix. */
255
294
  declare function renderStyleValue(config: Config, value: unknown, varPrefix?: VarNaming): string;
256
295
  /**
257
- * The value a negating prop paints — `calc(<value> * -1)`.
296
+ * The value a negating prop paints: a length or a percentage with a minus sign (`-1px`, `-100%`, and
297
+ * a zero as it is), a scale step's multiplier negated (`calc(var(--spacing) * -4)`), and anything
298
+ * else, a `var()` included, as `calc(<value> * -1)` — the spellings Tailwind writes.
258
299
  *
259
300
  * `calc` rather than a computed literal because the value is usually a `var(--…)`: the token it names
260
301
  * has to stay a reference, so a mode override or a theme swap still reaches it. It's applied at the
@@ -371,10 +412,50 @@ declare function routedBag({
371
412
  readonly through?: readonly (string | undefined)[];
372
413
  readonly bag: Record<string, unknown>;
373
414
  }): Record<string, unknown>;
415
+ /** A declaration's key as CSS: a property as itself, a token's key `{token:tw/blur}` as the variable
416
+ * the token declares. */
417
+ declare function declarationKeyName({
418
+ config,
419
+ key,
420
+ namespace
421
+ }: {
422
+ readonly config: Config;
423
+ readonly key: string;
424
+ readonly namespace?: VarNaming;
425
+ }): string;
426
+ /**
427
+ * One block of a bag's declarations: those on the element itself, or those a prop writes under its
428
+ * `selector` (`&` standing for the element) or a value writes under an at-rule.
429
+ */
430
+ interface StyleDeclarationGroup {
431
+ /** The prop's `selector`, with `&` for the element. Absent for the element's own declarations. */
432
+ readonly selector?: string;
433
+ /** The at-rule prelude the block sits in (`@media (width >= 40rem)`). */
434
+ readonly atRule?: string;
435
+ readonly declarations: Record<string, string>;
436
+ }
437
+ /**
438
+ * The declarations a bag sets on the element itself. Declarations a prop scopes to a `selector` or a
439
+ * value scopes to an at-rule are not the element's, so they are left out; a stylesheet reads them
440
+ * from {@link styleDeclarationGroups}.
441
+ */
374
442
  declare function styleDeclarations({
375
443
  config,
376
444
  bag,
377
445
  namespace
446
+ }: {
447
+ readonly config: Config;
448
+ readonly bag: Record<string, unknown>; /** See {@link styleDeclarationGroups}. */
449
+ readonly namespace?: VarNaming;
450
+ }): Record<string, string>;
451
+ /**
452
+ * A bag's declarations in the blocks they apply in: the element's own first, then each `selector`
453
+ * and at-rule block in the order the bag first reaches it. A block that sets nothing is left out.
454
+ */
455
+ declare function styleDeclarationGroups({
456
+ config,
457
+ bag,
458
+ namespace
378
459
  }: {
379
460
  readonly config: Config;
380
461
  readonly bag: Record<string, unknown>;
@@ -387,7 +468,7 @@ declare function styleDeclarations({
387
468
  * nothing on the page declares, which computes to the inherited value and paints silently wrong.
388
469
  */
389
470
  readonly namespace?: VarNaming;
390
- }): Record<string, string>;
471
+ }): StyleDeclarationGroup[];
391
472
  /**
392
473
  * An opacity value as a `color-mix()` share.
393
474
  *
@@ -631,4 +712,4 @@ declare function propValueFromAxis(config: Config, path: string, prop: string, v
631
712
  /** The `value` a string, number or boolean prop declares as its default, per prop. */
632
713
  declare function declaredPropDefaults(props: Record<string, unknown> | undefined): Record<string, unknown>;
633
714
  //#endregion
634
- export { IconTool, IndexedPreviewCondition, PreviewMatrix, PropDeclaration, StylePropEntry, StylePropertyItem, VarNaming, authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, glyphComponentOf, glyphLibrariesOf, glyphPropsOf, heldLinked, holdingPreviewConditions, iconToolAxisOptions, iconToolFor, iconToolOf, iconToolsOf, keyedPreviewConditions, linkedVarNaming, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, refListOf, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, validateComponentProps };
715
+ export { IconTool, IndexedPreviewCondition, PreviewMatrix, PropDeclaration, StyleDeclarationGroup, StylePropEntry, StylePropertyItem, VarNaming, authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declarationKeyName, declaredPropDefaults, declaredPropRouting, declaresCssVariable, glyphComponentOf, glyphLibrariesOf, glyphPropsOf, heldLinked, holdingPreviewConditions, iconToolAxisOptions, iconToolFor, iconToolOf, iconToolsOf, keyedPreviewConditions, linkedVarNaming, literalValueOf, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, refListOf, renderInterpolated, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarationGroups, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, tokenReference, validateComponentProps };
@@ -1,4 +1,6 @@
1
- import { isRef, kindOf, memberOf, pathOf, ref, refLeaf, sourceOf, splitRef } from "./utils/refs.js";
1
+ import { declarationText, hasRefMarker, isRef, kindOf, memberOf, pathOf, ref, refLeaf, refOfKey, replaceRefMarkers, sourceOf, splitRef } from "./utils/refs.js";
2
+ import { evaluateCalc, isDivide, isMultiply, leadingNumber } from "../entities/system/calc.js";
3
+ import { isOklchColor, renderOklch } from "../entities/system/color.js";
2
4
  import { isComposedValue, renderComposedValue } from "../entities/system/composed-values.js";
3
5
  import { modifierCategory } from "../entities/system/modifier-axis.js";
4
6
  import { forwardedPropName, forwardsOf, rootLayerOf } from "../entities/system/element.js";
@@ -6,7 +8,7 @@ import { fontFamilyStack } from "../entities/system/Font.js";
6
8
  import { listedIn } from "./utils/enumerated.js";
7
9
  import "../entities/system/Modifier.js";
8
10
  import { createSliceMemo } from "./memo.js";
9
- import { classPrefixOf, varPrefixOf } from "../entities/system/Settings.js";
11
+ import { classPrefixOf, prefixed, varPrefixOf } from "../entities/system/Settings.js";
10
12
  import { StyleProperty, leavesOfStylePropValue } from "../entities/system/StyleProperty.js";
11
13
  import { isConditionalPreview, storedPreview, unconditionalPreviewProps } from "./preview-store.js";
12
14
  //#region src/framework/projections.ts
@@ -53,7 +55,10 @@ function domainCache(cache, config) {
53
55
  * construction, and already the only name resolution uses. So a borrowed variable lives in the
54
56
  * consumer's namespace, qualified by the pin it came through.
55
57
  */
56
- const sourceVarPrefix = (config, slug) => `${varPrefixOf(config)}-${kebab(slug)}`;
58
+ const sourceVarPrefix = (config, slug) => prefixed({
59
+ prefix: varPrefixOf(config),
60
+ name: kebab(slug)
61
+ });
57
62
  /**
58
63
  * The name each of a linked system's tokens has in `config`'s stylesheet.
59
64
  *
@@ -124,13 +129,114 @@ function kebab(segment) {
124
129
  * The group segment is kebab-cased and the leaf is not, which is not a stylistic choice: it's the
125
130
  * shape the emitted stylesheet declares (`--{prefix}-{kebabCase(namespace)}-{safeTokenName(name)}`).
126
131
  * Deriving it any other way produces a `var()` that resolves to nothing for any camelCase group.
132
+ *
133
+ * Under the config's own naming, a token's own `cssVariable` is its whole name (`--spacing`), or
134
+ * the exact name when it starts with `--` (`--mantine-color-primary`, whatever the prefix), and
135
+ * otherwise the nearest token group that declares a `cssPrefix` spells its part of the name:
136
+ * `fontSize/sm` under `fontSize: { cssPrefix: 'text' }` is `--text-sm`, and an empty `cssPrefix`
137
+ * drops the group's segment. An empty config prefix drops its own.
127
138
  */
128
139
  function cssVar(config, _kind, path, varPrefix) {
129
140
  if (typeof varPrefix === "function") return varPrefix(path);
130
- const slash = path.indexOf("/");
131
141
  const prefix = varPrefix ?? varPrefixOf(config);
132
- if (slash <= 0 || slash >= path.length - 1) return `--${prefix}-${kebab(path)}`;
133
- return `--${prefix}-${kebab(path.slice(0, slash))}-${path.slice(slash + 1).replaceAll(".", "_").replaceAll("/", "-").replace(/\s+/g, "-")}`;
142
+ const own = varPrefix === void 0 ? ownCssVariable(config, path) : void 0;
143
+ if (typeof own === "string") return own.startsWith("--") ? own : `--${prefixed({
144
+ prefix,
145
+ name: own
146
+ })}`;
147
+ const named = varPrefix === void 0 ? groupPrefixed(config, path) : void 0;
148
+ if (named) return `--${[
149
+ prefix,
150
+ named.prefix,
151
+ leafSegment(named.rest)
152
+ ].filter(Boolean).join("-")}`;
153
+ const slash = path.indexOf("/");
154
+ if (slash <= 0 || slash >= path.length - 1) return `--${prefixed({
155
+ prefix,
156
+ name: kebab(path)
157
+ })}`;
158
+ return `--${prefixed({
159
+ prefix,
160
+ name: `${kebab(path.slice(0, slash))}-${leafSegment(path.slice(slash + 1))}`
161
+ })}`;
162
+ }
163
+ /**
164
+ * A variable name's leaf: an identifier, so a space would end the name and is written as a hyphen,
165
+ * the same spelling the class carries (`stylePropClassName`).
166
+ */
167
+ function leafSegment(leaf) {
168
+ return leaf.replaceAll(".", "_").replaceAll("/", "-").replace(/\s+/g, "-");
169
+ }
170
+ /** Token group path → its stored fields, per groups listing. */
171
+ const groupMetaCache = /* @__PURE__ */ new WeakMap();
172
+ /** Each token group's own fields by path, as the config holds them. */
173
+ function tokenGroupMeta(config) {
174
+ const groups = config.groups("token");
175
+ let meta = groupMetaCache.get(groups);
176
+ if (!meta) {
177
+ meta = new Map(groups.map((group) => [group.path, group.meta ?? {}]));
178
+ groupMetaCache.set(groups, meta);
179
+ }
180
+ return meta;
181
+ }
182
+ /** The nearest token group above `path` whose fields hold `field`, with the value and the path below
183
+ * the group. Read on the config itself, since a group's emit fields are local to the config that
184
+ * holds it. */
185
+ function nearestGroupField({ config, path, field }) {
186
+ const meta = tokenGroupMeta(config);
187
+ if (meta.size === 0) return void 0;
188
+ for (let at = path.lastIndexOf("/"); at > 0; at = path.lastIndexOf("/", at - 1)) {
189
+ const value = meta.get(path.slice(0, at))?.[field];
190
+ if (value !== void 0) return {
191
+ value,
192
+ rest: path.slice(at + 1)
193
+ };
194
+ }
195
+ }
196
+ /**
197
+ * The nearest token group above `path` that declares a `cssPrefix`, and the path below it.
198
+ * `undefined` when none does.
199
+ */
200
+ function groupPrefixed(config, path) {
201
+ if (!config) return void 0;
202
+ const found = nearestGroupField({
203
+ config,
204
+ path,
205
+ field: "cssPrefix"
206
+ });
207
+ return typeof found?.value === "string" ? {
208
+ prefix: found.value,
209
+ rest: found.rest
210
+ } : void 0;
211
+ }
212
+ /**
213
+ * Whether a token is declared as a custom property: its own `cssVariable`, else the nearest group's
214
+ * `cssVariables`, else yes. One that isn't is written into each value that uses it, as Tailwind's
215
+ * `@theme inline` does.
216
+ */
217
+ function declaresCssVariable(config, path) {
218
+ const own = ownCssVariable(config, path);
219
+ if (own !== void 0) return own !== false;
220
+ const overrides = (config.resolve("token", path)?.toJSON())?.overrides;
221
+ if (overrides && Object.keys(overrides).length > 0) return true;
222
+ return nearestGroupField({
223
+ config,
224
+ path,
225
+ field: "cssVariables"
226
+ })?.value !== false;
227
+ }
228
+ /**
229
+ * How CSS reaches a token: `var(--…)` when it declares a variable, else its value written in place,
230
+ * as the stylesheet writes it. For anything that paints a token outside a style prop, a swatch, an
231
+ * effect colour, a background.
232
+ */
233
+ function tokenReference(config, path) {
234
+ return renderStyleValue(config, ref("token", path));
235
+ }
236
+ /** A token's own `cssVariable`: the name it declares, `false` for none, `undefined` when unset. */
237
+ function ownCssVariable(config, path) {
238
+ const own = (config?.resolve("token", path)?.toJSON())?.cssVariable;
239
+ return typeof own === "string" || own === false ? own : void 0;
134
240
  }
135
241
  /** `var(--…)` — the CSS reference to {@link cssVar}. */
136
242
  function cssVarRef(config, kind, path, varPrefix) {
@@ -140,7 +246,10 @@ function cssVarRef(config, kind, path, varPrefix) {
140
246
  * whitespace in the path is written as a hyphen too; a composite or motion preset named with a space
141
247
  * otherwise declares a rule no element's class list can match. */
142
248
  function className(config, _kind, path) {
143
- return `${classPrefixOf(config)}-${path.replaceAll("/", "-").replace(/\s+/g, "-")}`;
249
+ return prefixed({
250
+ prefix: classPrefixOf(config),
251
+ name: path.replaceAll("/", "-").replace(/\s+/g, "-")
252
+ });
144
253
  }
145
254
  /**
146
255
  * A style prop's allowed values, flattened to concrete strings: literal keywords (`row`)
@@ -184,7 +293,7 @@ function memberEntries(config, target, opts) {
184
293
  }
185
294
  function expandMemberEntries(config, target, opts) {
186
295
  const { kind, path, source, member: facet } = splitRef(target);
187
- const offered = (holder, memberPath) => opts?.retired !== false || !holder.retired(kind, memberPath);
296
+ const offered = (holder, memberPath) => (opts?.retired !== false || !holder.retired(kind, memberPath)) && (holder.resolve(kind, memberPath)?.toJSON())?.utility !== false;
188
297
  /**
189
298
  * Whose members these are.
190
299
  *
@@ -219,7 +328,7 @@ function expandMemberEntries(config, target, opts) {
219
328
  }));
220
329
  }
221
330
  if (owner.resolve(kind, path)) {
222
- if (!offered(owner, path)) return [];
331
+ if (opts?.retired === false && owner.retired(kind, path)) return [];
223
332
  return [{
224
333
  leaf: path.slice(path.lastIndexOf("/") + 1),
225
334
  path: qualify(path)
@@ -533,8 +642,55 @@ function resolveFieldValue(config, kind, path, field, seen = /* @__PURE__ */ new
533
642
  /** A token's value with alias refs chased to the base literal — {@link resolveFieldValue} for the one
534
643
  * field every token consumer asks about, narrowed to the scalar a display or a stylesheet can use. */
535
644
  function resolveTokenValue(config, path, seen = /* @__PURE__ */ new Set()) {
536
- const value = resolveFieldValue(config, "token", path, "value", seen);
537
- return typeof value === "string" || typeof value === "number" ? value : void 0;
645
+ return literalOf({
646
+ config,
647
+ value: resolveFieldValue(config, "token", path, "value", seen),
648
+ seen
649
+ });
650
+ }
651
+ /**
652
+ * A stored value as the literal it comes to: a string or a number as itself, an OKLCH colour as its
653
+ * CSS, arithmetic evaluated over its operands' literals (`multiply(Token.ref('spacing'), 4)` with
654
+ * `spacing` at `0.25rem` is `1rem`), and a token ref as that token's literal. `undefined` for a value
655
+ * that is no single literal (a shadow, a gradient) or a ref that resolves to nothing.
656
+ */
657
+ function literalValueOf(config, value) {
658
+ return literalOf({
659
+ config,
660
+ value,
661
+ seen: /* @__PURE__ */ new Set()
662
+ });
663
+ }
664
+ /** `condition` reads each token the value names under that override condition, `null` its base. */
665
+ function literalOf({ config, value, seen, condition = null }) {
666
+ if (typeof value === "number") return value;
667
+ if (typeof value === "string") {
668
+ if (!hasRefMarker(value)) return value;
669
+ let unresolved = false;
670
+ const text = replaceRefMarkers(value, (target) => {
671
+ const literal = literalOf({
672
+ config,
673
+ value: target,
674
+ seen,
675
+ condition
676
+ });
677
+ if (literal === void 0) unresolved = true;
678
+ return String(literal ?? "");
679
+ });
680
+ return unresolved ? void 0 : text;
681
+ }
682
+ if (isOklchColor(value)) return renderOklch(value);
683
+ if (isMultiply(value) || isDivide(value)) return evaluateCalc(value, (part) => literalOf({
684
+ config,
685
+ value: part,
686
+ seen,
687
+ condition
688
+ }));
689
+ if (isRef(value) && kindOf(value) === "token") {
690
+ const source = config.configFor(value);
691
+ if (!source) return void 0;
692
+ return condition === null ? resolveTokenValue(source, pathOf(value), new Set(seen)) : resolveTokenValueUnder(source, pathOf(value), condition, new Set(seen));
693
+ }
538
694
  }
539
695
  /**
540
696
  * The literal a token resolves to under an override condition: its own override there when it holds
@@ -559,7 +715,25 @@ function resolveTokenValueUnder(config, path, condition, seen = /* @__PURE__ */
559
715
  if (kindOf(value) !== "token") return void 0;
560
716
  return resolveTokenValueUnder(source, pathOf(value), condition, seen);
561
717
  }
562
- return typeof value === "string" || typeof value === "number" ? value : void 0;
718
+ return literalOf({
719
+ config,
720
+ value,
721
+ seen,
722
+ condition
723
+ });
724
+ }
725
+ /**
726
+ * A string's interpolated refs written as their tokens: `blur({token:blur/sm})` is
727
+ * `blur(var(--blur-sm))`. A ref opening a `var(` is the variable's name, so a fallback can follow it:
728
+ * `var({token:tw/leading}, 1.5)` is `var(--tw-leading, 1.5)`.
729
+ */
730
+ function renderInterpolated({ config, text, varPrefix }) {
731
+ if (!text.includes("{")) return text;
732
+ return text.replace(/(var\(\s*)?\{([a-z][\w-]*:[^{}\s]*)\}/g, (_, open, target) => {
733
+ const rendered = renderStyleValue(config, { __ref: target }, varPrefix);
734
+ if (!open) return rendered;
735
+ return `${open}${/^var\((--[\w-]+)\)$/.exec(rendered)?.[1] ?? rendered}`;
736
+ });
563
737
  }
564
738
  /** A style-bag value → its CSS string: a token ref (or a bare token path) becomes a
565
739
  * `var(--…)` reference (so mode/theme overrides still flow), anything else is the literal.
@@ -576,18 +750,33 @@ function renderStyleValue(config, value, varPrefix) {
576
750
  if (!source) return "";
577
751
  if (kindOf(value) === "font") return fontStackOf(source, pathOf(value)) ?? "";
578
752
  const slug = sourceOf(value);
753
+ if (!declaresCssVariable(source, pathOf(value))) return renderStyleValue(source, inlineValueOf(source, pathOf(value)), slug === void 0 ? varPrefix : linkedVarNaming(config, slug));
579
754
  return cssVarRef(source, "token", pathOf(value), slug === void 0 ? varPrefix : linkedVarNaming(config, slug));
580
755
  }
756
+ if (typeof value === "string" && hasRefMarker(value)) return renderInterpolated({
757
+ config,
758
+ text: value,
759
+ varPrefix
760
+ });
581
761
  if (typeof value === "string" && sourceOf(value) !== void 0) {
582
762
  const asRef = ref("token", value);
583
763
  if (config.resolveRef(asRef)) return renderStyleValue(config, asRef, varPrefix);
584
764
  }
585
- if (typeof value === "string" && config.resolve("token", value)) return cssVarRef(config, "token", value, varPrefix);
765
+ if (typeof value === "string" && config.resolve("token", value)) {
766
+ if (!declaresCssVariable(config, value)) return renderStyleValue(config, inlineValueOf(config, value), varPrefix);
767
+ return cssVarRef(config, "token", value, varPrefix);
768
+ }
586
769
  if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) return value.slice(1, -1).replace(/url\([^)]*\)|_/g, (span) => span === "_" ? " " : span);
587
770
  return String(value);
588
771
  }
772
+ /** The stored value of a token that declares no variable, rendered in its place. */
773
+ function inlineValueOf(config, path) {
774
+ return (config.resolve("token", path)?.toJSON())?.value;
775
+ }
589
776
  /**
590
- * The value a negating prop paints — `calc(<value> * -1)`.
777
+ * The value a negating prop paints: a length or a percentage with a minus sign (`-1px`, `-100%`, and
778
+ * a zero as it is), a scale step's multiplier negated (`calc(var(--spacing) * -4)`), and anything
779
+ * else, a `var()` included, as `calc(<value> * -1)` — the spellings Tailwind writes.
591
780
  *
592
781
  * `calc` rather than a computed literal because the value is usually a `var(--…)`: the token it names
593
782
  * has to stay a reference, so a mode override or a theme swap still reaches it. It's applied at the
@@ -598,8 +787,33 @@ function renderStyleValue(config, value, varPrefix) {
598
787
  * literal is worse than one that never negates at all.
599
788
  */
600
789
  function negatedCssValue(value) {
790
+ const length = leadingNumber(value);
791
+ if (length && NEGATABLE_UNIT.test(length.rest)) {
792
+ const { sign, digits, rest: unit } = length;
793
+ if (Number(digits) === 0) return value;
794
+ return sign ? `${digits}${unit}` : `-${digits}${unit}`;
795
+ }
796
+ const step = scaleStepOf(value);
797
+ if (step) return `calc(${step.base} * ${step.sign ? "" : "-"}${step.digits})`;
601
798
  return `calc(${value} * -1)`;
602
799
  }
800
+ const NEGATABLE_UNIT = /^(?:px|%|r?em|ch|ex|lh|s?v[hw]|d?v[hw]|l?v[hw]|vmin|vmax|cm|mm|in|pt|pc)$/;
801
+ /** A `calc(var(--name) * <number>)` split into the `var()` and the factor. */
802
+ function scaleStepOf(value) {
803
+ if (!value.startsWith("calc(") || !value.endsWith(")")) return void 0;
804
+ const inner = value.slice(5, -1);
805
+ const times = inner.indexOf(" * ");
806
+ if (times === -1) return void 0;
807
+ const base = inner.slice(0, times);
808
+ if (!/^var\(--[\w-]+\)$/.test(base)) return void 0;
809
+ const factor = leadingNumber(inner.slice(times + 3));
810
+ if (factor?.rest !== "") return void 0;
811
+ return {
812
+ base,
813
+ sign: factor.sign,
814
+ digits: factor.digits
815
+ };
816
+ }
603
817
  /**
604
818
  * What one component's own module does with each prop it declares — name → the style property that
605
819
  * prop writes, or `null` for a prop that writes none.
@@ -749,6 +963,27 @@ function routedBag({ config, through, bag }) {
749
963
  }
750
964
  return routed;
751
965
  }
966
+ /** A declaration's key as CSS: a property as itself, a token's key `{token:tw/blur}` as the variable
967
+ * the token declares. */
968
+ function declarationKeyName({ config, key, namespace }) {
969
+ const keyed = refOfKey(key);
970
+ return keyed ? cssVar(config, "token", pathOf(keyed), namespace) : key;
971
+ }
972
+ /** A style prop's own `declarations`, each ref written as its token: a key `{token:…}` as the
973
+ * variable the token declares, and an interpolated ref as it renders. `{value}` is left in place. */
974
+ function propDeclarations({ config, body, namespace }) {
975
+ return Object.fromEntries(Object.entries(body.declarations ?? {}).map(([key, value]) => {
976
+ return [declarationKeyName({
977
+ config,
978
+ key,
979
+ namespace
980
+ }), renderInterpolated({
981
+ config,
982
+ text: declarationText(value),
983
+ varPrefix: namespace
984
+ })];
985
+ }));
986
+ }
752
987
  /**
753
988
  * The leaf a bag value names, for looking its entry up — or `undefined` when it names none.
754
989
  *
@@ -759,8 +994,38 @@ function leafAuthoredFor(value) {
759
994
  if (typeof value === "boolean") return value ? "" : void 0;
760
995
  if (typeof value === "string" || typeof value === "number") return String(value);
761
996
  }
997
+ /**
998
+ * The declarations a bag sets on the element itself. Declarations a prop scopes to a `selector` or a
999
+ * value scopes to an at-rule are not the element's, so they are left out; a stylesheet reads them
1000
+ * from {@link styleDeclarationGroups}.
1001
+ */
762
1002
  function styleDeclarations({ config, bag, namespace }) {
763
- const out = {};
1003
+ return styleDeclarationGroups({
1004
+ config,
1005
+ bag,
1006
+ namespace
1007
+ }).find((group) => group.selector === void 0 && group.atRule === void 0)?.declarations ?? {};
1008
+ }
1009
+ /**
1010
+ * A bag's declarations in the blocks they apply in: the element's own first, then each `selector`
1011
+ * and at-rule block in the order the bag first reaches it. A block that sets nothing is left out.
1012
+ */
1013
+ function styleDeclarationGroups({ config, bag, namespace }) {
1014
+ const groups = /* @__PURE__ */ new Map();
1015
+ const blockFor = ({ selector, atRule }) => {
1016
+ const id = `${selector ?? ""}\u0000${atRule ?? ""}`;
1017
+ let group = groups.get(id);
1018
+ if (!group) {
1019
+ group = {
1020
+ ...selector === void 0 ? {} : { selector },
1021
+ ...atRule === void 0 ? {} : { atRule },
1022
+ declarations: {}
1023
+ };
1024
+ groups.set(id, group);
1025
+ }
1026
+ return group.declarations;
1027
+ };
1028
+ const out = blockFor({});
764
1029
  const fades = /* @__PURE__ */ new Map();
765
1030
  for (const [rawKey, value] of Object.entries(bag)) {
766
1031
  if (rawKey.startsWith("_") || rawKey.startsWith("--")) continue;
@@ -794,11 +1059,36 @@ function styleDeclarations({ config, bag, namespace }) {
794
1059
  if (!prop) continue;
795
1060
  const body = prop.toJSON();
796
1061
  if (isRef(body.opacityOf)) continue;
797
- const cssProperties = Array.isArray(body.properties) ? body.properties : [];
1062
+ const selector = body.selector || void 0;
1063
+ const target = blockFor({ selector });
1064
+ const cssProperties = Array.isArray(body.properties) ? body.properties.map((property) => {
1065
+ const keyed = refOfKey(property);
1066
+ return keyed ? cssVar(config, "token", pathOf(keyed), namespace) : property;
1067
+ }) : [];
798
1068
  const authoredLeafValue = leafAuthoredFor(value);
799
1069
  const entry = authoredLeafValue === void 0 ? void 0 : stylePropEntries(config, key).find((candidate) => candidate.leaf === authoredLeafValue);
800
1070
  if (entry?.declarations) {
801
- for (const [cssProperty, declaredValue] of Object.entries(entry.declarations)) out[cssProperty] = renderStyleValue(config, declaredValue, namespace);
1071
+ for (const [key, declaredValue] of Object.entries(entry.declarations)) target[declarationKeyName({
1072
+ config,
1073
+ key,
1074
+ namespace
1075
+ })] = renderStyleValue(config, declaredValue, namespace);
1076
+ for (const [cssProperty, declared] of Object.entries(propDeclarations({
1077
+ config,
1078
+ body,
1079
+ namespace
1080
+ }))) if (!declared.includes("{value}")) target[cssProperty] = declared;
1081
+ for (const [atRule, block] of Object.entries(entry.atRules ?? {})) {
1082
+ const scoped = blockFor({
1083
+ selector,
1084
+ atRule
1085
+ });
1086
+ for (const [key, declaredValue] of Object.entries(block)) scoped[declarationKeyName({
1087
+ config,
1088
+ key,
1089
+ namespace
1090
+ })] = renderStyleValue(config, declaredValue, namespace);
1091
+ }
802
1092
  continue;
803
1093
  }
804
1094
  const base = renderBagValue({
@@ -811,9 +1101,14 @@ function styleDeclarations({ config, bag, namespace }) {
811
1101
  let rendered = stylePropNegates(config, key) ? negatedCssValue(base) : base;
812
1102
  const fade = fades.get(key);
813
1103
  if (fade !== void 0) rendered = `color-mix(in srgb, ${rendered} ${fade}, transparent)`;
814
- for (const cssProperty of cssProperties) out[cssProperty] = rendered;
1104
+ for (const cssProperty of cssProperties) target[cssProperty] = rendered;
1105
+ for (const [cssProperty, declared] of Object.entries(propDeclarations({
1106
+ config,
1107
+ body,
1108
+ namespace
1109
+ }))) target[cssProperty] = declared.replaceAll("{value}", rendered);
815
1110
  }
816
- return out;
1111
+ return [...groups.values()].filter((group) => Object.keys(group.declarations).length > 0);
817
1112
  }
818
1113
  /** One bag value as CSS text, or `undefined` for a flag turned off. */
819
1114
  function renderBagValue({ config, key, value, namespace }) {
@@ -1375,4 +1670,4 @@ function declaredPropDefaults(props) {
1375
1670
  return out;
1376
1671
  }
1377
1672
  //#endregion
1378
- export { LAYER_PROPS, authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, glyphComponentOf, glyphLibrariesOf, glyphPropsOf, glyphPropsOfBody, heldLinked, holdingPreviewConditions, iconToolAxisOptions, iconToolFor, iconToolOf, iconToolsOf, keyedPreviewConditions, layerPropsErrors, linkedVarNaming, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, refListOf, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, takenByForward, validateComponentProps };
1673
+ export { LAYER_PROPS, authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declarationKeyName, declaredPropDefaults, declaredPropRouting, declaresCssVariable, glyphComponentOf, glyphLibrariesOf, glyphPropsOf, glyphPropsOfBody, heldLinked, holdingPreviewConditions, iconToolAxisOptions, iconToolFor, iconToolOf, iconToolsOf, keyedPreviewConditions, layerPropsErrors, linkedVarNaming, literalValueOf, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, refListOf, renderInterpolated, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarationGroups, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, takenByForward, tokenReference, validateComponentProps };
@@ -33,7 +33,7 @@ function edgesOf(ref, data, extra = [], linkKinds = [], singletonKinds = []) {
33
33
  if (linkKinds.length > 0) for (const target of collected) {
34
34
  const slug = sourceOf(target);
35
35
  if (slug === void 0) continue;
36
- for (const kind of linkKinds) targets.add(`${kind}:${slug}`);
36
+ for (const kind of linkKinds) targets.add(`${kind}:${slug.split("~")[0]}`);
37
37
  }
38
38
  for (const target of extra) targets.add(target);
39
39
  targets.delete(ref);
@@ -49,6 +49,13 @@ type AuthoredValueForm = (ctx: {
49
49
  }) => z.ZodTypeAny | undefined;
50
50
  interface EntityKind {
51
51
  readonly name: string;
52
+ /** Adapt a pinned source body before this kind validates a linked extension. */
53
+ readonly prepareLinkedBody?: (args: {
54
+ readonly path: string;
55
+ readonly body: Record<string, unknown>;
56
+ readonly source: Config; /** The config borrowing the body, which resolves the body's source-qualified refs. */
57
+ readonly config: Config;
58
+ }) => Record<string, unknown>;
52
59
  /** The entity class's name (`style-property` → `StyleProperty`) — what a consumer writes to reach
53
60
  * it (`StyleProperty.ref(...)`, `Token.group(...)`).
54
61
  *
@@ -32,7 +32,7 @@ function titleCase(kind) {
32
32
  /** Derive a kind's immutable metadata from its field schemas — the value a `defineEntity`/
33
33
  * `defineDerivedEntity` attaches to its class and a `Config.register` stores in `#entities`. */
34
34
  function buildKind(spec) {
35
- const { name, fields, groupFields, subEntities = {}, computed, label, labelPlural, schemas: userSchemas, derived, sourceIdentity, sourceOrigin, authoredRefs, linkable, localGroupFields, emergentGroups, singleton = false, bareMember, sourceResolution, bulkAdopts, bulkOverlay, bulkDirectives, bulkOverlayWrite, nameEdges, rewriteName, styleBags, uniqueLeaves, authoredValue, mentionFields, invariants } = spec;
35
+ const { name, prepareLinkedBody, fields, groupFields, subEntities = {}, computed, label, labelPlural, schemas: userSchemas, derived, sourceIdentity, sourceOrigin, authoredRefs, linkable, localGroupFields, emergentGroups, singleton = false, bareMember, sourceResolution, bulkAdopts, bulkOverlay, bulkDirectives, bulkOverlayWrite, nameEdges, rewriteName, styleBags, uniqueLeaves, authoredValue, mentionFields, invariants } = spec;
36
36
  const allComputed = {
37
37
  sources: ({ member, config }) => sourceSlugs(member, config),
38
38
  borrowedFrom: ({ config, path }) => config.borrowedFrom(name, path),
@@ -48,6 +48,7 @@ function buildKind(spec) {
48
48
  }
49
49
  return {
50
50
  name,
51
+ ...prepareLinkedBody ? { prepareLinkedBody } : {},
51
52
  label: resolvedLabel,
52
53
  entityName: spec.entityName ?? entityClassName(name),
53
54
  labelPlural: labelPlural ?? `${resolvedLabel}s`,
@@ -15,7 +15,7 @@ import { Patch, SerializedConfig } from "./Config.js";
15
15
  * sets this constant to its own older timestamp; the merge ref then carries a registered migration
16
16
  * newer than the constant, and the assertion fails until the author re-mints.
17
17
  */
18
- declare const CURRENT_SCHEMA_VERSION = 20261001131000;
18
+ declare const CURRENT_SCHEMA_VERSION = 20261001162046;
19
19
  /** What the walkers need to see of an envelope — loose on purpose, so a typed `SerializedConfig`
20
20
  * and a raw old-shape record both flow in without casts. */
21
21
  interface VersionStamped {