@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
@@ -38,7 +38,10 @@ declare const STYLE_PROP_VALUE_FORMS: {
38
38
  alias: z.ZodBoolean;
39
39
  value: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
40
40
  __ref: z.ZodString;
41
- }, z.core.$strip>, z.ZodRecord<z.ZodString, z.ZodString>]>;
41
+ }, z.core.$strip>, z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
42
+ __ref: z.ZodString;
43
+ }, z.core.$strip>]>>]>;
44
+ className: z.ZodOptional<z.ZodString>;
42
45
  }, z.core.$strict>;
43
46
  readonly aliasToken: ({
44
47
  config
@@ -47,14 +50,21 @@ declare const STYLE_PROP_VALUE_FORMS: {
47
50
  value: z.ZodObject<{
48
51
  __ref: z.ZodString;
49
52
  }, z.core.$strip>;
53
+ className: z.ZodOptional<z.ZodString>;
50
54
  }, z.core.$strict>;
51
55
  readonly aliasLiteral: (ctx?: StylePropValueCtx) => z.ZodObject<{
52
56
  alias: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
53
57
  value: z.ZodString;
58
+ className: z.ZodOptional<z.ZodString>;
54
59
  }, z.core.$strict>;
55
60
  readonly aliasDeclarations: (_ctx?: StylePropValueCtx) => z.ZodObject<{
56
61
  alias: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
57
- value: z.ZodRecord<z.ZodString, z.ZodString>;
62
+ value: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
63
+ __ref: z.ZodString;
64
+ }, z.core.$strip>]>, z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
65
+ __ref: z.ZodString;
66
+ }, z.core.$strip>]>>]>>;
67
+ className: z.ZodOptional<z.ZodString>;
58
68
  }, z.core.$strict>;
59
69
  };
60
70
  type Forms = typeof STYLE_PROP_VALUE_FORMS;
@@ -68,6 +78,10 @@ interface StylePropLeaf {
68
78
  readonly leaf: string;
69
79
  readonly value: string;
70
80
  readonly declarations?: Record<string, string>;
81
+ /** Declarations that apply under an at-rule, by the at-rule's prelude (`@supports (…)`). */
82
+ readonly atRules?: Record<string, Record<string, string>>;
83
+ /** The class the value is written as, when it names its own. */
84
+ readonly className?: string;
71
85
  }
72
86
  /** One entry a ref names: the leaf it is called and the qualified path it stores. */
73
87
  interface RefMember {
@@ -98,6 +112,23 @@ declare function classifyStylePropValue({
98
112
  config: Config;
99
113
  entry: StylePropValue;
100
114
  }): StylePropValueKind;
115
+ /** A style property value that spells a token's variable as text, `var(--blur-sm)` for `blur/sm`. */
116
+ interface TokenVariableAsText {
117
+ /** The style property. */
118
+ readonly styleProperty: string;
119
+ /** Where in its body, as `values.0.value` or `declarations.outline`. */
120
+ readonly at: string;
121
+ /** The variable spelled. */
122
+ readonly variable: string;
123
+ /** The token that declares it. */
124
+ readonly token: string;
125
+ }
126
+ /**
127
+ * This config's style properties whose values or declarations spell a token's variable as text. The
128
+ * text stops naming the token on a rename or a prefix change, where an interpolated `Token.ref`
129
+ * follows it. A variable no token declares, such as a third-party library's, isn't one.
130
+ */
131
+ declare function tokenVariablesAsText(config: Config): TokenVariableAsText[];
101
132
  /** StyleProperty (`flex/direction`) — a prop a component can expose, mapped to a CSS
102
133
  * property with an allowed value set. Grouping (`flex`) is emergent from the path. */
103
134
  declare const StylePropertyEntity: EntityClass<z.ZodObject<{
@@ -107,6 +138,11 @@ declare const StylePropertyEntity: EntityClass<z.ZodObject<{
107
138
  classPrefix: z.ZodOptional<z.ZodString>;
108
139
  arbitrary: z.ZodDefault<z.ZodBoolean>;
109
140
  negates: z.ZodDefault<z.ZodBoolean>;
141
+ selector: z.ZodOptional<z.ZodString>;
142
+ alpha: z.ZodOptional<z.ZodString>;
143
+ declarations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
144
+ __ref: z.ZodString;
145
+ }, z.core.$strip>]>>>;
110
146
  opacityOf: z.ZodOptional<z.ZodLazy<z.ZodType<Ref, unknown, z.core.$ZodTypeInternals<Ref, unknown>>>>;
111
147
  }, z.core.$strip>, z.ZodObject<Record<never, never>, z.core.$strip>, "style-property", {}, Record<never, never>, false, Record<never, never>, Record<never, never>>;
