@yahoo/uds-create-config 5.52.0 → 5.53.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.
@@ -749,11 +749,11 @@ const LinkedSystemDefinition = defineEntity({
749
749
  * `Popover` over the v3 Bridge keeps the product's tokens from the Bridge, and the Popover renders
750
750
  * Yahoo OS's own Button. Once the old link is gone, the new one can become `linked`.
751
751
  *
752
- * OPEN: motion presets and composites don't follow it yet. A taken component plays this system's
753
- * motion preset of the name it uses, and a composite is the source's in a style rule and this
754
- * system's as a prop.
752
+ * The same holds for the motion presets and composites a taken component names: through a
753
+ * `hybrid` link it plays this system's of that name, and otherwise the source's own, emitted
754
+ * under the pin's names.
755
755
  */
756
- type: z.enum(["linked", "hybrid"]).optional().describe("How this source joins the system: `linked` (the default) brings in everything its entities reference and refuses a path another link offers differently; `hybrid` brings in only what no other link offers and uses this system's tokens wherever it has one of the same name."),
756
+ type: z.enum(["linked", "hybrid"]).optional().describe("How this source joins the system: `linked` (the default) brings in everything its entities reference and refuses a path another link offers differently; `hybrid` brings in only what no other link offers and uses this system's tokens, motion presets and composites wherever it has one of the same name."),
757
757
  /**
758
758
  * The exact published version every ref through this link resolves against.
759
759
  *
@@ -3,6 +3,7 @@ import { isRef, refSchema } from "../../framework/utils/refs.js";
3
3
  import { METADATA_KEYS } from "../../framework/utils/field-schema.js";
4
4
  import { pathInput } from "../../framework/schemas.js";
5
5
  import { defineEntity } from "../../framework/defineEntity.js";
6
+ import { drawnMotionBody } from "./drawn-motion.js";
6
7
  import { authoredValue } from "../../framework/projections.js";
7
8
  import { convertMotion } from "./motion-convert.js";
8
9
  import { cssMotionValue, motionTransition, reanimatedMotion, webMotionBody } from "./motion-types.js";
@@ -213,7 +214,7 @@ const Motion = defineEntity({
213
214
  ], { error: "a layer's `motion` is a preset (`Motion.ref('<path>')`) or a JS preset written in place — `{ \"whileHover\": { \"scale\": 1.05 }, \"transition\": { … } }`, the fields `Motion.item` takes with `runtime: 'js'`. A CSS preset is named with `Motion.item`." }),
214
215
  reach: ({ config, value, element }) => {
215
216
  if (isNativeConfig(config)) return "prop";
216
- const preset = config.resolve("motion", String(authoredValue(config, "motion", value)))?.toJSON();
217
+ const preset = drawnMotionBody(config, String(authoredValue(config, "motion", value)));
217
218
  if (preset?.type === "react-native-reanimated") throw new Error("No UDS web motion integration for react-native-reanimated. Select a supported preset or disable motion.");
218
219
  return preset && webMotionBody(preset).runtime === "css" && isRef(element) ? "prop" : "none";
219
220
  },
@@ -107,17 +107,23 @@ declare function componentModuleSource(config: Config, componentPathOrRef: strin
107
107
  *
108
108
  * One view for the browser's module key, the server's build and the CLI's borrowed modules, so the
109
109
  * three name the same classes and hash the same config.
110
+ *
111
+ * The view records the consumer and pin it was built for ({@link moduleViewOf}), so a motion preset
112
+ * its components name is drawn as the consumer draws it. `route` is the pin as the consumer spells
113
+ * it when the owner is reached through another link (`bridge~yos`); `slug` alone names classes.
110
114
  */
111
115
  declare function linkedModuleView({
112
116
  consumer,
113
117
  owner,
114
118
  slug,
115
- shadowed
119
+ shadowed,
120
+ route
116
121
  }: {
117
122
  readonly consumer: Config;
118
123
  readonly owner: Config;
119
124
  readonly slug: string;
120
125
  readonly shadowed: boolean;
126
+ readonly route?: string;
121
127
  }): Config;
122
128
  /**
123
129
  * A linked component's module source, and the config its module is built from.
@@ -1,6 +1,7 @@
1
1
  import { isNativeConfig } from "../../configs/platform.js";
2
2
  import { isRef, kindOf, pathOf, ref, routedSource, sourceHops, splitRef } from "../../framework/utils/refs.js";
3
3
  import { createSliceMemo } from "../../framework/memo.js";
4
+ import { registerModuleView } from "../../framework/module-views.js";
4
5
  import { File } from "./File.js";
5
6
  import { Settings, classPrefixOf, prefixed, varPrefixOf } from "./Settings.js";
6
7
  import { Component } from "./Component.js";
@@ -339,13 +340,17 @@ function componentModuleSource(config, componentPathOrRef) {
339
340
  *
340
341
  * One view for the browser's module key, the server's build and the CLI's borrowed modules, so the
341
342
  * three name the same classes and hash the same config.
343
+ *
344
+ * The view records the consumer and pin it was built for ({@link moduleViewOf}), so a motion preset
345
+ * its components name is drawn as the consumer draws it. `route` is the pin as the consumer spells
346
+ * it when the owner is reached through another link (`bridge~yos`); `slug` alone names classes.
342
347
  */
343
- function linkedModuleView({ consumer, owner, slug, shadowed }) {
348
+ function linkedModuleView({ consumer, owner, slug, shadowed, route = slug }) {
344
349
  const classNames = shadowed ? prefixed({
345
350
  prefix: classPrefixOf(consumer),
346
351
  name: slug
347
352
  }) : classPrefixOf(consumer);
348
- return owner.apply({
353
+ const view = owner.apply({
349
354
  kind: "settings",
350
355
  operation: "update",
351
356
  path: "settings",
@@ -354,6 +359,14 @@ function linkedModuleView({ consumer, owner, slug, shadowed }) {
354
359
  variables: varPrefixOf(owner)
355
360
  } }
356
361
  });
362
+ if (view !== owner) registerModuleView({
363
+ view,
364
+ link: {
365
+ consumer,
366
+ slug: route
367
+ }
368
+ });
369
+ return view;
357
370
  }
358
371
  /** Views are built per consumer, owner, slug and shadowing, since every linked component of one owner
359
372
  * shares one. */
@@ -1,6 +1,7 @@
1
1
  import { isRef, kindOf, pathOf } from "../../framework/utils/refs.js";
2
2
  import { isPlainObject } from "../../framework/utils/field-path.js";
3
3
  import { createSliceMemo } from "../../framework/memo.js";
4
+ import { drawnMotionBody, drawnMotionName } from "./drawn-motion.js";
4
5
  import { convertMotion } from "./motion-convert.js";
5
6
  import { webMotionBody } from "./motion-types.js";
6
7
  import { placedMotions, variantGroupsOf } from "./motion-variants.js";
@@ -31,11 +32,6 @@ function unmountableLayers(config, path) {
31
32
  }
32
33
  return out;
33
34
  }
34
- /** The preset a layer bag's `motion` names, by path. */
35
- function namedPreset(value) {
36
- if (isRef(value)) return pathOf(value);
37
- return typeof value === "string" ? value : void 0;
38
- }
39
35
  /**
40
36
  * Whether a layer's element wears a CSS preset's class: a plain element through its class list, and a
41
37
  * component through the `motion` value its layer is handed, which the component's style-prop runtime
@@ -46,14 +42,18 @@ function wears(element) {
46
42
  return isRef(element) && kindOf(element) === "component";
47
43
  }
48
44
  const resolvePlacements = createSliceMemo({
49
- kinds: ["component", "motion"],
45
+ kinds: [
46
+ "component",
47
+ "motion",
48
+ "linked-system"
49
+ ],
50
50
  compute: (config) => {
51
51
  const out = [];
52
52
  const seen = /* @__PURE__ */ new Set();
53
53
  for (const component of config.list("component")) {
54
54
  const { layers = {}, styles = {} } = component.toJSON();
55
55
  for (const entry of Object.values(styles)) for (const [layer, bag] of Object.entries(entry?.layers ?? {})) {
56
- const preset = namedPreset(isPlainObject(bag) ? bag.motion : void 0);
56
+ const preset = drawnMotionName(config, isPlainObject(bag) ? bag.motion : void 0);
57
57
  if (preset === void 0) continue;
58
58
  if (motionBodyAt(config, preset)?.runtime !== "css" || !(layer in layers)) continue;
59
59
  const key = `${component.path}\u0000${layer}\u0000${preset}`;
@@ -80,14 +80,22 @@ function cssMotionPlacements(config) {
80
80
  * variants its states name only to presets in its own group.
81
81
  */
82
82
  const cssMotionVariantGroups = createSliceMemo({
83
- kinds: ["component", "motion"],
83
+ kinds: [
84
+ "component",
85
+ "motion",
86
+ "linked-system"
87
+ ],
84
88
  compute: (config) => variantGroupsOf({
85
89
  config,
86
90
  placements: cssMotionPlacements(config)
87
91
  })
88
92
  });
89
93
  const resolveExits = createSliceMemo({
90
- kinds: ["component", "motion"],
94
+ kinds: [
95
+ "component",
96
+ "motion",
97
+ "linked-system"
98
+ ],
91
99
  compute: (config) => {
92
100
  const out = [];
93
101
  const seen = /* @__PURE__ */ new Set();
@@ -95,7 +103,7 @@ const resolveExits = createSliceMemo({
95
103
  const { styles = {} } = component.toJSON();
96
104
  let unmountable;
97
105
  for (const entry of Object.values(styles)) for (const [layer, bag] of Object.entries(entry?.layers ?? {})) {
98
- const preset = namedPreset(isPlainObject(bag) ? bag.motion : void 0);
106
+ const preset = drawnMotionName(config, isPlainObject(bag) ? bag.motion : void 0);
99
107
  if (preset === void 0) continue;
100
108
  const body = motionBodyAt(config, preset);
101
109
  if (body?.runtime !== "css" || body.exit === void 0) continue;
@@ -193,7 +201,7 @@ function linkedMotionSwaps(config) {
193
201
  return resolveLinkedSwaps(config);
194
202
  }
195
203
  function motionBodyAt(config, path) {
196
- const body = config.resolve("motion", path)?.toJSON();
204
+ const body = drawnMotionBody(config, path);
197
205
  return body ? webMotionBody(body) : void 0;
198
206
  }
199
207
  //#endregion
@@ -33,7 +33,11 @@ interface CssMotionKeyframeList {
33
33
  /** Where the list sits, as a dotted path: `variants.show.scale`. */
34
34
  readonly at: string;
35
35
  }
36
- /** Every CSS preset that animates states, by path, with the timing of each value per destination. */
36
+ /**
37
+ * Every CSS preset that animates states, by the name it plays under, with the timing of each value per
38
+ * destination: this config's presets, and each linked source's it plays under the pin's name
39
+ * ({@link linkedMotionPresets}), whose tokens are read in the source.
40
+ */
37
41
  declare function cssMotionTransitions(config: Config): Readonly<Record<string, CssMotionTransitions>>;
38
42
  /**
39
43
  * How long a CSS preset's `exit` takes, in milliseconds: the longest of its values' durations and
@@ -1,5 +1,6 @@
1
1
  import { isPlainObject } from "../../framework/utils/field-path.js";
2
2
  import { createSliceMemo } from "../../framework/memo.js";
3
+ import { drawnMotionBody, linkedMotionPresets } from "./drawn-motion.js";
3
4
  import { cssEasing, easingCurve, linearEasing, springTiming } from "./css-timing.js";
4
5
  import { webMotionBody } from "./motion-types.js";
5
6
  import { stateVariantNames } from "./motion-variants.js";
@@ -418,10 +419,31 @@ const resolveCssMotionPresets = createSliceMemo({
418
419
  };
419
420
  }
420
421
  });
421
- /** Every CSS preset that animates states, by path, with the timing of each value per destination. */
422
+ /**
423
+ * Every CSS preset that animates states, by the name it plays under, with the timing of each value per
424
+ * destination: this config's presets, and each linked source's it plays under the pin's name
425
+ * ({@link linkedMotionPresets}), whose tokens are read in the source.
426
+ */
422
427
  function cssMotionTransitions(config) {
423
- return resolveCssMotionPresets(config).transitions;
428
+ const own = resolveCssMotionPresets(config).transitions;
429
+ const linked = linkedMotionPresets(config);
430
+ if (linked.length === 0) return own;
431
+ const hit = withLinkedTransitions.get(config);
432
+ if (hit) return hit;
433
+ const out = { ...own };
434
+ for (const preset of linked) {
435
+ const body = webMotionBody(preset.body);
436
+ if (body.runtime !== "css" || !hasStates(body)) continue;
437
+ out[preset.name] = transitionsOf({
438
+ config: preset.source,
439
+ body,
440
+ report: () => {}
441
+ });
442
+ }
443
+ withLinkedTransitions.set(config, out);
444
+ return out;
424
445
  }
446
+ const withLinkedTransitions = /* @__PURE__ */ new WeakMap();
425
447
  /**
426
448
  * How long a CSS preset's `exit` takes, in milliseconds: the longest of its values' durations and
427
449
  * delays as the element changes to its exit state. `0` for a preset with no exit, and a timing that is
@@ -443,7 +465,7 @@ function cssMotionTokens(config) {
443
465
  return resolveCssMotionPresets(config).tokens;
444
466
  }
445
467
  function motionBodyAt(config, path) {
446
- const body = config.resolve("motion", path)?.toJSON();
468
+ const body = drawnMotionBody(config, path);
447
469
  return body ? webMotionBody(body) : void 0;
448
470
  }
449
471
  //#endregion
@@ -0,0 +1,29 @@
1
+ import { Config } from "../../framework/Config.js";
2
+
3
+ //#region src/entities/system/drawn-motion.d.ts
4
+ /** A linked source's preset this config plays under the pin's name. */
5
+ interface LinkedMotionPreset {
6
+ /** `<path>@<slug>`: the name a motion value plays it by. */
7
+ readonly name: string;
8
+ readonly slug: string;
9
+ /** Its path in the source. */
10
+ readonly path: string;
11
+ readonly body: Record<string, unknown>;
12
+ /** The source, which the tokens its body names resolve in. */
13
+ readonly source: Config;
14
+ }
15
+ /**
16
+ * The name the preset a `motion` value names plays under in `config`: a path of `config`'s own, or a
17
+ * source's preset as `<path>@<slug>`. A module view reads its owner's names, and draws them as the
18
+ * consumer it was built for does.
19
+ */
20
+ declare function drawnMotionName(config: Config, value: unknown): string | undefined;
21
+ /** The body of the preset `name` plays ({@link drawnMotionName}), from whichever config holds it. */
22
+ declare function drawnMotionBody(config: Config, name: string): Record<string, unknown> | undefined;
23
+ /**
24
+ * Every linked preset `config` plays under its pin's name: each preset a reached source holds that
25
+ * this config's refs reach, unless the link draws it with a row of this config's own.
26
+ */
27
+ declare function linkedMotionPresets(config: Config): readonly LinkedMotionPreset[];
28
+ //#endregion
29
+ export { LinkedMotionPreset, drawnMotionBody, drawnMotionName, linkedMotionPresets };
@@ -0,0 +1,77 @@
1
+ import { isRef, pathOf, ref, routedSource, sourceOf } from "../../framework/utils/refs.js";
2
+ import { moduleViewOf } from "../../framework/module-views.js";
3
+ import { linkedLocalRow } from "../../framework/projections.js";
4
+ //#region src/entities/system/drawn-motion.ts
5
+ /**
6
+ * The name the preset a `motion` value names plays under in `config`: a path of `config`'s own, or a
7
+ * source's preset as `<path>@<slug>`. A module view reads its owner's names, and draws them as the
8
+ * consumer it was built for does.
9
+ */
10
+ function drawnMotionName(config, value) {
11
+ let named;
12
+ if (isRef(value)) named = value.__ref;
13
+ else if (typeof value === "string") named = value;
14
+ if (named === void 0) return void 0;
15
+ const path = pathOf(named);
16
+ const slug = sourceOf(named);
17
+ const view = moduleViewOf(config);
18
+ if (!view) return slug === void 0 ? path : linkedName({
19
+ config,
20
+ slug,
21
+ path
22
+ });
23
+ return linkedName({
24
+ config: view.consumer,
25
+ slug: slug === void 0 ? view.slug : routedSource(view.slug, slug),
26
+ path
27
+ });
28
+ }
29
+ /** The body of the preset `name` plays ({@link drawnMotionName}), from whichever config holds it. */
30
+ function drawnMotionBody(config, name) {
31
+ const at = moduleViewOf(config)?.consumer ?? config;
32
+ return (sourceOf(name) === void 0 ? at.resolve("motion", name) : at.resolveRef(ref("motion", name)))?.toJSON();
33
+ }
34
+ function linkedName({ config, slug, path }) {
35
+ return linkedLocalRow({
36
+ config,
37
+ slug,
38
+ kind: "motion",
39
+ path
40
+ }) ?? `${path}@${slug}`;
41
+ }
42
+ const linkedPresets = /* @__PURE__ */ new WeakMap();
43
+ /**
44
+ * Every linked preset `config` plays under its pin's name: each preset a reached source holds that
45
+ * this config's refs reach, unless the link draws it with a row of this config's own.
46
+ */
47
+ function linkedMotionPresets(config) {
48
+ const hit = linkedPresets.get(config);
49
+ if (hit) return hit;
50
+ const out = [];
51
+ for (const slug of config.reachedSlugs()) {
52
+ const source = config.sourceFor(slug);
53
+ if (!source) continue;
54
+ for (const target of config.sourceClosure(slug, "motion")) {
55
+ const path = pathOf(target);
56
+ if (linkedLocalRow({
57
+ config,
58
+ slug,
59
+ kind: "motion",
60
+ path
61
+ }) !== void 0) continue;
62
+ const body = source.resolve("motion", path)?.toJSON();
63
+ if (body) out.push({
64
+ name: `${path}@${slug}`,
65
+ slug,
66
+ path,
67
+ body,
68
+ source
69
+ });
70
+ }
71
+ }
72
+ out.sort((a, b) => a.name.localeCompare(b.name));
73
+ linkedPresets.set(config, out);
74
+ return out;
75
+ }
76
+ //#endregion
77
+ export { drawnMotionBody, drawnMotionName, linkedMotionPresets };
@@ -34,9 +34,10 @@ declare function hasJsMotion(config: Config): boolean;
34
34
  /** The tokens JS presets name that a preset cannot carry as one value, for the build to report. */
35
35
  declare function motionPresetTokens(config: Config): MotionPresetToken[];
36
36
  /**
37
- * Each preset's own `reducedMotion`, the default for a layer that plays it and sets none, by path:
38
- * the named presets of either runtime, and the JS presets a style rule writes in place by their id.
37
+ * Each preset's own `reducedMotion`, the default for a layer that plays it and sets none, by the name
38
+ * it plays under: the named presets of either runtime, each linked source's this config plays under
39
+ * the pin's name, and the JS presets a style rule writes in place by their id.
39
40
  */
40
- declare const presetReducedMotions: (config: Config) => Readonly<Record<string, ReducedMotion>>;
41
+ declare function presetReducedMotions(config: Config): Readonly<Record<string, ReducedMotion>>;
41
42
  //#endregion
42
43
  export { MOTION_PACKAGE, MotionPresetToken, hasJsMotion, inlineMotionProps, jsMotionPresets, motionPresetTokens, presetReducedMotions };
@@ -1,6 +1,7 @@
1
1
  import { collectRefs, isRef, kindOf, pathOf, refOfKey } from "../../framework/utils/refs.js";
2
2
  import { composedValueFormOf } from "./composed-values.js";
3
3
  import { createSliceMemo } from "../../framework/memo.js";
4
+ import { linkedMotionPresets } from "./drawn-motion.js";
4
5
  import { tokenValueRow } from "./token-values.js";
5
6
  import { MOTION_EASING_NAMES, bezierPoints, motionOfCssKeyword } from "./easings.js";
6
7
  import { webMotionBody } from "./motion-types.js";
@@ -241,7 +242,27 @@ const resolveJsMotionPresets = createSliceMemo({
241
242
  * a style rule sets is the one exception: it reaches `motion/react` as its `var()`.
242
243
  */
243
244
  function jsMotionPresets(config) {
244
- return resolveJsMotionPresets(config).presets;
245
+ return withLinkedPresets(config).presets;
246
+ }
247
+ const linkedJsPresets = /* @__PURE__ */ new WeakMap();
248
+ /**
249
+ * {@link resolveJsMotionPresets}, with each linked source's JS preset this config plays under the
250
+ * pin's name ({@link linkedMotionPresets}), its tokens read in the source.
251
+ */
252
+ function withLinkedPresets(config) {
253
+ const own = resolveJsMotionPresets(config);
254
+ const linked = linkedMotionPresets(config).filter((preset) => webMotionBody(preset.body).type === "motion/react");
255
+ if (linked.length === 0) return own;
256
+ const hit = linkedJsPresets.get(config);
257
+ if (hit) return hit;
258
+ const presets = { ...own.presets };
259
+ for (const preset of linked) presets[preset.name] = resolveBody(preset.source, preset.name, webMotionBody(preset.body), []);
260
+ const out = {
261
+ ...own,
262
+ presets
263
+ };
264
+ linkedJsPresets.set(config, out);
265
+ return out;
245
266
  }
246
267
  /** Every JS preset a style rule writes in place, by its id, resolved as {@link jsMotionPresets} are. */
247
268
  function inlineMotionProps(config) {
@@ -252,7 +273,7 @@ const MOTION_PACKAGE = "motion";
252
273
  /** Whether anything in the system animates through `motion/react`: a JS preset, named or written in
253
274
  * place in a style rule. */
254
275
  function hasJsMotion(config) {
255
- const { presets, inline } = resolveJsMotionPresets(config);
276
+ const { presets, inline } = withLinkedPresets(config);
256
277
  return Object.keys(presets).length > 0 || Object.keys(inline).length > 0;
257
278
  }
258
279
  /** The tokens JS presets name that a preset cannot carry as one value, for the build to report. */
@@ -260,10 +281,28 @@ function motionPresetTokens(config) {
260
281
  return resolveJsMotionPresets(config).tokens;
261
282
  }
262
283
  /**
263
- * Each preset's own `reducedMotion`, the default for a layer that plays it and sets none, by path:
264
- * the named presets of either runtime, and the JS presets a style rule writes in place by their id.
284
+ * Each preset's own `reducedMotion`, the default for a layer that plays it and sets none, by the name
285
+ * it plays under: the named presets of either runtime, each linked source's this config plays under
286
+ * the pin's name, and the JS presets a style rule writes in place by their id.
265
287
  */
266
- const presetReducedMotions = createSliceMemo({
288
+ function presetReducedMotions(config) {
289
+ const own = ownReducedMotions(config);
290
+ const linked = linkedMotionPresets(config).flatMap((preset) => {
291
+ const { reducedMotion } = webMotionBody(preset.body);
292
+ return isReducedMotion(reducedMotion) ? [[preset.name, reducedMotion]] : [];
293
+ });
294
+ if (linked.length === 0) return own;
295
+ const hit = linkedReducedMotions.get(config);
296
+ if (hit) return hit;
297
+ const out = {
298
+ ...own,
299
+ ...Object.fromEntries(linked)
300
+ };
301
+ linkedReducedMotions.set(config, out);
302
+ return out;
303
+ }
304
+ const linkedReducedMotions = /* @__PURE__ */ new WeakMap();
305
+ const ownReducedMotions = createSliceMemo({
267
306
  kinds: ["motion", "component"],
268
307
  compute: (config) => {
269
308
  const out = {};
@@ -2,6 +2,7 @@ import { reject } from "../../framework/rejection.js";
2
2
  import { isRef, kindOf, pathOf } from "../../framework/utils/refs.js";
3
3
  import { isPlainObject } from "../../framework/utils/field-path.js";
4
4
  import { createSliceMemo } from "../../framework/memo.js";
5
+ import { drawnMotionBody, drawnMotionName } from "./drawn-motion.js";
5
6
  import { webMotionBody } from "./motion-types.js";
6
7
  import { inlineJsMotion } from "./Motion.js";
7
8
  //#region src/entities/system/motion-variants.ts
@@ -90,14 +91,18 @@ function namedVariants(body) {
90
91
  }
91
92
  /** Every JS preset a style rule puts on a layer, across the system. */
92
93
  const placedMotions = createSliceMemo({
93
- kinds: ["component", "motion"],
94
+ kinds: [
95
+ "component",
96
+ "motion",
97
+ "linked-system"
98
+ ],
94
99
  compute: (config) => {
95
100
  const out = [];
96
101
  for (const component of config.list("component")) {
97
102
  const { styles = {} } = component.toJSON();
98
103
  for (const [rule, entry] of Object.entries(styles)) for (const [layer, bag] of Object.entries(entry?.layers ?? {})) {
99
104
  const named = bag?.motion;
100
- const preset = isRef(named) ? pathOf(named.__ref) : typeof named === "string" ? named : isInlineMotion(named) ? inlineMotionId(component.path, rule, layer) : void 0;
105
+ const preset = isInlineMotion(named) ? inlineMotionId(component.path, rule, layer) : drawnMotionName(config, named);
101
106
  if (preset === void 0 || !jsBody(config, preset)) continue;
102
107
  out.push({
103
108
  component: component.path,
@@ -123,7 +128,11 @@ const composedComponents = (config, path) => {
123
128
  * nothing joins nothing, which keeps Button and Checkbox apart although both render Box.
124
129
  */
125
130
  const motionVariantGroups = createSliceMemo({
126
- kinds: ["component", "motion"],
131
+ kinds: [
132
+ "component",
133
+ "motion",
134
+ "linked-system"
135
+ ],
127
136
  compute: (config) => variantGroupsOf({
128
137
  config,
129
138
  placements: placedMotions(config)
@@ -283,7 +292,7 @@ function orphanedMotionVariants(config) {
283
292
  return out;
284
293
  }
285
294
  function motionBodyAt(config, path) {
286
- const body = config.resolve("motion", path)?.toJSON();
295
+ const body = drawnMotionBody(config, path);
287
296
  return body ? webMotionBody(body) : void 0;
288
297
  }
289
298
  function renamePresetBody(config, path, body, from, to) {
@@ -138,9 +138,18 @@ declare function componentRuleClasses(config: Config, path: string): ComponentRu
138
138
  * composite contributes no declarations there, so omitting it would drop the class entirely.
139
139
  */
140
140
  declare function componentCompositeClasses(config: Config, path: string): ComponentRuleClass[];
141
- /** The class a CSS motion preset is applied by. Namespaced, so it doesn't share a space with the
142
- * style-prop utilities; the `@keyframes` it runs keep their own name. */
141
+ /**
142
+ * The class a CSS motion preset is applied by. Namespaced, so it doesn't share a space with the
143
+ * style-prop utilities; the `@keyframes` it runs keep their own name ({@link motionKeyframesName}).
144
+ *
145
+ * `path` is a name {@link drawnMotionName} gives: a linked source's preset played under the pin's
146
+ * name is qualified by the pin (`uds-ds-motion-fade` for `fade@ds`), and a module view's names are
147
+ * its consumer's classes.
148
+ */
143
149
  declare function motionClassName(config: Config, path: string): string;
150
+ /** The `@keyframes` a CSS preset runs: `uds-fade`, or `uds-ds-fade` for a linked source's preset
151
+ * played under the pin's name. */
152
+ declare function motionKeyframesName(config: Config, path: string): string;
144
153
  /**
145
154
  * Every MOTION class a component's style rules apply — a layer bag naming a preset (`motion: 'spin'`).
146
155
  *
@@ -207,6 +216,46 @@ declare function componentPropComposites(config: Config, path: string, state?: R
207
216
  * plain-tag layer (which declares nothing) and a key naming no member are left as they are.
208
217
  */
209
218
  declare function inlineRuleComposites(config: Config, element: unknown, authored: Record<string, unknown>): Record<string, unknown>;
219
+ /**
220
+ * The composites a rule's bag names that are drawn with another config's row, taken out of the bag.
221
+ *
222
+ * A linked component's rule is read against its source, and a `hybrid` link draws a composite it
223
+ * names with the consuming config's row of that name. `drawnWith` answers which row that is, as a
224
+ * member path in `local`, and the member's styles are that row's, so they have to be rendered against
225
+ * `local` while the rest of the rule is rendered against the source. The marker is the source's, which
226
+ * the component's own `_on<Group><Member>` rules query. `bag` is what's left for
227
+ * {@link inlineRuleComposites}.
228
+ */
229
+ declare function splitRuleComposites({
230
+ config,
231
+ element,
232
+ bag: authored,
233
+ local,
234
+ drawnWith
235
+ }: {
236
+ readonly config: Config;
237
+ readonly element: unknown;
238
+ readonly bag: Record<string, unknown>;
239
+ readonly local: Config;
240
+ readonly drawnWith: (member: string) => string | undefined;
241
+ }): {
242
+ readonly bag: Record<string, unknown>;
243
+ readonly drawn: Record<string, unknown> | undefined;
244
+ };
245
+ /**
246
+ * A composite member's styles as a bag, with the composite's marker
247
+ * (`--<prefix>-composite-<group>: <member>`) that `_on<Group><Member>` finds it by, under the prefix
248
+ * of `markedIn` (`config` unless given).
249
+ */
250
+ declare function compositeMemberBag({
251
+ config,
252
+ path,
253
+ markedIn
254
+ }: {
255
+ readonly config: Config;
256
+ readonly path: string;
257
+ readonly markedIn?: Config;
258
+ }): Record<string, unknown>;
210
259
  /**
211
260
  * The props a style rule's bag sends to each layer's ELEMENT rather than to the stylesheet, for one
212
261
  * instance. The same split the emitted module makes when it builds a layer's routed bundle.
@@ -388,4 +437,4 @@ declare function modifierUtilitiesUsed(config: Config): Set<string>;
388
437
  */
389
438
  declare function splitVariants(cls: string): string[];
390
439
  //#endregion
391
- export { ComponentRuleClass, PickedComposite, SplitStyleProps, arbitraryClassName, arbitraryNeedsClass, classLeaf, classSelector, componentClassBase, componentCompositeClasses, componentLayerClass, componentMotionClasses, componentPropClasses, componentPropComposites, componentRuleClasses, componentStateAttribute, componentStateAttributes, forceModeAttribute, forceModeAttributeOf, forceModeProp, forceModePropGroup, forceModePropValue, forceModeProps, forceStateAttribute, forceStateSelector, inlineRuleComposites, isForceModeProp, kebabComponent, layerJsMotionPresets, layerRoutedProps, modifierUtilitiesUsed, motionClassName, ruleCondition, splitStyleProps, splitVariants, stylePropClassBase, stylePropClassName, stylePropClasses, stylePropOffScaleLiteral, stylePropOwnClasses, styleRuleMotionClasses };
440
+ export { ComponentRuleClass, PickedComposite, SplitStyleProps, arbitraryClassName, arbitraryNeedsClass, classLeaf, classSelector, componentClassBase, componentCompositeClasses, componentLayerClass, componentMotionClasses, componentPropClasses, componentPropComposites, componentRuleClasses, componentStateAttribute, componentStateAttributes, compositeMemberBag, forceModeAttribute, forceModeAttributeOf, forceModeProp, forceModePropGroup, forceModePropValue, forceModeProps, forceStateAttribute, forceStateSelector, inlineRuleComposites, isForceModeProp, kebabComponent, layerJsMotionPresets, layerRoutedProps, modifierUtilitiesUsed, motionClassName, motionKeyframesName, ruleCondition, splitRuleComposites, splitStyleProps, splitVariants, stylePropClassBase, stylePropClassName, stylePropClasses, stylePropOffScaleLiteral, stylePropOwnClasses, styleRuleMotionClasses };