@yahoo/uds-create-config 4.16.0 → 4.18.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 (52) hide show
  1. package/dist/configs/CanvasConfig.d.ts +98 -0
  2. package/dist/configs/react-native-system.d.ts +75 -8
  3. package/dist/configs/system.d.ts +82 -12
  4. package/dist/configs/system.js +5 -1
  5. package/dist/css/root-font-size.d.ts +16 -0
  6. package/dist/css/root-font-size.js +97 -0
  7. package/dist/css/value-type.js +5 -1
  8. package/dist/entities/native/NativeModifier.d.ts +1 -1
  9. package/dist/entities/native/NativeSettings.d.ts +1 -0
  10. package/dist/entities/native/NativeToken.d.ts +4 -3
  11. package/dist/entities/system/Component.d.ts +50 -0
  12. package/dist/entities/system/Component.js +8 -1
  13. package/dist/entities/system/Modifier.d.ts +2 -2
  14. package/dist/entities/system/Modifier.js +2 -5
  15. package/dist/entities/system/Settings.d.ts +3 -0
  16. package/dist/entities/system/Settings.js +12 -1
  17. package/dist/entities/system/StyleProperty.js +2 -3
  18. package/dist/entities/system/Token.d.ts +21 -8
  19. package/dist/entities/system/Token.js +24 -5
  20. package/dist/entities/system/color.d.ts +4 -1
  21. package/dist/entities/system/color.js +59 -5
  22. package/dist/entities/system/composed-values.d.ts +36 -0
  23. package/dist/entities/system/composed-values.js +52 -0
  24. package/dist/entities/system/style-bag.d.ts +2 -2
  25. package/dist/entities/system/style-bag.js +3 -5
  26. package/dist/entities/system/token-index.js +1 -1
  27. package/dist/entities/system/token-values.d.ts +138 -0
  28. package/dist/entities/system/token-values.js +154 -0
  29. package/dist/entities/system/transition.d.ts +35 -0
  30. package/dist/entities/system/transition.js +57 -0
  31. package/dist/framework/Config.d.ts +1 -1
  32. package/dist/framework/Config.js +1 -1
  33. package/dist/framework/defineEntity.d.ts +1 -1
  34. package/dist/framework/defineEntity.js +2 -5
  35. package/dist/framework/projections.js +2 -3
  36. package/dist/framework/schema-version.d.ts +1 -1
  37. package/dist/framework/schema-version.js +5 -5
  38. package/dist/framework/utils/identifier.d.ts +8 -0
  39. package/dist/framework/utils/identifier.js +62 -0
  40. package/dist/framework/utils/refs.d.ts +7 -1
  41. package/dist/framework/utils/refs.js +10 -1
  42. package/dist/index.d.ts +9 -4
  43. package/dist/index.js +10 -5
  44. package/dist/migrations/2.0.0/raw-css.js +1 -1
  45. package/dist/migrations/2.0.0/v1-artifact.d.ts +42 -7
  46. package/dist/migrations/2.0.0/v1-artifact.js +1 -1
  47. package/dist/migrations/20260923144958_transition_token_value.d.ts +13 -0
  48. package/dist/migrations/20260923144958_transition_token_value.js +10 -0
  49. package/dist/migrations/20260923160425_render_token_refs.d.ts +14 -0
  50. package/dist/migrations/20260923160425_render_token_refs.js +10 -0
  51. package/dist/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +2 -2