112
148
  /**
@@ -132,4 +168,4 @@ declare function cssPropertyNames(styleProp: {
132
168
  readonly properties: readonly string[];
133
169
  }): string[];
134
170
  //#endregion
135
- export { RefMember, STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StylePropLeaf, StylePropValue, StylePropValueCtx, StylePropValueKind, StyleProperty, StylePropertyClass, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName };
171
+ export { RefMember, STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StylePropLeaf, StylePropValue, StylePropValueCtx, StylePropValueKind, StyleProperty, StylePropertyClass, TokenVariableAsText, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName, tokenVariablesAsText };
@@ -1,9 +1,10 @@
1
- import { isRef, kindOf, pathOf, ref, refSchema, sourceOf, splitRef } from "../../framework/utils/refs.js";
1
+ import { declarationText, isRef, kindOf, pathOf, ref, refOfKey, refSchema, replaceRefMarkers, sourceOf, splitRef } from "../../framework/utils/refs.js";
2
2
  import { defineEntity } from "../../framework/defineEntity.js";
3
3
  import { cssValueIssue, isCssProperty } from "../../css/values.js";
4
4
  import { camelCase } from "./style-bag.js";
5
5
  import { createSliceMemo } from "../../framework/memo.js";
6
6
  import { isIdentifier } from "../../framework/utils/identifier.js";
7
+ import { cssVar, declaresCssVariable } from "../../framework/projections.js";
7
8
  import { z } from "zod";
8
9
  //#region src/entities/system/StyleProperty.ts
9
10
  /**
@@ -31,10 +32,11 @@ const aliasName = z.union([z.string(), z.number()]);
31
32
  /** The first property among `properties` that refuses `text`, as the refusal. A property the table
32
33
  * can't judge says nothing. */