@@ -0,0 +1,16 @@
1
+ //#region src/css/root-font-size.d.ts
2
+ /** A root font size that parses, and the pixels one `rem` is when the value fixes one. */
3
+ interface RootFontSize {
4
+ /** As written. */
5
+ readonly css: string;
6
+ /** The pixels one `rem` is: a `px` value as is, a relative one (`%`, `em`, `rem`) against the
7
+ * browser's 16px default. `undefined` for a value that fixes none — a fluid `clamp(…)`, a
8
+ * viewport unit, a keyword. */
9
+ readonly px: number | undefined;
10
+ }
11
+ /**
12
+ * The value parsed, or `undefined` when it is not a `font-size` a stylesheet can carry.
13
+ */
14
+ declare function parseRootFontSize(value: string): RootFontSize | undefined;
15
+ //#endregion
16
+ export { RootFontSize, parseRootFontSize };
@@ -0,0 +1,97 @@
1
+ import { isLengthUnit } from "./value-type.js";
2
+ import parse from "css-tree/parser";
3
+ /** The `font-size` keywords, which fix no pixel value a build can name. */
4
+ const KEYWORDS = new Set([
5
+ "xx-small",
6
+ "x-small",
7
+ "small",
8
+ "medium",
9
+ "large",
10
+ "x-large",
11
+ "xx-large",
12
+ "xxx-large",
13
+ "smaller",
14
+ "larger",
15
+ "inherit",
16
+ "initial",
17
+ "unset"
18
+ ]);
19
+ /** The functions a `font-size` may be, each of which fixes no pixel value a build can name. */
20
+ const FUNCTIONS = new Set([
21
+ "calc",
22
+ "clamp",
23
+ "min",
24
+ "max",
25
+ "var",
26
+ "env"
27
+ ]);
28
+ /**
29
+ * The value parsed, or `undefined` when it is not a `font-size` a stylesheet can carry.
30
+ */
31
+ function parseRootFontSize(value) {
32
+ const css = value.trim();
33
+ if (css === "") return void 0;
34
+ if (!balanced(css)) return void 0;
35
+ let malformed = false;
36
+ let parsed;
37
+ try {
38
+ parsed = parse(css, {
39
+ context: "value",
40
+ onParseError: () => {
41
+ malformed = true;
42
+ }
43
+ });
44
+ } catch {
45
+ return;
46
+ }
47
+ if (malformed || parsed.type !== "Value") return void 0;
48
+ const [node, ...rest] = parsed.children.toArray();
49
+ if (!node || rest.length > 0) return void 0;
50
+ switch (node.type) {
51
+ case "Dimension": {
52
+ if (!isLengthUnit(node.unit.toLowerCase())) return void 0;
53
+ const amount = Number(node.value);
54
+ const unit = node.unit.toLowerCase();
55
+ if (unit === "px") return {
56
+ css,
57
+ px: amount
58
+ };
59
+ if (unit === "em" || unit === "rem") return {
60
+ css,
61
+ px: amount * 16
62
+ };
63
+ return {
64
+ css,
65
+ px: void 0
66
+ };
67
+ }
68
+ case "Percentage": return {
69
+ css,
70
+ px: Number(node.value) / 100 * 16
71
+ };
72
+ case "Number": return Number(node.value) === 0 ? {
73
+ css,
74
+ px: 0
75
+ } : void 0;
76
+ case "Identifier": return KEYWORDS.has(node.name.toLowerCase()) ? {
77
+ css,
78
+ px: void 0
79
+ } : void 0;
80
+ case "Function": return FUNCTIONS.has(node.name.toLowerCase()) ? {
81
+ css,
82
+ px: void 0
83
+ } : void 0;
84
+ default: return;
85
+ }
86
+ }
87
+ function balanced(css) {
88
+ let depth = 0;
89
+ for (const char of css) {
90
+ if (char === "(") depth += 1;
91
+ if (char === ")") depth -= 1;
92
+ if (depth < 0) return false;
93
+ }
94
+ return depth === 0;
95
+ }
96
+ //#endregion
97
+ export { parseRootFontSize };
@@ -29,6 +29,10 @@ const EASING_FUNCTIONS = [
29
29
  "linear("
30
30
  ];
31
31
  /** Absolute and relative length units, longest-first so `rem` wins over `em`. */
32
+ /** Whether `unit` is a CSS length unit, `rem` and `vw` alike. */
33
+ function isLengthUnit(unit) {
34
+ return LENGTH_UNITS.includes(unit);
35
+ }
32
36
  const LENGTH_UNITS = [
33
37
  "ch",
34
38
  "cm",
@@ -128,4 +132,4 @@ function resolveValueType({ type, value }) {
128
132
  return sniffValueType(value);
129
133
  }
130
134
  //#endregion
131
- export { resolveValueType, sniffValueType };
135
+ export { isLengthUnit, resolveValueType, sniffValueType };
@@ -14,8 +14,8 @@ declare const NativeModifier: EntityClass<z.ZodObject<{
14
14
  value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
15
15
  }, z.core.$strip>, z.ZodObject<{
16
16
  type: z.ZodOptional<z.ZodEnum<{
17
- state: "state";
18
17
  mode: "mode";
18
+ state: "state";
19
19
  }>>;
20
20
  default: z.ZodOptional<z.ZodObject<{
21
21
  __ref: z.ZodString;
@@ -29,6 +29,7 @@ declare const NativeSettings: EntityClass<z.ZodObject<Omit<{
29
29
  }, z.core.$strict>]>>;
30
30
  scope: z.ZodOptional<z.ZodString>;
31
31
  preflight: z.ZodOptional<z.ZodBoolean>;
32
+ rem: z.ZodOptional<z.ZodString>;
32
33
  safelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
33
34
  }, z.core.$strict>>;
34
35
  playground: z.ZodOptional<z.ZodObject<{
@@ -2,18 +2,19 @@ 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 { EntityClass } from "../../framework/defineEntity.js";
5
- import { DerivedColor, Gradient } from "../system/color.js";
5
+ import { ComposedValue } from "../system/composed-values.js";
6
+ import { DerivedColor } from "../system/color.js";
6
7
  import { OverrideCondition } from "../system/Token.js";
7
8
  import { z } from "zod";
8
9
 
9
10
  //#region src/entities/native/NativeToken.d.ts
10
11
  declare const nativeTokenValue: z.ZodType<number | boolean | string | Ref$1 | DerivedColor>;
11
12
  declare const NativeToken: EntityClass<z.ZodObject<Omit<{
12
- value: z.ZodType<string | number | Ref | DerivedColor | Gradient, unknown, z.core.$ZodTypeInternals<string | number | Ref | DerivedColor | Gradient, unknown>>;
13
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
13
14
  type: z.ZodOptional<z.ZodString>;
14
15
  overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
15
16
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
16
- value: z.ZodType<string | number | Ref | DerivedColor | Gradient, unknown, z.core.$ZodTypeInternals<string | number | Ref | DerivedColor | Gradient, unknown>>;
17
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
17
18
  }, z.core.$strip>>>;
18
19
  }, "value" | "overrides"> & {
19
20
  value: z.ZodType<string | number | boolean | Ref | DerivedColor, unknown, z.core.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
@@ -10,7 +10,9 @@ import { StoredPreviewCondition } from "../../framework/preview-store.js";
10
10
  import { listSlots, namedSlots, slotHostedBy, slotOf } from "../../spec/slot.js";
11
11
  import { cellAt } from "../../spec/cell.js";
12
12
  import { instanceAt } from "../../spec/instance.js";
13
+ import { ComposedValue } from "./composed-values.js";
13
14
  import { StyleValue } from "./style-bag.js";
15
+ import { OverrideCondition, TokenRefPath, TokenReference } from "./Token.js";
14
16
  import { AsInput, LayerMap, PhantomCallProps, PhantomReadProps, PropMap, RenderArgs, StyleRuleInput } from "./defineComponent.js";
15
17
  import { z } from "zod";
16
18
 
@@ -718,6 +720,22 @@ declare const componentFields: z.ZodObject<{
718
720
  __ref: z.ZodString;
719
721
  }, z.core.$strip>>;
720
722
  export: z.ZodOptional<z.ZodString>;
723
+ tokenRefs: z.ZodOptional<z.ZodArray<Omit<EntityClass<z.ZodObject<{
724
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
725
+ type: z.ZodOptional<z.ZodString>;
726
+ overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
727
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
728
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
729
+ }, z.core.$strip>>>;
730
+ }, z.core.$strip>, z.ZodObject<{
731
+ cssPrefix: z.ZodOptional<z.ZodString>;
732
+ }, z.core.$strip>, "token", {
733
+ readonly create: {
734
+ readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
735
+ };
736
+ }, Record<never, never>, false, Record<never, never>, Record<never, never>>, "ref"> & {
737
+ readonly ref: <const Path extends TokenRefPath>(path: Path) => TokenReference<Path>;
738
+ }>>;
721
739
  }, z.core.$strict>;
722
740
  /**
723
741
  * A component's stored body — every field, typed off the schema above.
@@ -906,6 +924,22 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
906
924
  __ref: z.ZodString;
907
925
  }, z.core.$strip>>;
908
926
  export: z.ZodOptional<z.ZodString>;
927
+ tokenRefs: z.ZodOptional<z.ZodArray<Omit<EntityClass<z.ZodObject<{
928
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
929
+ type: z.ZodOptional<z.ZodString>;
930
+ overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
931
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
932
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
933
+ }, z.core.$strip>>>;
934
+ }, z.core.$strip>, z.ZodObject<{
935
+ cssPrefix: z.ZodOptional<z.ZodString>;
936
+ }, z.core.$strip>, "token", {
937
+ readonly create: {
938
+ readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
939
+ };
940
+ }, Record<never, never>, false, Record<never, never>, Record<never, never>>, "ref"> & {
941
+ readonly ref: <const Path extends TokenRefPath>(path: Path) => TokenReference<Path>;
942
+ }>>;
909
943
  }, z.core.$strict>, z.ZodObject<{
910
944
  icon: z.ZodOptional<z.ZodString>;
911
945
  }, z.core.$strip>, "component", {
@@ -1078,6 +1112,22 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
1078
1112
  __ref: z.ZodString;
1079
1113
  }, z.core.$strip>>;
1080
1114
  export: z.ZodOptional<z.ZodString>;
1115
+ tokenRefs: z.ZodOptional<z.ZodArray<Omit<EntityClass<z.ZodObject<{
1116
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
1117
+ type: z.ZodOptional<z.ZodString>;
1118
+ overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
1119
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
1120
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
1121
+ }, z.core.$strip>>>;
1122
+ }, z.core.$strip>, z.ZodObject<{
1123
+ cssPrefix: z.ZodOptional<z.ZodString>;
1124
+ }, z.core.$strip>, "token", {
1125
+ readonly create: {
1126
+ readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
1127
+ };
1128
+ }, Record<never, never>, false, Record<never, never>, Record<never, never>>, "ref"> & {
1129
+ readonly ref: <const Path extends TokenRefPath>(path: Path) => TokenReference<Path>;
1130
+ }>>;
1081
1131
  }, z.core.$strict>;
1082
1132
  readonly description: "Create a component at a new qualified `path`. For a component with nothing in it yet, one to design on the canvas before it has props or variants, use `create-blank` with only a `path` instead of authoring a minimal body here. When its complete body is known, include all layers, anatomy, props, defaults, and styles in this one operation. `data.layers` maps each stable layer name to the element it renders and must include `root` — a tag for a primitive (`{ \"layers\": { \"root\": \"div\" } }`), and for anything built on top, a ref to a primitive (`{ \"root\": { \"__ref\": \"component:Box\" }, \"label\": { \"__ref\": \"component:Text\" } }`), which is what lets the layer take that primitive's style props. A primitive exposes style props as `{ \"type\": \"styleProperty\", \"value\": { \"__ref\": \"style-property:bg\" } }` and declares `children: { \"type\": \"slot\", \"text\": true }` when it holds content — without the slot, nothing nested inside it renders. A composed component exposes a layer's props at its own top level through `forwards`, keyed by layer: `{ \"forwards\": { \"root\": \"*\" } }` passes every prop the root's element takes (so `<Button width=\"full\">` reaches the root's `width`), and a list of member refs (`[{ \"__ref\": \"component:Box#props/width\" }]`, each naming the component that declares the prop) passes only those. A prop a layer already exposes is forwarded, never redeclared in `props`; a `props` entry of `{ \"type\": \"forward\" }` is the reverse route, one of this component's props into a layer's. `anatomy` is the tree the build would read off a render: each node renders a layer (`{ \"__ref\": \"layer:<name>\" }`), an intrinsic tag, or `Slot` (`{ \"element\": \"Slot\", \"props\": { \"name\": \"children\" } }`, where a slot prop's content lands — but a declared slot fills `root` by default, so a primitive still declares its `children` slot prop yet needs no `Slot` node or `children` entry for it, and a `Slot` node is only for landing that content in a nested layer); `children` lists sibling node keys from the `root` node, never a prop name; `text` is a fixed string; `visible` is a condition on a prop (`{ \"$state\": \"<prop>\" }`, or with `eq`); content shown in a nested layer is a route, `children: { \"type\": \"forward\", \"target\": \"label/children\" }`. `$state` appears only in `visible` and in an `element` a prop chooses, never as a prop value. A glyph is a node of its own, `{ \"element\": { \"__ref\": \"icon:<library>/<Name>\" } }`, listed in the `children` of the layer node that holds it, never a prop value. Text a prop supplies to a text layer is never a node either: declare the prop `{ \"type\": \"forward\", \"target\": \"<layer>/children\" }` and the layer shows it; a `Slot` node goes only under a layer that takes nodes. A style rule is `\"styles\": { \"*\": { \"when\": null, \"layers\": { \"root\": { \"bg\": \"surface\" } } } }`, keyed by a name, with each layer's bag under `layers`. A composed component carries its own styles: give it an unconditional (`\"*\"`) rule that lays out and surfaces it through the system's style properties — spacing, alignment and gap on the container, a background and color where it has one — rather than leaning on the bare primitives, which paint flat. Style a layer only with the style props the primitive it renders exposes, set to the values those props accept: `component/get` on the primitive lists both, so read it and choose from that rather than reaching for a CSS name or value the system has no style property for. Set `preview` (`{ \"*\": { \"when\": null, \"props\": { \"children\": \"Example\" } } }`) or `defaultProps` so the component previews with content; a root that takes children renders empty otherwise. A style rule's layer bag is keyed by style-prop name (`fontWeight`, `color`), never by CSS property name, and each value is one of that prop's values.";
1083
1133
  readonly example: ({
@@ -19,6 +19,7 @@ import { instanceAt } from "../../spec/instance.js";
19
19
  import { Package } from "./Package.js";
20
20
  import { File } from "./File.js";
21
21
  import { iconDomainInvariants } from "./icon-domain-invariants.js";
22
+ import { Token } from "./Token.js";
22
23
  import { intoName, routedPropIn, routesContent } from "../../framework/prop-surface.js";
23
24
  import { z } from "zod";
24
25
  //#region src/entities/system/Component.ts
@@ -1444,7 +1445,13 @@ const componentFields = z.strictObject({
1444
1445
  * two components, and a borrower registers the publisher's `Card` under its own name. Nothing
1445
1446
  * recovers that binding from a path.
1446
1447
  */