33
34
  function cssIssue(properties, text, grammar) {
35
+ const written = replaceRefMarkers(text, () => "var(--ref)");
34
36
  for (const property of properties ?? []) {
35
37
  const issue = cssValueIssue({
36
38
  property,
37
- value: text,
39
+ value: written,
38
40
  grammar
39
41
  });
40
42
  if (issue !== void 0 && issue !== "unknown") return issue;
@@ -76,6 +78,14 @@ const namesItem = (config, wanted) => (ctx) => {
76
78
  /** A declaration bag's key. A ref is a record of one string too, so the ref marker is not a key here,
77
79
  * or a ref no ref form admits would read as a bag. */
78
80
  const declarationKey = z.string().regex(/^(?!__ref$)/);
81
+ /** A declaration's value: CSS text, or a ref to the token it is, `'border-style':
82
+ * Token.ref('tw/border-style')`. */
83
+ const declarationValue = z.union([z.string(), refSchema]);
84
+ /** The class an alias or a flag is written as, in place of `<classPrefix>-<alias>`. */
85
+ const valueClassName = z.string().regex(/^[^\s]+$/).meta({
86
+ title: "Class name",
87
+ description: "The class this value is written as, in place of the prop's `<classPrefix>-<leaf>`: `{ alias: 'auto', value: 'auto', className: 'isolation-auto' }`. The system's class prefix still applies."
88
+ }).optional();
79
89
  const STYLE_PROP_VALUE_FORMS = {
80
90
  keyword: (ctx = {}) => z.string().meta({
81
91
  title: "Keyword",
@@ -105,32 +115,46 @@ const STYLE_PROP_VALUE_FORMS = {
105
115
  value: z.union([
106
116
  z.string(),
107
117
  refSchema,
108
- z.record(z.string(), z.string())
109
- ])
118
+ z.record(z.string(), declarationValue)
119
+ ]),
120
+ className: valueClassName
110
121
  }).meta({
111
122
  title: "Flag",
112
123
  description: "A boolean alias for a boolean-shaped prop: `<Box border />` sets `border-width: 1px`."
113
124
  }),
114
125
  aliasToken: ({ config } = {}) => z.strictObject({
115
126
  alias: aliasName,
116
- value: refSchema.check(namesItem(config, true))
127
+ value: refSchema.check(namesItem(config, true)),
128
+ className: valueClassName
117
129
  }).meta({
118
130
  title: "Token alias",
119
131
  description: "One token under a name of the prop's own, still emitted as its `var()`."
120
132
  }),
121
133
  aliasLiteral: (ctx = {}) => z.strictObject({
122
134
  alias: aliasName,
123
- value: z.string()
135
+ value: z.string(),
136
+ className: valueClassName
124
137
  }).meta({
125
138
  title: "Alias",
126
139
  description: "The authored key and the CSS value it resolves to: `{ alias: \"col\", value: \"column\" }`."
127
140
  }).check(cssText(ctx, (value) => value.value)),
128
141
  aliasDeclarations: (_ctx = {}) => z.strictObject({
129
142
  alias: aliasName,
130
- value: z.record(declarationKey, z.string())
143
+ value: z.record(declarationKey, z.union([declarationValue, z.record(declarationKey, declarationValue)])),
144
+ className: valueClassName
145
+ }).check((ctx) => {
146
+ for (const [key, value] of Object.entries(ctx.value.value)) {
147
+ if (typeof value === "string" || isRef(value) || key.startsWith("@")) continue;
148
+ ctx.issues.push({
149
+ code: "custom",
150
+ input: value,
151
+ path: ["value", key],
152
+ message: `\`${key}\` holds a block, and only an at-rule (\`@supports …\`, \`@media …\`) holds declarations of its own.`
153
+ });
154
+ }
131
155
  }).meta({
132
156
  title: "Declarations",
133
- description: "A leaf that stands for a whole declaration bag: `truncate` sets `overflow`, `text-overflow` and `white-space`."
157
+ description: "A leaf that stands for a whole declaration bag: `truncate` sets `overflow`, `text-overflow` and `white-space`. A key that is an at-rule (`@media (forced-colors: active)`) holds the declarations that apply under it."
134
158
  })
135
159
  };
136
160
  const KINDS = Object.keys(STYLE_PROP_VALUE_FORMS);
@@ -140,7 +164,7 @@ function flagValue(value, members) {
140
164
  if ("__ref" in value && typeof value.__ref === "string") return members(value).map(({ path }) => ({ value: path }));
141
165
  return [{
142
166
  value: "",
143
- declarations: value
167
+ declarations: textOf(value)
144
168
  }];
145
169
  }
146
170
  /**
@@ -172,22 +196,45 @@ const STYLE_PROP_VALUE_LEAVES = {
172
196
  })),
173
197
  flag: (entry, members) => entry.alias ? flagValue(entry.value, members).map((rest) => ({
174
198
  leaf: "",
175
- ...rest
199
+ ...rest,
200
+ ...classNameOf(entry)
176
201
  })) : [],
177
202
  aliasToken: (entry, members) => members(entry.value).map(({ path }) => ({
178
203
  leaf: String(entry.alias),
179
- value: path
204
+ value: path,
205
+ ...classNameOf(entry)
180
206
  })),
181
207
  aliasLiteral: (entry) => [{
182
208
  leaf: String(entry.alias),
183
- value: entry.value
209
+ value: entry.value,
210
+ ...classNameOf(entry)
184
211
  }],
185
212
  aliasDeclarations: (entry) => [{
186
213
  leaf: String(entry.alias),
187
214
  value: "",
188
- declarations: entry.value
215
+ ...splitDeclarations(entry.value),
216
+ ...classNameOf(entry)
189
217
  }]
190
218
  };
219
+ /** A bag's declarations as text, each ref as its marker. */
220
+ function textOf(bag) {
221
+ return Object.fromEntries(Object.entries(bag).map(([key, value]) => [key, declarationText(value)]));
222
+ }
223
+ /** A declaration bag as a leaf holds it: the plain declarations, and the blocks under at-rules. */
224
+ function splitDeclarations(bag) {
225
+ const declarations = {};
226
+ const atRules = {};
227
+ for (const [key, value] of Object.entries(bag)) if (typeof value === "string" || isRef(value)) declarations[key] = declarationText(value);
228
+ else atRules[key] = textOf(value);
229
+ return Object.keys(atRules).length > 0 ? {
230
+ declarations,
231
+ atRules
232
+ } : { declarations };
233
+ }
234
+ /** An entry's own class, spread into its leaf when it names one. */
235
+ function classNameOf(entry) {
236
+ return entry.className === void 0 ? {} : { className: entry.className };
237
+ }
191
238
  /** The leaves one stored entry stands for; none for a value no form admits. */
192
239
  function leavesOfStylePropValue(entry, members) {
193
240
  const kind = stylePropValueFormOf(entry);
@@ -221,7 +268,7 @@ function classifyStylePropValue({ config, entry }) {
221
268
  */
222
269
  const cssPropertyName = z.string().check((ctx) => {
223
270
  const name = ctx.value;
224
- if (name.startsWith("-")) return;
271
+ if (name.startsWith("-") || refOfKey(name)) return;
225
272
  if (/[A-Z]/.test(name)) {
226
273
  const css = name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
227
274
  ctx.issues.push({
@@ -263,6 +310,8 @@ function eachValueOnce(ctx) {
263
310
  }
264
311
  /** One comparable form for a CSS value, so `#FFFFFF` and `#ffffff ` read as the same value. */
265
312
  const comparable = (value) => typeof value === "string" || typeof value === "number" ? String(value).trim().toLowerCase() : void 0;
313
+ /** A colour spelled as a hex or a colour function. */
314
+ const COLOR_LITERAL = /^(?:#[0-9a-f]{3,8}|(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\(.*\))$/;
266
315
  /** Whether `values` reaches the token at `path` — through the token itself, an alias naming it, or
267
316
  * a group above it. */
268
317
  function reaches(values, path) {
@@ -307,15 +356,80 @@ const copiesNoToken = (config) => (ctx) => {
307
356
  });
308
357
  });
309
358
  };
310
- /** Each token value, as `comparable` spells it, and the first token path that holds it. Kept per token
311
- * slice, since a batch of style-property writes would otherwise list every token once per write. */
359
+ /** Each variable a token declares, by name, and the token declaring it. */
360
+ const tokenPathsByVariable = createSliceMemo({
361
+ kinds: ["token", "settings"],
362
+ compute: (config) => {
363
+ const tokens = /* @__PURE__ */ new Map();
364
+ for (const token of config.list("token")) {
365
+ if (!declaresCssVariable(config, token.path)) continue;
366
+ tokens.set(cssVar(config, "token", token.path), token.path);
367
+ }
368
+ return tokens;
369
+ }
370
+ });
371
+ /**
372
+ * This config's style properties whose values or declarations spell a token's variable as text. The
373
+ * text stops naming the token on a rename or a prefix change, where an interpolated `Token.ref`
374
+ * follows it. A variable no token declares, such as a third-party library's, isn't one.
375
+ */
376
+ function tokenVariablesAsText(config) {
377
+ const tokens = tokenPathsByVariable(config);
378
+ const found = [];
379
+ const visit = ({ styleProperty, value, at }) => {
380
+ if (typeof value === "string") for (const [, variable = ""] of value.matchAll(/var\((--[\w-]+)/g)) {
381
+ const token = tokens.get(variable);
382
+ if (token) found.push({
383
+ styleProperty,
384
+ at: at.join("."),
385
+ variable,
386
+ token
387
+ });
388
+ }
389
+ else if (Array.isArray(value)) for (const [index, item] of value.entries()) visit({
390
+ styleProperty,
391
+ value: item,
392
+ at: [...at, index]
393
+ });
394
+ else if (value && typeof value === "object" && !("__ref" in value)) for (const [key, nested] of Object.entries(value)) visit({
395
+ styleProperty,
396
+ value: nested,
397
+ at: [...at, key]
398
+ });
399
+ };
400
+ for (const prop of config.list("style-property")) {
401
+ if (config.configFor(ref("style-property", prop.path)) !== config) continue;
402
+ const { values, declarations } = prop.toJSON();
403
+ visit({
404
+ styleProperty: prop.path,
405
+ value: values,
406
+ at: ["values"]
407
+ });
408
+ visit({
409
+ styleProperty: prop.path,
410
+ value: declarations,
411
+ at: ["declarations"]
412
+ });
413
+ }
414
+ return found;
415
+ }
416
+ /**
417
+ * Each colour token's value, as `comparable` spells it, and the first token path that holds it. Kept
418
+ * per token slice, since a batch of style-property writes would otherwise list every token once per
419
+ * write.
420
+ *
421
+ * Only colours: a colour literal that equals a token is that token written out, while a length or a
422
+ * number coincides across unrelated scales — `4px` is a blur radius and a stroke width, `1` a line
423
+ * height and `flex: 1`.
424
+ */
312
425
  const tokenPathsByValue = createSliceMemo({
313
426
  kinds: ["token"],
314
427
  compute: (config) => {
315
428
  const tokens = /* @__PURE__ */ new Map();
316
429
  for (const token of config.list("token")) {
317
430
  const value = comparable(token.body.value);
318
- if (value !== void 0 && !tokens.has(value)) tokens.set(value, token.path);
431
+ if (value === void 0 || !COLOR_LITERAL.test(value)) continue;
432
+ if (!tokens.has(value)) tokens.set(value, token.path);
319
433
  }
320
434
  return tokens;
321
435
  }
@@ -397,6 +511,15 @@ const styleFields = ({ config, body, linked } = {}) => z.object({
397
511
  */
398
512
  negates: z.boolean().default(false).describe("Apply the negation of the value, so one scale works in both directions. Defaults to `false`."),
399
513
  /**
514
+ * Declarations the prop sets beside every value. A prop whose value is one part of a composed
515
+ * property writes its part to a custom property and the whole here: `translateX` sets
516
+ * `--tw-translate-x` and `translate: var(--tw-translate-x) var(--tw-translate-y)`, so the X and Y
517
+ * props compose on one element.
518
+ */
519
+ selector: z.string().regex(/&/).optional().describe("The selector the prop's rules are written under, with `&` for its class: `:where(& > :not(:last-child))` styles the element's children, as `space-x-4` does."),
520
+ alpha: z.string().refine((name) => name.startsWith("--") || refOfKey(name) !== void 0).optional().describe("The custom property holding the alpha the prop's colour values are mixed at, as `--tw-shadow-alpha` for `shadow-red-500`, or a token's key for the variable it declares. A colour value is written as its literal, and under `@supports` as its variable mixed at that alpha."),
521
+ declarations: z.record(declarationKey, declarationValue).optional().describe("Declarations the prop sets with every value, as `translate: var(--tw-translate-x) var(--tw-translate-y)` beside a prop writing `--tw-translate-x`. `{value}` stands for the value the prop was given, so a prop with no `properties` of its own can still place it: `margin-inline-start: calc({value} * var(--tw-space-x-reverse))`."),
522
+ /**
400
523
  * The style prop whose alpha this one controls — `bgOpacity` is the opacity of `bg`.
401
524
  *
402
525
  * The pairing is modelled from the SIBLING's side, and the sibling is a real entity. The old model
@@ -455,4 +578,4 @@ function cssPropertyNames(styleProp) {
455
578
  return styleProp.properties.map(toCssPropertyName);
456
579
  }
457
580
  //#endregion
458
- export { STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StyleProperty, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName };
581
+ export { STYLE_PROP_VALUE_FORMS, STYLE_PROP_VALUE_LEAVES, StyleProperty, classifyStylePropValue, cssPropertyNames, leavesOfStylePropValue, stylePropValueFormOf, stylePropValueSchema, toCssPropertyName, tokenVariablesAsText };
@@ -2,8 +2,8 @@ import { Ref } from "../../framework/utils/ref-grammar.js";
2
2
  import { Ref as Ref$1 } from "../../framework/utils/refs.js";
3
3
  import { SubEntityClass } from "../../framework/defineSubEntity.js";
4
4
  import { PathOf } from "../../framework/registered.js";
5
- import { EntityClass, KindRef } from "../../framework/defineEntity.js";
6
5
  import { ComposedValue } from "./composed-values.js";
6
+ import { EntityClass, KindRef } from "../../framework/defineEntity.js";
7
7
  import { TokenReaders } from "./token-values.js";
8
8
  import { z } from "zod";
9
9
 
@@ -48,6 +48,12 @@ declare const tokenFields: z.ZodObject<{
48
48
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
49
49
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
50
50
  }, z.core.$strip>>>;
51
+ utility: z.ZodOptional<z.ZodBoolean>;
52
+ property: z.ZodOptional<z.ZodObject<{
53
+ syntax: z.ZodOptional<z.ZodString>;
54
+ inherits: z.ZodOptional<z.ZodBoolean>;
55
+ }, z.core.$strict>>;
56
+ cssVariable: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
51
57
  }, z.core.$strip>;
52
58
  /** A token's stored body — what a `create` writes and what `Token.get` hands back typed. Exported
53
59
  * so a consumer building one names the entity's own shape instead of restating it. */
@@ -59,8 +65,15 @@ declare const TokenEntity: EntityClass<z.ZodObject<{
59
65
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
60
66
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
61
67
  }, z.core.$strip>>>;
68
+ utility: z.ZodOptional<z.ZodBoolean>;
69
+ property: z.ZodOptional<z.ZodObject<{
70
+ syntax: z.ZodOptional<z.ZodString>;
71
+ inherits: z.ZodOptional<z.ZodBoolean>;
72
+ }, z.core.$strict>>;
73
+ cssVariable: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
62
74
  }, z.core.$strip>, z.ZodObject<{
63
75
  cssPrefix: z.ZodOptional<z.ZodString>;
76
+ cssVariables: z.ZodOptional<z.ZodBoolean>;
64
77
  }, z.core.$strip>, "token", {}, Record<never, never>, false, Record<never, never>, Record<never, never>>;
65
78
  /** A token ref as an authoring file holds it: the marker, `extend`, and the value readers. */
66
79
  type TokenReference<Path extends string> = KindRef<typeof TokenEntity, 'token', Path> & TokenReaders<Path>;
@@ -4,8 +4,10 @@ import { defineEntity } from "../../framework/defineEntity.js";
4
4
  import { defineSubEntity } from "../../framework/defineSubEntity.js";
5
5
  import { Font } from "./Font.js";
6
6
  import { Modifier } from "./Modifier.js";
7
+ import { WEB_CONFIG_NAME } from "../../configs/platform.js";
7
8
  import { authoringPhantom } from "../../framework/utils/authoring-phantom.js";
8
9
  import { TOKEN_READERS, spellTokenRead } from "./token-values.js";
10
+ import { prepareLinkedTokenBody } from "./linked-token-values.js";
9
11
  import { z } from "zod";
10
12
  //#region src/entities/system/Token.ts
11
13
  /**
@@ -116,13 +118,29 @@ function defineOverride(value) {
116
118
  const Override = defineOverride(tokenValue);
117
119
  const TokenEntity = defineEntity({
118
120
  kind: "token",
121
+ prepareLinkedBody: ({ path, body, source, config }) => prepareLinkedTokenBody({
122
+ path,
123
+ body,
124
+ source,
125
+ config,
126
+ destinationPlatform: WEB_CONFIG_NAME
127
+ }),
119
128
  fields: z.object({
120
129
  value: tokenValue.describe("The base value: a literal, a ref to another token, or a composed value such as a gradient."),
121
130
  type: z.string().optional().describe("The CSS value type, when the value alone does not say it."),
122
- overrides: Override.optional().describe("Per-mode values, keyed by the condition they apply under.")
131
+ overrides: Override.optional().describe("Per-mode values, keyed by the condition they apply under."),
132
+ utility: z.boolean().optional().describe("`false` keeps the token out of its group's values, so a style prop that takes the group writes no class for it — `spacing/scale` powers `--spacing` without becoming a `p-scale`. A ref naming the token still resolves."),
133
+ property: z.strictObject({
134
+ syntax: z.string().optional().describe("The `@property` syntax, as `<length>`. Defaults to `*`."),
135
+ inherits: z.boolean().optional().describe("Whether the property inherits. Defaults to `false`.")
136
+ }).optional().describe("Registers the token's custom property with `@property` instead of declaring it on `:root`, with the token's value as its initial value, or none for `initial`. For a variable utilities set on an element, as Tailwind's `--tw-border-style`."),
137
+ cssVariable: z.union([z.string().regex(/^(?:--)?[A-Za-z0-9_][A-Za-z0-9_-]*$/), z.literal(false)]).optional().describe("The custom property this token is declared as. A name is placed under the system's prefix: `'spacing'` is `--uds-spacing`, or `--spacing` with an empty prefix. A name starting with `--` is used exactly, whatever the prefix, as for a third-party library's variable: `'--mantine-color-primary'`. `false` declares none and writes the token into the values that use it. Unset, its name comes from its path and the nearest group's `cssPrefix`, and whether it is declared from the nearest group's `cssVariables`.")
138
+ }),
139
+ groupFields: z.object({
140
+ cssPrefix: z.string().optional().describe("The prefix the group emits its custom properties under. Local to the config that holds the group."),
141
+ cssVariables: z.boolean().optional().describe("`false` writes the group's tokens into the values that use them instead of declaring a custom property for each, as Tailwind's `@theme inline` does. A token's own `cssVariable` wins. Local to the config that holds the group.")
123
142
  }),
124
- groupFields: z.object({ cssPrefix: z.string().optional().describe("The prefix the group emits its custom properties under. Local to the config that holds the group.") }),
125
- localGroupFields: ["cssPrefix"],
143
+ localGroupFields: ["cssPrefix", "cssVariables"],
126
144
  subEntities: { overrides: Override },
127
145
  schemas: { create: { description: `Create a token at \`<group>/<name>\` — \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${JSON.stringify(ref("token", "<group>"))}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.` } }
128
146
  });
@@ -0,0 +1,28 @@
1
+ import { Ref } from "../../framework/utils/refs.js";
2
+ import { z } from "zod";
3
+
4
+ //#region src/entities/system/calc.d.ts
5
+ /** One side of the arithmetic: a number, a CSS length such as `0.25rem`, or a ref to a token. */
6
+ type CalcOperand = number | string | Ref;
7
+ interface Multiply {
8
+ readonly multiply: readonly [CalcOperand, CalcOperand];
9
+ }
10
+ interface Divide {
11
+ readonly divide: readonly [CalcOperand, CalcOperand];
12
+ }
13
+ /** Whether a value is a `multiply(…)`. */
14
+ declare function isMultiply(value: unknown): value is Multiply;
15
+ /** Whether a value is a `divide(…)`. */
16
+ declare function isDivide(value: unknown): value is Divide;
17
+ /** `multiply(Token.ref('spacing'), 4)` — the first operand times the second. */
18
+ declare function multiply(left: CalcOperand, right: CalcOperand): Multiply;
19
+ /** `divide(1.25, 0.875)` — the first operand over the second. */
20
+ declare function divide(left: CalcOperand, right: CalcOperand): Divide;
21
+ /**
22
+ * What the arithmetic comes to, given each operand's literal — `1rem` for `0.25rem` times 4,
23
+ * `1.4286` for `1.25` over `0.875` — or `undefined` when the operands are not plain quantities or
24
+ * their units don't combine. `literal` resolves an operand (a ref to the token's value).
25
+ */
26
+ declare function evaluateCalc(value: Multiply | Divide, literal: (part: CalcOperand) => string | number | undefined): string | number | undefined;
27
+ //#endregion
28
+ export { CalcOperand, Divide, Multiply, divide, evaluateCalc, isDivide, isMultiply, multiply };
@@ -0,0 +1,121 @@
1
+ import { refSchema } from "../../framework/utils/refs.js";
2
+ import { z } from "zod";
3
+ //#region src/entities/system/calc.ts
4
+ /**
5
+ * A value that is arithmetic on others — `multiply(Token.ref('spacing'), 4)` is
6
+ * `calc(var(--spacing) * 4)`, and `divide(1.25, 0.875)` is `calc(1.25 / 0.875)`.
7
+ *
8
+ * Held as its operands rather than as the `calc()` string so a ref in it stays a ref: a rename
9
+ * follows into it, the dependency graph sees it, and a reader can work out what it comes to
10
+ * (`spacing` at `0.25rem` times 4 is `1rem`) where a string would only be text.
11
+ */
12
+ const operand = z.union([
13
+ z.number(),
14
+ z.string().min(1),
15
+ refSchema
16
+ ]);
17
+ const operands = z.tuple([operand, operand]);
18
+ const multiplyValue = z.strictObject({ multiply: operands });
19
+ const divideValue = z.strictObject({ divide: operands });
20
+ const isPair = (value) => Array.isArray(value) && value.length === 2;
21
+ /** Whether a value is a `multiply(…)`. */
22
+ function isMultiply(value) {
23
+ return typeof value === "object" && value !== null && isPair(value.multiply);
24
+ }
25
+ /** Whether a value is a `divide(…)`. */
26
+ function isDivide(value) {
27
+ return typeof value === "object" && value !== null && isPair(value.divide);
28
+ }
29
+ /** `multiply(Token.ref('spacing'), 4)` — the first operand times the second. */
30
+ function multiply(left, right) {
31
+ return { multiply: [left, right] };
32
+ }
33
+ /** `divide(1.25, 0.875)` — the first operand over the second. */
34
+ function divide(left, right) {
35
+ return { divide: [left, right] };
36
+ }
37
+ /** A `multiply(…)` as CSS — `calc(var(--spacing) * 4)` — with `render` resolving each operand. */
38
+ function renderMultiply(value, render) {
39
+ const [left, right] = value.multiply;
40
+ return `calc(${render(left)} * ${render(right)})`;
41
+ }
42
+ /** A `divide(…)` as CSS — `calc(1.25 / 0.875)`. */
43
+ function renderDivide(value, render) {
44
+ const [left, right] = value.divide;
45
+ return `calc(${render(left)} / ${render(right)})`;
46
+ }
47
+ function multiplyAuthoring(value) {
48
+ return {
49
+ helper: "multiply",
50
+ args: [...value.multiply]
51
+ };
52
+ }
53
+ function divideAuthoring(value) {
54
+ return {
55
+ helper: "divide",
56
+ args: [...value.divide]
57
+ };
58
+ }
59
+ const isDigit = (char) => char !== void 0 && char >= "0" && char <= "9";
60
+ /**
61
+ * The plain number a string starts with — an optional minus, digits, and an optional fraction
62
+ * (`-1`, `0.25`, `.5`) — split from what follows it, or `undefined` when it starts with none.
63
+ * A scan rather than a regex, so its cost stays linear on any input.
64
+ */
65
+ function leadingNumber(text) {
66
+ let at = text.startsWith("-") ? 1 : 0;
67
+ const start = at;
68
+ while (isDigit(text[at])) at++;
69
+ const whole = at > start;
70
+ if (text[at] === "." && isDigit(text[at + 1])) {
71
+ at++;
72
+ while (isDigit(text[at])) at++;
73
+ } else if (!whole) return;
74
+ return {
75
+ sign: start === 1 ? "-" : "",
76
+ digits: text.slice(start, at),
77
+ rest: text.slice(at)
78
+ };
79
+ }
80
+ function quantityOf(value) {
81
+ if (typeof value === "number") return {
82
+ amount: value,
83
+ unit: ""
84
+ };
85
+ if (value === void 0) return void 0;
86
+ const number = leadingNumber(value.trim());
87
+ if (!number || !/^[a-z%]*$/i.test(number.rest)) return void 0;
88
+ return {
89
+ amount: Number(`${number.sign}${number.digits}`),
90
+ unit: number.rest
91
+ };
92
+ }
93
+ /**
94
+ * What the arithmetic comes to, given each operand's literal — `1rem` for `0.25rem` times 4,
95
+ * `1.4286` for `1.25` over `0.875` — or `undefined` when the operands are not plain quantities or
96
+ * their units don't combine. `literal` resolves an operand (a ref to the token's value).
97
+ */
98
+ function evaluateCalc(value, literal) {
99
+ const [left, right] = isMultiply(value) ? value.multiply : value.divide;
100
+ const a = quantityOf(literal(left));
101
+ const b = quantityOf(literal(right));
102
+ if (!a || !b) return void 0;
103
+ let result;
104
+ if (isMultiply(value)) {
105
+ if (a.unit && b.unit) return void 0;
106
+ result = {
107
+ amount: a.amount * b.amount,
108
+ unit: a.unit || b.unit
109
+ };
110
+ } else {
111
+ if (b.amount === 0 || b.unit && b.unit !== a.unit) return void 0;
112
+ result = {
113
+ amount: a.amount / b.amount,
114
+ unit: b.unit ? "" : a.unit
115
+ };
116
+ }
117
+ const amount = Math.round(result.amount * 1e4) / 1e4;
118
+ return result.unit ? `${amount}${result.unit}` : amount;
119
+ }
120
+ //#endregion
121
+ export { divide, divideAuthoring, divideValue, evaluateCalc, isDivide, isMultiply, leadingNumber, multiply, multiplyAuthoring, multiplyValue, renderDivide, renderMultiply };
@@ -1,10 +1,32 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  //#region src/entities/system/color.d.ts
4
- /** A colour: a literal, a token ref, a derived colour, or a gradient. */
4
+ /** A colour: a literal, a token ref, an OKLCH colour, a derived colour, or a gradient. */
5
5
  type ColorValue = string | {
6
6
  readonly __ref: string;
7
- } | DerivedColor | Gradient;
7
+ } | OklchColor | DerivedColor | Gradient;
8
+ /**
9
+ * A colour as its OKLCH channels — `oklch(63.7, 0.237, 25.331)` is CSS `oklch(63.7% 0.237 25.331)`.
10
+ *
11
+ * Held as numbers rather than as the CSS string so an editor reads and adjusts the channels
12
+ * themselves: lightness and chroma move without the hue drifting, which is what makes a ramp of them
13
+ * a scale. It renders to the CSS function, so the browser paints it in its own gamut.
14
+ */
15
+ interface OklchColor {
16
+ readonly oklch: {
17
+ /** Lightness, as a percentage (0–100). */readonly l: number; /** Chroma, from 0 (grey) up; sRGB colours stay under about 0.37. */
18
+ readonly c: number; /** Hue in degrees. Absent for a grey, whose hue CSS spells `none`. */
19
+ readonly h?: number; /** Opacity, 0–1. Absent is opaque. */
20
+ readonly alpha?: number;
21
+ };
22
+ }
23
+ /** Whether a value is an OKLCH colour. */
24
+ declare function isOklchColor(value: unknown): value is OklchColor;
25
+ /** `oklch(63.7, 0.237, 25.331)` — lightness as a percentage, chroma, and hue in degrees. A grey
26
+ * leaves the hue out. */
27
+ declare function oklch(l: number, c: number, h?: number, alpha?: number): OklchColor;
28
+ /** An OKLCH colour as CSS: `oklch(63.7% 0.237 25.331)`, `oklch(98.5% 0 none)`, `oklch(… / 0.5)`. */
29
+ declare function renderOklch(value: OklchColor): string;
8
30
  interface DerivedColor {
9
31
  readonly op: 'darken' | 'lighten' | 'alpha' | 'mix';
10
32
  readonly color: ColorValue;
@@ -46,11 +68,14 @@ declare function isDerivedColor(value: unknown): value is DerivedColor;
46
68
  /** The interpolation space each op is authored in: `oklch` for shading (perceptually even), `srgb`
47
69
  * for alpha. */
48
70
  declare const DERIVED_COLOR_SPACE: Record<DerivedColor['op'], string>;
71
+ /** The space a derived colour with no `space` of its own mixes in, on every platform. */
72
+ declare const DERIVED_COLOR_DEFAULT_SPACE = "oklch";
49
73
  /** Blend toward black in light mode and white in dark — so a hover state darkens in both. */
50
74
  declare function darken(color: ColorValue, amount: number): DerivedColor;
51
75
  /** Mirror of {@link darken}: toward white in light mode, black in dark. */
52
76
  declare function lighten(color: ColorValue, amount: number): DerivedColor;
53
- /** `alpha(color, 0.2)` — the colour at 20% opacity. Takes a 0–1 opacity, like CSS. */
77
+ /** `alpha(color, 0.2)` — the colour at 20% opacity. Takes a 0–1 opacity, like CSS, kept to a tenth
78
+ * of a percent. */
54
79
  declare function alpha(color: ColorValue, opacity: number): DerivedColor;
55
80
  /** `mix(a, b, 30)` — 30% of the way from `a` to `b`. */
56
81
  declare function mix(color: ColorValue, towards: ColorValue, amount: number, space?: string): DerivedColor;
@@ -64,4 +89,4 @@ declare function renderDerivedColor(value: DerivedColor, render: (color: ColorVa
64
89
  /** A gradient as CSS — `linear-gradient(to right, transparent 0%, var(--uds-bg-primary) 50%)`. */
65
90
  declare function renderGradient(value: Gradient, render: (color: ColorValue) => string): string;
66
91
  //#endregion
67
- export { ColorValue, DERIVED_COLOR_SPACE, DerivedColor, Gradient, alpha, darken, isDerivedColor, isGradient, lighten, linearGradient, mix, renderDerivedColor, renderGradient };
92
+ export { ColorValue, DERIVED_COLOR_DEFAULT_SPACE, DERIVED_COLOR_SPACE, DerivedColor, Gradient, OklchColor, alpha, darken, isDerivedColor, isGradient, isOklchColor, lighten, linearGradient, mix, oklch, renderDerivedColor, renderGradient, renderOklch };