1447
- export: z.string().optional()
1448
+ export: z.string().optional(),
1449
+ /**
1450
+ * The tokens the render reads as values — `Token.ref('spacing/2.5').px()` — baked from the source
1451
+ * like `file`. Each is an edge: a rename reaches the read, a delete names the component, and a
1452
+ * link's closure carries the token. A read whose path a prop completes names the group.
1453
+ */
1454
+ tokenRefs: z.array(Token).optional()
1448
1455
  }).check((ctx) => {
1449
1456
  const body = ctx.value;
1450
1457
  if (isPlainObject(body.props)) for (const [name, decl] of Object.entries(body.props)) {
@@ -27,8 +27,8 @@ type WebActivation = (typeof WEB_ACTIVATIONS)[number];
27
27
  */
28
28
  declare const modifierGroupFields: z.ZodObject<{
29
29
  type: z.ZodOptional<z.ZodEnum<{
30
- state: "state";
31
30
  mode: "mode";
31
+ state: "state";
32
32
  }>>;
33
33
  default: z.ZodOptional<z.ZodObject<{
34
34
  __ref: z.ZodString;
@@ -49,8 +49,8 @@ declare const Modifier: EntityClass<z.ZodObject<{
49
49
  }, z.core.$strip>, z.ZodObject<Record<never, never>, z.core.$strip>, "composite", Record<never, never>, Record<never, never>, false, Record<never, never>, Record<never, never>>>]>;
50
50
  }, z.core.$strip>, z.ZodObject<{
51
51
  type: z.ZodOptional<z.ZodEnum<{
52
- state: "state";
53
52
  mode: "mode";
53
+ state: "state";
54
54
  }>>;
55
55
  default: z.ZodOptional<z.ZodObject<{
56
56
  __ref: z.ZodString;
@@ -1,4 +1,4 @@
1
- import { isRef, kindOf } from "../../framework/utils/refs.js";
1
+ import { isRef, refTo } from "../../framework/utils/refs.js";
2
2
  import { defineEntity } from "../../framework/defineEntity.js";
3
3
  import { Composite } from "./Composite.js";
4
4
  import { z } from "zod";
@@ -14,10 +14,7 @@ const WEB_ACTIVATIONS = [
14
14
  "composite"
15
15
  ];
16
16
  /** A ref to a modifier, spelled out because the kind's own class is what this is a field of. */
17
- const modifierRef = z.object({ __ref: z.string().refine((raw) => kindOf(raw) === "modifier") }).meta({
18
- ref: true,
19
- refKind: "modifier"
20
- });
17
+ const modifierRef = refTo("modifier");
21
18
  /**
22
19
  * A modifier group's own fields.
23
20
  *
@@ -29,6 +29,7 @@ type CssPrefix = z.output<typeof prefix>;
29
29
  declare const CSS_DEFAULTS: {
30
30
  readonly prefix: "uds";
31
31
  readonly preflight: true;
32
+ readonly rem: "16px";
32
33
  };
33
34
  /** The `css` section — how a web system names what it emits and where its stylesheet applies. A
34
35
  * native system declares no such section, so a native config setting one is refused. */
@@ -39,6 +40,7 @@ declare const CssSection: SubEntityClass<z.ZodObject<{
39
40
  }, z.core.$strict>]>>;
40
41
  scope: z.ZodOptional<z.ZodString>;
41
42
  preflight: z.ZodOptional<z.ZodBoolean>;
43
+ rem: z.ZodOptional<z.ZodString>;
42
44
  safelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
43
45
  }, z.core.$strict>>;
44
46
  /** The two namespaces a prefix names, whichever form it was written in, defaulted when unset. */
@@ -83,6 +85,7 @@ declare const Settings: EntityClass<z.ZodObject<{
83
85
  }, z.core.$strict>]>>;
84
86
  scope: z.ZodOptional<z.ZodString>;
85
87
  preflight: z.ZodOptional<z.ZodBoolean>;
88
+ rem: z.ZodOptional<z.ZodString>;
86
89
  safelist: z.ZodOptional<z.ZodArray<z.ZodString>>;
87
90
  }, z.core.$strict>>;
88
91
  playground: z.ZodOptional<z.ZodObject<{
@@ -1,4 +1,5 @@
1
1
  import { buildFields } from "../../configs/build-options.js";
2
+ import { parseRootFontSize } from "../../css/root-font-size.js";
2
3
  import { defineEntity } from "../../framework/defineEntity.js";
3
4
  import { defineSubEntity } from "../../framework/defineSubEntity.js";
4
5
  import { z } from "zod";
@@ -61,7 +62,8 @@ const prefix = z.union([z.string(), z.strictObject({
61
62
  /** The `css` section as a config that set nothing reads it, spelled once. */
62
63
  const CSS_DEFAULTS = {
63
64
  prefix: "uds",
64
- preflight: true
65
+ preflight: true,
66
+ rem: "16px"
65
67
  };
66
68
  const cssFields = z.strictObject({
67
69
  prefix: prefix.meta({
@@ -81,6 +83,15 @@ const cssFields = z.strictObject({
81
83
  description: "Start the stylesheet with the element resets, so buttons, inputs and headings begin from the same baseline in every browser. Off, the page keeps its own base styles. Unset, on.",
82
84
  default: CSS_DEFAULTS.preflight
83
85
  }).optional(),
86
+ /** The root font size of the page this sheet is for, as CSS — what one `rem` is. Stated, the app's
87
+ * sheet sets it and a token's `px()` reads a `rem` value against it; a fixed size (`16px`, `62.5%`,
88
+ * `1em`) folds to pixels against the browser's 16px default, a fluid one (`clamp(…)`) leaves a
89
+ * rem with no pixel reading. A sheet confined to a `scope` cannot set it, and states it only. */
90
+ rem: z.string().refine((value) => parseRootFontSize(value) !== void 0, { error: "a root font size is one CSS `font-size` value: a length (`16px`, `1em`), a percentage (`62.5%`), a math or `var()` function (`clamp(14px, 1vw + 12px, 18px)`), or a font-size keyword." }).meta({
91
+ title: "Rem",
92
+ description: `The root font size of the page this stylesheet is for, as CSS: \`16px\`, \`62.5%\`, \`1em\`, or a fluid \`clamp(…)\`. Stated, the stylesheet sets \`html { font-size }\` to it unless it is confined to a \`scope\`, and a token's \`px()\` reads a rem value against it — a fixed size folds against the browser's 16px default, a fluid one leaves rem values with no pixel reading. Unset, ${CSS_DEFAULTS.rem} is assumed and the page's root is left alone.`,
93
+ default: CSS_DEFAULTS.rem
94
+ }).optional(),
84
95
  /** Classes the stylesheet keeps whether or not a scanned file names them — for a class built at
85
96
  * runtime, or one used by a file the build does not scan. */
86
97
  safelist: z.array(z.string().min(1)).meta({
@@ -2,6 +2,7 @@ import { isRef, kindOf, pathOf, ref, refSchema, sourceOf, splitRef } from "../..
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
+ import { isIdentifier } from "../../framework/utils/identifier.js";
5
6
  import { z } from "zod";
6
7
  //#region src/entities/system/StyleProperty.ts
7
8
  /**
@@ -9,15 +10,13 @@ import { z } from "zod";
9
10
  * The forms a `values` entry takes come first, each a zod schema built from its context, and the
10
11
  * entity's fields read them.
11
12
  */
12
- /** A style property's path is the JSX prop a component exposes, so each segment is an identifier. */
13
- const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
14
13
  /** A token group's ref. Through the framework helper rather than `Token.ref`: `Token` reaches this
15
14
  * module through `Modifier` and `Composite`, so importing it here is a cycle. */
16
15
  const tokenGroup = (group) => ref("token", group);
17
16
  /** A token ref as a model writes it, spelled from the ref itself so the prose can't drift. */
18
17
  const spelled = (path) => JSON.stringify(tokenGroup(path));
19
18
  const stylePropertyPath = z.string().check((ctx) => {
20
- const bad = ctx.value.split("/").find((segment) => !IDENTIFIER.test(segment));
19
+ const bad = ctx.value.split("/").find((segment) => !isIdentifier(segment));
21
20
  if (bad === void 0) return;
22
21
  ctx.issues.push({
23
22
  code: "custom",
@@ -1,8 +1,10 @@
1
1
  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
- import { EntityClass } from "../../framework/defineEntity.js";
5
- import { DerivedColor, Gradient } from "./color.js";
4
+ import { PathOf } from "../../framework/registered.js";
5
+ import { EntityClass, KindRef } from "../../framework/defineEntity.js";
6
+ import { ComposedValue } from "./composed-values.js";
7
+ import { TokenReaders } from "./token-values.js";
6
8
  import { z } from "zod";
7
9
 
8
10
  //#region src/entities/system/Token.d.ts
@@ -40,22 +42,22 @@ declare function defineOverride<TValue extends z.ZodTypeAny>(value: TValue): Sub
40
42
  value: TValue;
41
43
  }, z.core.$strip>>;
42
44
  declare const tokenFields: z.ZodObject<{
43
- value: z.ZodType<string | number | Ref | DerivedColor | Gradient, unknown, z.core.$ZodTypeInternals<string | number | Ref | DerivedColor | Gradient, unknown>>;
45
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
44
46
  type: z.ZodOptional<z.ZodString>;
45
47
  overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
46
48
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
47
- value: z.ZodType<string | number | Ref | DerivedColor | Gradient, unknown, z.core.$ZodTypeInternals<string | number | Ref | DerivedColor | Gradient, unknown>>;
49
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
48
50
  }, z.core.$strip>>>;
49
51
  }, z.core.$strip>;
50
52
  /** A token's stored body — what a `create` writes and what `Token.get` hands back typed. Exported
51
53
  * so a consumer building one names the entity's own shape instead of restating it. */
52
54
  type TokenBody = z.infer<typeof tokenFields>;
53
- declare const Token: EntityClass<z.ZodObject<{
54
- value: z.ZodType<string | number | Ref | DerivedColor | Gradient, unknown, z.core.$ZodTypeInternals<string | number | Ref | DerivedColor | Gradient, unknown>>;
55
+ declare const TokenEntity: EntityClass<z.ZodObject<{
56
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
55
57
  type: z.ZodOptional<z.ZodString>;
56
58
  overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
57
59
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
58
- value: z.ZodType<string | number | Ref | DerivedColor | Gradient, unknown, z.core.$ZodTypeInternals<string | number | Ref | DerivedColor | Gradient, unknown>>;
60
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
59
61
  }, z.core.$strip>>>;
60
62
  }, z.core.$strip>, z.ZodObject<{
61
63
  cssPrefix: z.ZodOptional<z.ZodString>;
@@ -64,5 +66,16 @@ declare const Token: EntityClass<z.ZodObject<{
64
66
  readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
65
67
  };
66
68
  }, Record<never, never>, false, Record<never, never>, Record<never, never>>;
69
+ /** A token ref as an authoring file holds it: the marker, `extend`, and the value readers. */
70
+ type TokenReference<Path extends string> = KindRef<typeof tokenFields, 'token', Path> & TokenReaders<Path>;
71
+ /**
72
+ * What `Token.ref` takes in an authoring file: a registered path, or a path a prop completes under a
73
+ * registered group — `` `spacing/${gutter}` `` — whose readers stay open since no one token is named.
74
+ */
75
+ type TokenRefPath = PathOf<'token'> | `${PathOf<'token'>}/${string}`;
76
+ declare function tokenRef<const Path extends TokenRefPath>(path: Path): TokenReference<Path>;
77
+ declare const Token: Omit<typeof TokenEntity, "ref"> & {
78
+ readonly ref: typeof tokenRef;
79
+ };
67
80
  //#endregion
68
- export { OverrideCondition, Token, TokenBody, defineOverride, overrideCondition, overrideKey, overrideKeyModifiers, overrideModifiers };
81
+ export { OverrideCondition, Token, TokenBody, TokenRefPath, TokenReference, defineOverride, overrideCondition, overrideKey, overrideKeyModifiers, overrideModifiers };
@@ -1,9 +1,10 @@
1
1
  import { isRef, pathOf, ref, sourceOf } from "../../framework/utils/refs.js";
2
2
  import { defineEntity } from "../../framework/defineEntity.js";
3
3
  import { defineSubEntity } from "../../framework/defineSubEntity.js";
4
- import { derivedColor, gradient } from "./color.js";
4
+ import { composedValueSchemas } from "./composed-values.js";
5
5
  import { Font } from "./Font.js";
6
6
  import { Modifier } from "./Modifier.js";
7
+ import { TOKEN_READERS } from "./token-values.js";
7
8
  import { z } from "zod";
8
9
  //#region src/entities/system/Token.ts
9
10
  /**
@@ -28,9 +29,8 @@ const tokenValue = z.union([
28
29
  z.number(),
29
30
  z.lazy(() => Token),
30
31
  Font,
31
- derivedColor,
32
- gradient
33
- ], { error: "a token's `value` is one CSS value as a string (`#1DB954`, `4px`, `\"Circular\", sans-serif`), a number, a ref to another token or to a font (`{ \"__ref\": \"font:<name>\" }`), a derived color (`{ \"op\": \"darken\", \"color\": { \"__ref\": \"token:color/brand\" }, \"amount\": 10 }`) or a gradient. It is required; a font family is a string value or a `font` entity, never a field of its own." });
32
+ ...composedValueSchemas
33
+ ], { error: "a token's `value` is one CSS value as a string (`#1DB954`, `4px`, `\"Circular\", sans-serif`), a number, a ref to another token or to a font (`{ \"__ref\": \"font:<name>\" }`), a derived color (`{ \"op\": \"darken\", \"color\": { \"__ref\": \"token:color/brand\" }, \"amount\": 10 }`), a gradient, or a transition (`{ \"transition\": [{ \"properties\": [\"background-color\"], \"timing\": { \"__ref\": \"token:motion/color\" } }] }`). It is required; a font family is a string value or a `font` entity, never a field of its own." });
34
34
  /**
35
35
  * What an override applies under: one modifier, or several that all hold at once (`dark` and `lg`
36
36
  * together). Each is a ref, so the override is an ordinary edge: the dependency graph sees every
@@ -113,7 +113,7 @@ function defineOverride(value) {
113
113
  });
114
114
  }
115
115
  const Override = defineOverride(tokenValue);
116
- const Token = defineEntity({
116
+ const TokenEntity = defineEntity({
117
117
  kind: "token",
118
118
  fields: z.object({
119
119
  value: tokenValue,
@@ -125,5 +125,24 @@ const Token = defineEntity({
125
125
  subEntities: { overrides: Override },
126
126
  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.` } }
127
127
  });
128
+ /**
129
+ * Token refs add the value readers a render calls — `Token.ref('spacing/2.5').px()`. Phantoms, like a
130
+ * component ref's `render`: the build resolves each read in place and the authoring file's own call is
131
+ * never made, so calling one anyway names what happened. Non-enumerable, so the marker a config
132
+ * evaluates to is the same small wire form a hydrated config receives.
133
+ */
134
+ const baseTokenRef = TokenEntity.ref.bind(TokenEntity);
135
+ function tokenRef(path) {
136
+ const value = baseTokenRef(path);
137
+ for (const reader of TOKEN_READERS) Object.defineProperty(value, reader, {
138
+ enumerable: false,
139
+ configurable: false,
140
+ value: () => {
141
+ throw new Error(`[uds] Token.ref(${JSON.stringify(path)}).${reader}() was called from an authoring file. The build resolves a token read in place, and a render only ever runs from the module it emits.`);
142
+ }
143
+ });
144
+ return value;
145
+ }
146
+ const Token = Object.assign(TokenEntity, { ref: tokenRef });
128
147
  //#endregion
129
148
  export { Token, defineOverride, overrideCondition, overrideKey, overrideKeyModifiers, overrideModifiers };
@@ -43,6 +43,9 @@ declare function isGradient(value: unknown): value is Gradient;
43
43
  declare function linearGradient(spec: Omit<Gradient, 'gradient'>): Gradient;
44
44
  /** Whether a value is a derived colour — the test the renderers branch on. */
45
45
  declare function isDerivedColor(value: unknown): value is DerivedColor;
46
+ /** The interpolation space each op is authored in: `oklch` for shading (perceptually even), `srgb`
47
+ * for alpha. */
48
+ declare const DERIVED_COLOR_SPACE: Record<DerivedColor['op'], string>;
46
49
  /** Blend toward black in light mode and white in dark — so a hover state darkens in both. */
47
50
  declare function darken(color: ColorValue, amount: number): DerivedColor;
48
51
  /** Mirror of {@link darken}: toward white in light mode, black in dark. */
@@ -61,4 +64,4 @@ declare function renderDerivedColor(value: DerivedColor, render: (color: ColorVa
61
64
  /** A gradient as CSS — `linear-gradient(to right, transparent 0%, var(--uds-bg-primary) 50%)`. */
62
65
  declare function renderGradient(value: Gradient, render: (color: ColorValue) => string): string;
63
66
  //#endregion
64
- export { ColorValue, DerivedColor, Gradient, alpha, darken, isDerivedColor, isGradient, lighten, linearGradient, mix, renderDerivedColor, renderGradient };
67
+ export { ColorValue, DERIVED_COLOR_SPACE, DerivedColor, Gradient, alpha, darken, isDerivedColor, isGradient, lighten, linearGradient, mix, renderDerivedColor, renderGradient };
@@ -63,13 +63,21 @@ const derivedColor = z.lazy(() => z.strictObject({
63
63
  function isDerivedColor(value) {
64
64
  return typeof value === "object" && value !== null && typeof value.op === "string" && "color" in value;
65
65
  }
66
+ /** The interpolation space each op is authored in: `oklch` for shading (perceptually even), `srgb`
67
+ * for alpha. */
68
+ const DERIVED_COLOR_SPACE = {
69
+ darken: "oklch",
70
+ lighten: "oklch",
71
+ alpha: "srgb",
72
+ mix: "oklch"
73
+ };
66
74
  /** Blend toward black in light mode and white in dark — so a hover state darkens in both. */
67
75
  function darken(color, amount) {
68
76
  return {
69
77
  op: "darken",
70
78
  color,
71
79
  amount,
72
- space: "oklch"
80
+ space: DERIVED_COLOR_SPACE.darken
73
81
  };
74
82
  }
75
83
  /** Mirror of {@link darken}: toward white in light mode, black in dark. */
@@ -78,7 +86,7 @@ function lighten(color, amount) {
78
86
  op: "lighten",
79
87
  color,
80
88
  amount,
81
- space: "oklch"
89
+ space: DERIVED_COLOR_SPACE.lighten
82
90
  };
83
91
  }
84
92
  /** `alpha(color, 0.2)` — the colour at 20% opacity. Takes a 0–1 opacity, like CSS. */
@@ -87,11 +95,11 @@ function alpha(color, opacity) {
87
95
  op: "alpha",
88
96
  color,
89
97
  amount: Math.round(Math.max(0, Math.min(1, opacity)) * 100),
90
- space: "srgb"
98
+ space: DERIVED_COLOR_SPACE.alpha
91
99
  };
92
100
  }
93
101
  /** `mix(a, b, 30)` — 30% of the way from `a` to `b`. */
94
- function mix(color, towards, amount, space = "oklch") {
102
+ function mix(color, towards, amount, space = DERIVED_COLOR_SPACE.mix) {
95
103
  return {
96
104
  op: "mix",
97
105
  color,
@@ -101,6 +109,52 @@ function mix(color, towards, amount, space = "oklch") {
101
109
  };
102
110
  }
103
111
  /**
112
+ * The helper call that authors a stored derived colour, or `undefined` for one no helper produces (a
113
+ * `darken` in another space, an `alpha` at a fractional percent). `alpha` stores a 0–100 percent and
114
+ * takes a 0–1 opacity, so the argument is the percent scaled back. A `darken`, `lighten` or `mix`
115
+ * with no `space` renders in `oklch`, so it is the helper's own value; an `alpha` with none renders
116
+ * in `oklch` too, which is not what `alpha()` writes, so it stays an object.
117
+ */
118
+ function derivedColorAuthoring(value) {
119
+ if (value.op === "darken" || value.op === "lighten") {
120
+ if ((value.space ?? DERIVED_COLOR_SPACE[value.op]) !== DERIVED_COLOR_SPACE[value.op] || value.with !== void 0) return;
121
+ return {
122
+ helper: value.op,
123
+ args: [value.color, value.amount]
124
+ };
125
+ }
126
+ if (value.op === "alpha") {
127
+ const whole = Number.isInteger(value.amount);
128
+ const inRange = value.amount >= 0 && value.amount <= 100;
129
+ if (value.space !== DERIVED_COLOR_SPACE.alpha || value.with !== void 0 || !whole || !inRange) return;
130
+ return {
131
+ helper: "alpha",
132
+ args: [value.color, value.amount / 100]
133
+ };
134
+ }
135
+ if (value.with === void 0) return void 0;
136
+ const args = [
137
+ value.color,
138
+ value.with,
139
+ value.amount
140
+ ];
141
+ if (value.space !== void 0 && value.space !== DERIVED_COLOR_SPACE.mix) args.push(value.space);
142
+ return {
143
+ helper: "mix",
144
+ args
145
+ };
146
+ }
147
+ /** The `linearGradient({...})` call that authors a stored gradient; a radial or conic one has no
148
+ * helper and prints as the object it is. */
149
+ function gradientAuthoring(value) {
150
+ if (value.gradient !== "linear") return void 0;
151
+ const { gradient: _kind, ...spec } = value;
152
+ return {
153
+ helper: "linearGradient",
154
+ args: [spec]
155
+ };
156
+ }
157
+ /**
104
158
  * A derived colour as CSS.
105
159
  *
106
160
  * `render` resolves the nested colours — passed in rather than imported, because resolving a token
@@ -120,4 +174,4 @@ function renderGradient(value, render) {
120
174
  return `${value.repeating ? "repeating-" : ""}${value.gradient}-gradient(${[...head, stops].join(", ")})`;
121
175
  }
122
176
  //#endregion
123
- export { alpha, darken, derivedColor, gradient, isDerivedColor, isGradient, lighten, linearGradient, mix, renderDerivedColor, renderGradient };
177
+ export { DERIVED_COLOR_SPACE, alpha, darken, derivedColor, derivedColorAuthoring, gradient, gradientAuthoring, isDerivedColor, isGradient, lighten, linearGradient, mix, renderDerivedColor, renderGradient };