@yahoo/uds-create-config 5.25.2 → 5.27.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 (58) hide show
  1. package/dist/configs/react-native-system.d.ts +22 -4
  2. package/dist/configs/system.js +4 -0
  3. package/dist/entities/native/NativeToken.d.ts +22 -4
  4. package/dist/entities/native/NativeToken.js +2 -1
  5. package/dist/entities/system/Component.d.ts +35 -2
  6. package/dist/entities/system/Component.js +224 -9
  7. package/dist/entities/system/LinkedSystem.js +3 -2
  8. package/dist/entities/system/Settings.js +1 -1
  9. package/dist/entities/system/StyleProperty.js +1 -1
  10. package/dist/entities/system/Token.d.ts +22 -4
  11. package/dist/entities/system/Token.js +4 -3
  12. package/dist/entities/system/component-module.d.ts +17 -1
  13. package/dist/entities/system/component-module.js +54 -2
  14. package/dist/entities/system/defineComponent.d.ts +53 -13
  15. package/dist/entities/system/defineComponent.js +1 -0
  16. package/dist/entities/system/forward-route-invariants.js +1 -1
  17. package/dist/entities/system/icon-domain-invariants.js +1 -1
  18. package/dist/entities/system/root-layer-invariants.js +1 -1
  19. package/dist/entities/system/token-values.js +1 -1
  20. package/dist/framework/Config.d.ts +1 -1
  21. package/dist/framework/Config.js +33 -2
  22. package/dist/framework/class-names.d.ts +27 -1
  23. package/dist/framework/class-names.js +56 -3
  24. package/dist/framework/component-states.d.ts +40 -0
  25. package/dist/framework/component-states.js +53 -0
  26. package/dist/framework/config-ops.d.ts +1 -1
  27. package/dist/framework/config-ops.js +25 -4
  28. package/dist/framework/defineSubEntity.d.ts +7 -0
  29. package/dist/framework/defineSubEntity.js +9 -0
  30. package/dist/framework/interaction-conditions.d.ts +48 -0
  31. package/dist/framework/interaction-conditions.js +104 -0
  32. package/dist/framework/layer-styles.d.ts +5 -1
  33. package/dist/framework/layer-styles.js +18 -4
  34. package/dist/framework/projections.js +44 -2
  35. package/dist/framework/render-spec.js +14 -4
  36. package/dist/framework/schema-version.d.ts +1 -1
  37. package/dist/framework/schema-version.js +5 -5
  38. package/dist/framework/utils/deprecated.d.ts +18 -0
  39. package/dist/framework/utils/deprecated.js +12 -0
  40. package/dist/framework/validate-spec.js +14 -0
  41. package/dist/index.d.ts +5 -3
  42. package/dist/index.js +6 -4
  43. package/dist/migrations/20261013154037_component_states.d.ts +19 -0
  44. package/dist/migrations/20261013154037_component_states.js +74 -0
  45. package/dist/migrations/20261013164300_token_overrides_list.d.ts +7 -0
  46. package/dist/migrations/20261013164300_token_overrides_list.js +10 -0
  47. package/dist/renderer/componentEntry.d.ts +7 -1
  48. package/dist/renderer/componentEntry.js +30 -1
  49. package/dist/renderer/forced-stamp.d.ts +26 -0
  50. package/dist/renderer/forced-stamp.js +50 -0
  51. package/dist/renderer/index.d.ts +3 -1
  52. package/dist/renderer/index.js +3 -1
  53. package/dist/renderer/use-forced-stamp.d.ts +11 -0
  54. package/dist/renderer/use-forced-stamp.js +50 -0
  55. package/dist/spec/specToJsx.d.ts +6 -0
  56. package/dist/spec/specToJsx.js +5 -0
  57. package/dist/tsconfig.tsbuildinfo +1 -1
  58. package/package.json +2 -2
@@ -69,10 +69,19 @@ declare const ReactNativeSystemDefinition: ConfigClass<{
69
69
  tokens: EntityClass<_$zod.ZodObject<Omit<{
70
70
  value: _$zod.ZodType<string | number | Ref | ComposedValue, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
71
71
  type: _$zod.ZodOptional<_$zod.ZodString>;
72
- overrides: _$zod.ZodOptional<SubEntityClass<_$zod.ZodObject<{
72
+ overrides: _$zod.ZodOptional<_$zod.ZodUnion<readonly [_$zod.ZodPipe<_$zod.ZodArray<_$zod.ZodObject<{
73
73
  on: _$zod.ZodType<OverrideCondition, unknown, _$zod_v4_core0.$ZodTypeInternals<OverrideCondition, unknown>>;
74
74
  value: _$zod.ZodType<string | number | Ref | ComposedValue, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
75
- }, _$zod_v4_core0.$strip>>>;
75
+ }, _$zod_v4_core0.$strip>>, _$zod.ZodTransform<Record<string, {
76
+ on: OverrideCondition;
77
+ value: string | number | Ref | ComposedValue;
78
+ }>, {
79
+ on: OverrideCondition;
80
+ value: string | number | Ref | ComposedValue;
81
+ }[]>>, SubEntityClass<_$zod.ZodObject<{
82
+ on: _$zod.ZodType<OverrideCondition, unknown, _$zod_v4_core0.$ZodTypeInternals<OverrideCondition, unknown>>;
83
+ value: _$zod.ZodType<string | number | Ref | ComposedValue, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
84
+ }, _$zod_v4_core0.$strip>>]>>;
76
85
  utility: _$zod.ZodOptional<_$zod.ZodBoolean>;
77
86
  property: _$zod.ZodOptional<_$zod.ZodObject<{
78
87
  syntax: _$zod.ZodOptional<_$zod.ZodString>;
@@ -81,10 +90,19 @@ declare const ReactNativeSystemDefinition: ConfigClass<{
81
90
  cssVariable: _$zod.ZodOptional<_$zod.ZodUnion<readonly [_$zod.ZodString, _$zod.ZodLiteral<false>]>>;
82
91
  }, "value" | "property" | "overrides" | "cssVariable"> & {
83
92
  value: _$zod.ZodType<string | number | boolean | Ref | DerivedColor, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
84
- overrides: _$zod.ZodOptional<SubEntityClass<_$zod.ZodObject<{
93
+ overrides: _$zod.ZodOptional<_$zod.ZodUnion<readonly [_$zod.ZodPipe<_$zod.ZodArray<_$zod.ZodObject<{
94
+ on: _$zod.ZodType<OverrideCondition, unknown, _$zod_v4_core0.$ZodTypeInternals<OverrideCondition, unknown>>;
95
+ value: _$zod.ZodType<string | number | boolean | Ref | DerivedColor, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
96
+ }, _$zod_v4_core0.$strip>>, _$zod.ZodTransform<Record<string, {
97
+ on: OverrideCondition;
98
+ value: string | number | boolean | Ref | DerivedColor;
99
+ }>, {
100
+ on: OverrideCondition;
101
+ value: string | number | boolean | Ref | DerivedColor;
102
+ }[]>>, SubEntityClass<_$zod.ZodObject<{
85
103
  on: _$zod.ZodType<OverrideCondition, unknown, _$zod_v4_core0.$ZodTypeInternals<OverrideCondition, unknown>>;
86
104
  value: _$zod.ZodType<string | number | boolean | Ref | DerivedColor, unknown, _$zod_v4_core0.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
87
- }, _$zod_v4_core0.$strip>>>;
105
+ }, _$zod_v4_core0.$strip>>]>>;
88
106
  }, _$zod_v4_core0.$strip>, _$zod.ZodObject<Omit<{
89
107
  cssPrefix: _$zod.ZodOptional<_$zod.ZodString>;
90
108
  cssVariables: _$zod.ZodOptional<_$zod.ZodBoolean>;
@@ -66,6 +66,8 @@ import { startingStyleModifierMigration } from "../migrations/20261009022722_sta
66
66
  import { cssMotionStrictMigration } from "../migrations/20261009181635_css_motion_strict.js";
67
67
  import { linkOverrideRefMigration } from "../migrations/20261009193004_link_override_ref.js";
68
68
  import { linkedComponentModulesMigration } from "../migrations/20261012200606_linked_component_modules.js";
69
+ import { componentStatesMigration } from "../migrations/20261013154037_component_states.js";
70
+ import { tokenOverridesListMigration } from "../migrations/20261013164300_token_overrides_list.js";
69
71
  const System = defineConfig({
70
72
  name: WEB_CONFIG_NAME,
71
73
  description: "A design system: the values, primitives and compositions an app is built from. A ref is written `{ \"__ref\": \"<kind>:<path>\" }`, so a token is `{ \"__ref\": \"token:<group>/<name>\" }` and a token group is `{ \"__ref\": \"token:<group>\" }`. Build it in this order. Tokens first, in groups by purpose (colors, spacing, radii, type sizes), since everything else names them. Then style properties, each with `values` holding one ref to the token group whose members it accepts (`{ \"__ref\": \"token:color\" }`), so a prop takes token names rather than raw CSS; add a string keyword only for a value no token holds. A style rule's bag holds a prop's values (`\"padding\": \"4\"`, `\"bg\": \"brand\"`), never raw CSS. Then a few primitive components, one layer each, whose props are style properties (a box for layout and surface, a text for type, a pressable for interaction). Then every other component composed from those primitives: its layers name a primitive or an element, its anatomy places them (a node renders a layer, a tag or `Slot` where a slot prop's content lands; `children` is the edge list; `visible` is `{ \"$state\": \"<prop>\" }`; a prop shown in a nested layer is a `forward` route, and `$state` never sits in a node's props), and its style rules set style-property values per layer, keyed by prop values when a variant changes them. Modes (a dark mode is `{ type: \"selector\", value: \".dark\" }`) change token values through a token's `overrides`, so a component never restyles for a mode; modifiers (hover, focus, disabled) name a state, and a style rule applies under one as a `_hover` block inside a layer bag. Composites bundle style values that recur. Last, register canvas roles: the canvas draws with them, one entity per tool (`frame` for the layout primitive, `text` for the text primitive, `image`) whose entries name the components that tool inserts, and a tool whose role is unregistered is disabled. Give each component a `preview` entry under `*` so it shows content, and never write a CSS property name where a style-property name belongs.",
@@ -143,5 +145,7 @@ if (!registeredSchemaMigrations().has(20261009022722)) registerSchemaMigrations(
143
145
  if (!registeredSchemaMigrations().has(20261009181635)) registerSchemaMigrations(cssMotionStrictMigration);
144
146
  if (!registeredSchemaMigrations().has(20261009193004)) registerSchemaMigrations(linkOverrideRefMigration);
145
147
  if (!registeredSchemaMigrations().has(20261012200606)) registerSchemaMigrations(linkedComponentModulesMigration);
148
+ if (!registeredSchemaMigrations().has(20261013154037)) registerSchemaMigrations(componentStatesMigration);
149
+ if (!registeredSchemaMigrations().has(20261013164300)) registerSchemaMigrations(tokenOverridesListMigration);
146
150
  //#endregion
147
151
  export { System };
@@ -12,10 +12,19 @@ declare const nativeTokenValue: z.ZodType<number | boolean | string | Ref$1 | De
12
12
  declare const NativeToken: EntityClass<z.ZodObject<Omit<{
13
13
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
14
14
  type: z.ZodOptional<z.ZodString>;
15
- overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
15
+ overrides: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodObject<{
16
16
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
17
17
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
18
- }, z.core.$strip>>>;
18
+ }, z.core.$strip>>, z.ZodTransform<Record<string, {
19
+ on: OverrideCondition;
20
+ value: string | number | Ref | ComposedValue;
21
+ }>, {
22
+ on: OverrideCondition;
23
+ value: string | number | Ref | ComposedValue;
24
+ }[]>>, SubEntityClass<z.ZodObject<{
25
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
26
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
27
+ }, z.core.$strip>>]>>;
19
28
  utility: z.ZodOptional<z.ZodBoolean>;
20
29
  property: z.ZodOptional<z.ZodObject<{
21
30
  syntax: z.ZodOptional<z.ZodString>;
@@ -24,10 +33,19 @@ declare const NativeToken: EntityClass<z.ZodObject<Omit<{
24
33
  cssVariable: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
25
34
  }, "value" | "property" | "overrides" | "cssVariable"> & {
26
35
  value: z.ZodType<string | number | boolean | Ref | DerivedColor, unknown, z.core.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
27
- overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
36
+ overrides: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodObject<{
37
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
38
+ value: z.ZodType<string | number | boolean | Ref | DerivedColor, unknown, z.core.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
39
+ }, z.core.$strip>>, z.ZodTransform<Record<string, {
40
+ on: OverrideCondition;
41
+ value: string | number | boolean | Ref | DerivedColor;
42
+ }>, {
43
+ on: OverrideCondition;
44
+ value: string | number | boolean | Ref | DerivedColor;
45
+ }[]>>, SubEntityClass<z.ZodObject<{
28
46
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
29
47
  value: z.ZodType<string | number | boolean | Ref | DerivedColor, unknown, z.core.$ZodTypeInternals<string | number | boolean | Ref | DerivedColor, unknown>>;
30
- }, z.core.$strip>>>;
48
+ }, z.core.$strip>>]>>;
31
49
  }, z.core.$strip>, z.ZodObject<Omit<{
32
50
  cssPrefix: z.ZodOptional<z.ZodString>;
33
51
  cssVariables: z.ZodOptional<z.ZodBoolean>;
@@ -1,5 +1,6 @@
1
1
  import { refSchema } from "../../framework/utils/refs.js";
2
2
  import { oklchColor } from "../system/color.js";
3
+ import { orList } from "../../framework/defineSubEntity.js";
3
4
  import { Font } from "../system/Font.js";
4
5
  import { NATIVE_CONFIG_NAME } from "../../configs/platform.js";
5
6
  import { prepareLinkedTokenBody } from "../system/linked-token-values.js";
@@ -66,7 +67,7 @@ const NativeToken = Token.extend({
66
67
  entityName: "NativeToken",
67
68
  fields: {
68
69
  value: nativeTokenValue,
69
- overrides: NativeOverride.optional(),
70
+ overrides: orList(NativeOverride).optional(),
70
71
  cssVariable: null,
71
72
  property: null
72
73
  },
@@ -11,7 +11,7 @@ import { StyleClass } from "./style-bag.js";
11
11
  import { TokenClass } from "./Token.js";
12
12
  import { StoredPreviewCondition } from "../../framework/preview-store.js";
13
13
  import { RepeatSpec } from "../../framework/render-spec.js";
14
- import { AsInput, DefaultsOf, LayerMap, PhantomCallProps, PhantomReadProps, PropMap, RenderArgs, StyleRuleInput } from "./defineComponent.js";
14
+ import { AsInput, DefaultsOf, LayerMap, PhantomCallProps, PhantomReadProps, PropMap, RenderArgs, StateMap, StyleRuleInput } from "./defineComponent.js";
15
15
  import { listSlots, namedSlots, slotHostedBy, slotOf, slotsFor } from "../../spec/slot.js";
16
16
  import { cellAt } from "../../spec/cell.js";
17
17
  import { instanceAt } from "../../spec/instance.js";
@@ -645,6 +645,13 @@ interface AnatomyNodeClass extends Named<typeof AnatomyNodeDefinition> {}
645
645
  declare const componentFields: z.ZodObject<{
646
646
  layers: LayerClass;
647
647
  props: z.ZodOptional<PropClass>;
648
+ states: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
649
+ type: z.ZodLiteral<"boolean">;
650
+ }, z.core.$strict>, z.ZodObject<{
651
+ type: z.ZodLiteral<"variant">;
652
+ value: z.ZodArray<z.ZodString>;
653
+ default: z.ZodOptional<z.ZodString>;
654
+ }, z.core.$strict>], "type">>>;
648
655
  forwards: z.ZodOptional<ForwardClass>;
649
656
  styles: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodType<ComponentStyleBody, unknown, z.core.$ZodTypeInternals<ComponentStyleBody, unknown>>>, z.ZodTransform<Record<string, ComponentStyleBody>, ComponentStyleBody[]>>, SubEntityClass<z.ZodType<ComponentStyleBody, unknown, z.core.$ZodTypeInternals<ComponentStyleBody, unknown>>>]>>;
650
657
  renderProps: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -679,6 +686,13 @@ declare function blankComponentBody(): ComponentBody;
679
686
  declare const ComponentEntity: EntityClass<z.ZodObject<{
680
687
  layers: LayerClass;
681
688
  props: z.ZodOptional<PropClass>;
689
+ states: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
690
+ type: z.ZodLiteral<"boolean">;
691
+ }, z.core.$strict>, z.ZodObject<{
692
+ type: z.ZodLiteral<"variant">;
693
+ value: z.ZodArray<z.ZodString>;
694
+ default: z.ZodOptional<z.ZodString>;
695
+ }, z.core.$strict>], "type">>>;
682
696
  forwards: z.ZodOptional<ForwardClass>;
683
697
  styles: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodType<ComponentStyleBody, unknown, z.core.$ZodTypeInternals<ComponentStyleBody, unknown>>>, z.ZodTransform<Record<string, ComponentStyleBody>, ComponentStyleBody[]>>, SubEntityClass<z.ZodType<ComponentStyleBody, unknown, z.core.$ZodTypeInternals<ComponentStyleBody, unknown>>>]>>;
684
698
  renderProps: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -780,6 +794,19 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
780
794
  }, z.core.$strip>>;
781
795
  }, z.core.$strip>;
782
796
  };
797
+ readonly 'element-wrap': {
798
+ readonly input: z.ZodObject<{
799
+ path: z.ZodType<string, string | undefined, z.core.$ZodTypeInternals<string, string | undefined>>;
800
+ key: z.ZodOptional<z.ZodString>;
801
+ data: z.ZodObject<{
802
+ elements: z.ZodArray<z.ZodString>;
803
+ element: z.ZodDefault<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
804
+ __ref: z.ZodString;
805
+ }, z.core.$strip>]>>;
806
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
807
+ }, z.core.$strip>;
808
+ }, z.core.$strip>;
809
+ };
783
810
  readonly validate: {
784
811
  readonly input: z.ZodObject<{
785
812
  path: z.ZodType<string, string | undefined, z.core.$ZodTypeInternals<string, string | undefined>>;
@@ -841,6 +868,12 @@ type ResolvedForwards<Contract, Delta> = Delta extends {
841
868
  } ? Forwards : Contract extends {
842
869
  forwards: infer Forwards;
843
870
  } ? Forwards : Record<never, never>;
871
+ /** The states an extension ends up with: its delta's, else the linked contract's. */
872
+ type ResolvedStates<Contract, Delta> = Delta extends {
873
+ readonly states: infer States extends StateMap;
874
+ } ? States : Contract extends {
875
+ states: infer States extends StateMap;
876
+ } ? States : Record<never, never>;
844
877
  type ForwardedLayerNames<Contract, Delta> = ResolvedForwards<Contract, Delta> extends Record<string, unknown> ? keyof ResolvedForwards<Contract, Delta> & string : never;
845
878
  /** The source-only half of a linked component extension. The callback is deliberately absent from
846
879
  * {@link ExtendedRef}: it is code, so ts-morph reads it from the authoring file while Config stores
@@ -852,7 +885,7 @@ type ExtendedComponentRef<Contract = ComponentContractOf<string>, Delta extends
852
885
  * read signature for `ComponentProps`. */
853
886
  <As extends AsInput = never>(props: PhantomCallProps<ResolvedLayers<Contract, Delta>, ResolvedProps<Contract, Delta>, ResolvedDefaults<Contract, Delta>, Extra, ForwardedLayerNames<Contract, Delta>, As>): null;
854
887
  <As extends AsInput = never>(props: PhantomReadProps<ResolvedLayers<Contract, Delta>, ResolvedProps<Contract, Delta>, ResolvedDefaults<Contract, Delta>, Extra, ForwardedLayerNames<Contract, Delta>, As>): null;
855
- render<E = Extra>(fn: (args: RenderArgs<ResolvedLayers<Contract, Delta>, ResolvedProps<Contract, Delta>> & E) => unknown): ExtendedComponentRef<Contract, Delta, E>;
888
+ render<E = Extra>(fn: (args: RenderArgs<ResolvedLayers<Contract, Delta>, ResolvedProps<Contract, Delta>, ResolvedStates<Contract, Delta>> & E) => unknown): ExtendedComponentRef<Contract, Delta, E>;
856
889
  /**
857
890
  * Style rules for the extension, typed against the layers it ends up with — the linked contract's,
858
891
  * with the delta's replacing them when it declares its own.
@@ -7,6 +7,7 @@ import { shadowValue } from "./shadow.js";
7
7
  import { defineEntity } from "../../framework/defineEntity.js";
8
8
  import { defineSubEntity, orList } from "../../framework/defineSubEntity.js";
9
9
  import { childrenPolicy, forwardedPropName } from "./element.js";
10
+ import { interactionKey, interactionModifiers } from "../../framework/interaction-conditions.js";
10
11
  import { listedIn } from "../../framework/utils/enumerated.js";
11
12
  import { valueSchemaOf } from "../../framework/value-domain.js";
12
13
  import { Package } from "./Package.js";
@@ -712,25 +713,38 @@ function bagFor(config, layerElement) {
712
713
  bags.set(key, built);
713
714
  return built;
714
715
  }
716
+ /**
717
+ * A content prop's value in a rule, or `false` for none: a state that drops a Button's start icon
718
+ * says so with `startIcon: false`, since leaving the key out keeps whatever the rule beneath gives.
719
+ * Not for a required prop, whose content is the component's to have.
720
+ */
721
+ function withNone(value, declaration) {
722
+ if (declaration?.required === true) return value;
723
+ return z.union([value, z.literal(false)]);
724
+ }
715
725
  /** One element's bag, built. Reached only through the memo above. */
716
726
  function buildBagFor(config, layerElement) {
717
727
  const shape = { ...stylePropShape(config) };
718
728
  const component = isRef(layerElement) && config.resolveRef(layerElement.__ref) ? pathOf(layerElement.__ref) : void 0;
719
729
  const glyphProps = component ? glyphPropsOf(config, component) : {};
720
- for (const [name, decl] of Object.entries(component ? resolveComponentProps(config, component) : {})) shape[name] = styleBagValue({
721
- config,
722
- declaration: decl,
723
- name,
724
- glyphLibraries: decl?.type === "forward" ? glyphProps[name] : void 0
725
- }).optional();
730
+ for (const [name, decl] of Object.entries(component ? resolveComponentProps(config, component) : {})) {
731
+ const forwarded = decl?.type === "forward";
732
+ const value = styleBagValue({
733
+ config,
734
+ declaration: decl,
735
+ name,
736
+ glyphLibraries: forwarded ? glyphProps[name] : void 0
737
+ });
738
+ shape[name] = (forwarded ? withNone(value, decl) : value).optional();
739
+ }
726
740
  const declared = component ? (config.resolve("component", component)?.toJSON())?.props ?? {} : {};
727
741
  for (const [name, decl] of Object.entries(declared)) {
728
742
  if (decl?.type !== "slot" || name in shape) continue;
729
- shape[name] = styleBagValue({
743
+ shape[name] = withNone(styleBagValue({
730
744
  config,
731
745
  declaration: decl,
732
746
  name
733
- }).optional();
747
+ }), decl).optional();
734
748
  }
735
749
  shape.as ??= z.string().optional();
736
750
  for (const [key, owned] of config.layerKeys()) shape[key] = owned.schema(shape[key]).optional();
@@ -808,6 +822,18 @@ function conditionValue(prop, declaration, config) {
808
822
  if (!ownsItsValues(declaration)) return domain;
809
823
  return z.union([domain, refSchema]).transform((value) => isRef(value) ? value : valueRef(prop, String(value)));
810
824
  }
825
+ /** The condition on a STATE: a boolean's `true`/`false`, or one of a variant state's names. A state
826
+ * has no value members to ref, so a variant's condition is the name itself. */
827
+ function stateConditionValue(declaration) {
828
+ const decl = declaration;
829
+ if (decl?.type === "variant" && Array.isArray(decl.value)) return z.enum(decl.value);
830
+ return scalarConditionValue({ type: "boolean" }) ?? z.boolean();
831
+ }
832
+ /** An interaction condition's value: `true` (the root), or the layer whose interaction it is. */
833
+ function interactionConditionValue(layers) {
834
+ const truthy = z.union([z.literal(true), z.literal("true").transform(() => true)]);
835
+ return layers.length > 0 ? z.union([truthy, z.enum(layers)], { error: `an interaction condition is \`true\` (the component's root) or one of its layers: ${layers.join(", ")}` }) : truthy;
836
+ }
811
837
  /**
812
838
  * The condition on a boolean or number prop, in the prop's type. A preview axis spells every value
813
839
  * as a string and a rule's key stringifies it, so a writer reading either hands back `'true'` where
@@ -1022,6 +1048,13 @@ const StyleRule = defineSubEntity({
1022
1048
  build: () => {
1023
1049
  const when = {};
1024
1050
  for (const [name, decl] of Object.entries(body.props ?? {})) when[name] = conditionValue(name, decl, config).optional();
1051
+ for (const [name, decl] of Object.entries(body.states ?? {})) when[name] = stateConditionValue(decl).optional();
1052
+ const layerNames = Object.keys(body.layers ?? {});
1053
+ for (const modifier of interactionModifiers(config)) {
1054
+ const key = interactionKey(modifier);
1055
+ if (key in when) continue;
1056
+ when[key] = interactionConditionValue(layerNames).optional();
1057
+ }
1025
1058
  const layers = {};
1026
1059
  for (const [name, el] of Object.entries(body.layers ?? {})) layers[name] = bagFor(config, el).optional();
1027
1060
  return z.strictObject({
@@ -1049,10 +1082,11 @@ function perOwner({ cache, config, owner, build }) {
1049
1082
  cache.set(config, perConfig);
1050
1083
  }
1051
1084
  const hit = perConfig.get(owner);
1052
- if (hit && hit.props === owner.props && hit.layers === owner.layers) return hit.schema;
1085
+ if (hit && hit.props === owner.props && hit.states === owner.states && hit.layers === owner.layers) return hit.schema;
1053
1086
  const schema = build();
1054
1087
  perConfig.set(owner, {
1055
1088
  props: owner.props,
1089
+ states: owner.states,
1056
1090
  layers: owner.layers,
1057
1091
  schema
1058
1092
  });
@@ -1089,6 +1123,7 @@ const Preview = defineSubEntity({
1089
1123
  build: () => {
1090
1124
  const when = {};
1091
1125
  for (const [name, decl] of Object.entries(body.props ?? {})) when[name] = conditionValue(name, decl, config).optional();
1126
+ for (const [name, decl] of Object.entries(body.states ?? {})) when[name] = stateConditionValue(decl).optional();
1092
1127
  return z.strictObject({
1093
1128
  when: z.strictObject(when, { error: previewShapeError }).nullable().default(null),
1094
1129
  props: z.record(z.string(), z.unknown()).transform((props) => bindPreviewGlyphs({
@@ -1347,6 +1382,83 @@ function anatomySubtreeContains(anatomy, ancestor, key) {
1347
1382
  function resolveMoveBody(config, path) {
1348
1383
  return config.resolve("component", path)?.toJSON();
1349
1384
  }
1385
+ /**
1386
+ * Wrap a component's root: the container becomes `root`, and what the root was — its element, its
1387
+ * children and slots, its props, the style rules and forwards that name it — moves onto `inner`, the
1388
+ * container's one child. Props the config doesn't model (`id`, an `onClick`) keep landing on `root`,
1389
+ * which is now the container.
1390
+ */
1391
+ function wrapRoot({ config, path, inner, element, props }) {
1392
+ const body = config.resolve("component", path)?.toJSON();
1393
+ const root = body?.anatomy?.root;
1394
+ if (!body || !root) reject("NOT_FOUND", `"${path}" has no root to wrap.`);
1395
+ const { element: _element, ...rootRest } = root;
1396
+ let next = config.apply({
1397
+ kind: "component",
1398
+ operation: "sub-create",
1399
+ path,
1400
+ field: "layers",
1401
+ key: inner,
1402
+ data: body?.layers?.root
1403
+ }).apply({
1404
+ kind: "component",
1405
+ operation: "sub-create",
1406
+ path,
1407
+ field: "anatomy",
1408
+ key: inner,
1409
+ data: {
1410
+ ...rootRest,
1411
+ element: layerRef(inner)
1412
+ }
1413
+ }).apply({
1414
+ kind: "component",
1415
+ operation: "sub-upsert",
1416
+ path,
1417
+ field: "layers",
1418
+ key: "root",
1419
+ data: asElementRef(config, element)
1420
+ }).apply({
1421
+ kind: "component",
1422
+ operation: "sub-upsert",
1423
+ path,
1424
+ field: "anatomy",
1425
+ key: "root",
1426
+ data: {
1427
+ element: layerRef("root"),
1428
+ children: [inner],
1429
+ ...props ? { props } : {}
1430
+ }
1431
+ });
1432
+ for (const [key, rule] of Object.entries(body?.styles ?? {})) {
1433
+ const bag = rule.layers?.root;
1434
+ if (bag === void 0) continue;
1435
+ next = next.apply({
1436
+ kind: "component",
1437
+ operation: "sub-update",
1438
+ path,
1439
+ field: "styles",
1440
+ key,
1441
+ data: {
1442
+ when: rule.when ?? null,
1443
+ [`layers.${inner}`]: bag,
1444
+ "layers.root": null
1445
+ }
1446
+ });
1447
+ }
1448
+ for (const [name, decl] of Object.entries(body?.props ?? {})) {
1449
+ const target = decl.target;
1450
+ if (!(isRef(target) ? kindOf(target) === "layer" && pathOf(target) === "root" : typeof target === "string" && target.startsWith("root/"))) continue;
1451
+ next = next.apply({
1452
+ kind: "component",
1453
+ operation: "sub-update",
1454
+ path,
1455
+ field: "props",
1456
+ key: name,
1457
+ data: { target: isRef(target) ? layerRef(inner) : `${inner}/${String(target).slice(5)}` }
1458
+ });
1459
+ }
1460
+ return next;
1461
+ }
1350
1462
  const AnatomyNode = defineSubEntity({
1351
1463
  name: "element",
1352
1464
  label: "Element",
@@ -1538,6 +1650,18 @@ const componentFields = z.strictObject({
1538
1650
  }),
1539
1651
  props: Prop.optional(),
1540
1652
  /**
1653
+ * What the RENDER knows and a style rule keys on — the field has a value, the image loaded, the
1654
+ * indicator is sliding. Not props: a caller never passes one, so none is part of the component's
1655
+ * API. The render passes them to `states(props, { … })` and spreads the layers it hands back;
1656
+ * the rules naming one follow the values given, and the root carries each as a `data-<name>`
1657
+ * attribute. Studio and tests force one through the module's internal channel, never a prop.
1658
+ */
1659
+ states: z.record(z.string(), z.discriminatedUnion("type", [z.strictObject({ type: z.literal("boolean") }), z.strictObject({
1660
+ type: z.literal("variant"),
1661
+ value: z.array(z.string()).min(1),
1662
+ default: z.string().optional()
1663
+ })])).optional().describe("What the render knows and a style rule keys on, by name: `{ \"filled\": { \"type\": \"boolean\" } }`, or `{ \"phase\": { \"type\": \"variant\", \"value\": [\"idle\", \"sliding\"], \"default\": \"idle\" } }`. Not props, so not part of the component's API: the render passes its `props` and the values it knows to `const layers = states(props, { filled: value !== \"\" })` and spreads those layers (`{...layers.root}`) instead of `props`; a rule naming a state in `when` (`{ \"filled\": true }`, `{ \"phase\": \"sliding\" }`) is tested against the values given, and the root carries each as a `data-<name>` attribute. A value a caller controls is a prop instead, which the render reports the state from."),
1664
+ /**
1541
1665
  * Which layers' element props this component also exposes — see {@link Forward}.
1542
1666
  *
1543
1667
  * Replaced `inherits`, which named one layer and meant `{ [layer]: '*' }`. That field and its
@@ -1598,6 +1722,34 @@ const componentFields = z.strictObject({
1598
1722
  message: `\`${name}\` conditions on itself. \`when\` names a sibling prop and one of its values.`
1599
1723
  });
1600
1724
  }
1725
+ if (isPlainObject(body.states) && Object.keys(body.states).length > 0) {
1726
+ const props = isPlainObject(body.props) ? body.props : {};
1727
+ if (Object.hasOwn(props, "states")) ctx.issues.push({
1728
+ code: "custom",
1729
+ input: props.states,
1730
+ path: ["props", "states"],
1731
+ message: "A component with states hands its render a `states` helper, so no prop of its own can be named `states`."
1732
+ });
1733
+ for (const [name, decl] of Object.entries(body.states)) {
1734
+ if (Object.hasOwn(props, name)) ctx.issues.push({
1735
+ code: "custom",
1736
+ input: decl,
1737
+ path: ["states", name],
1738
+ message: `\`${name}\` is both a prop and a state. A state is what the render knows, never a value a caller passes; a caller-controlled value is a prop the render reports the state from.`
1739
+ });
1740
+ const variant = decl;
1741
+ if (variant?.type === "variant" && variant.default !== void 0 && Array.isArray(variant.value) && !variant.value.includes(variant.default)) ctx.issues.push({
1742
+ code: "custom",
1743
+ input: variant.default,
1744
+ path: [
1745
+ "states",
1746
+ name,
1747
+ "default"
1748
+ ],
1749
+ message: `\`${name}\`'s default \`${String(variant.default)}\` is not one of its values (${variant.value.join(", ")}).`
1750
+ });
1751
+ }
1752
+ }
1601
1753
  if (body.forwards || body.file || isRef(body.props)) return;
1602
1754
  const declared = body.props ?? {};
1603
1755
  if (Object.hasOwn(declared, "children") && declared.children !== null) return;
@@ -2328,6 +2480,69 @@ subtree: z.boolean().default(true) }).optional()
2328
2480
  });
2329
2481
  }
2330
2482
  },
2483
+ "element-wrap": {
2484
+ input: z.object({
2485
+ path: pathInput("component"),
2486
+ /** The new layer's name: the wrapper's, or — wrapping the root — the old root's new name. The
2487
+ * entity mints a free one when the caller doesn't care. */
2488
+ key: z.string().optional(),
2489
+ data: z.object({
2490
+ /** The layers to wrap: siblings under one parent, wrapped in their order, or `['root']`. */
2491
+ elements: z.array(z.string()).min(1),
2492
+ /** What the wrapper renders — a frame component or a tag. */
2493
+ element: z.union([z.string(), refSchema]).default("div"),
2494
+ /** The wrapper's own props. */
2495
+ props: z.record(z.string(), z.unknown()).optional()
2496
+ })
2497
+ }),
2498
+ readOnly: false,
2499
+ label: "Wrap elements",
2500
+ description: "Wrap a component's sibling layers in a new container. Wrapping `root` makes the container the root and moves the old root — its element, children, slots, styles and forwards — onto a new layer inside it.",
2501
+ scope: "item",
2502
+ creates: true,
2503
+ title: (entity) => `Wrap ${entity} elements`,
2504
+ handler: (input, config) => {
2505
+ const payload = input.data;
2506
+ const path = String(input.path);
2507
+ const keys = payload.elements;
2508
+ const anatomy = resolveMoveBody(config, path)?.anatomy ?? {};
2509
+ for (const k of keys) if (!anatomy[k]) reject("NOT_FOUND", `"${path}" has no element "${k}" to wrap.`);
2510
+ if (keys.includes("root")) {
2511
+ if (keys.length > 1) rejectData("The root wraps on its own: it has no siblings to wrap with it.", ["elements"]);
2512
+ return wrapRoot({
2513
+ config,
2514
+ path,
2515
+ inner: input.key ? String(input.key) : freeLayerKey(config, path, "content"),
2516
+ element: payload.element,
2517
+ props: payload.props
2518
+ });
2519
+ }
2520
+ const parent = Object.entries(anatomy).find(([, node]) => keys.every((k) => node.children?.includes(k)));
2521
+ if (!parent) rejectData("Only layers that share a parent wrap together — wrap each parent's layers on their own.", ["elements"]);
2522
+ const [parentKey, parentNode] = parent;
2523
+ const order = (parentNode.children ?? []).filter((c) => keys.includes(c));
2524
+ const index = (parentNode.children ?? []).indexOf(order[0]);
2525
+ const wrapper = input.key ? String(input.key) : freeLayerKey(config, path, "wrap");
2526
+ return config.run([{
2527
+ kind: "component",
2528
+ operation: "element-insert",
2529
+ path,
2530
+ key: wrapper,
2531
+ data: {
2532
+ element: payload.element,
2533
+ parent: parentKey,
2534
+ index,
2535
+ ...payload.props ? { props: payload.props } : {}
2536
+ }
2537
+ }, ...order.map((k) => ({
2538
+ kind: "component",
2539
+ operation: "element-move",
2540
+ path,
2541
+ key: k,
2542
+ data: { parent: wrapper }
2543
+ }))]);
2544
+ }
2545
+ },
2331
2546
  validate: {
2332
2547
  input: z.object({
2333
2548
  path: pathInput("component"),
@@ -753,8 +753,9 @@ displayName: ({ member, path }) => typeof member.name === "string" && member.nam
753
753
  bulkOverlayWrite: ({ accessor, path, overlay }) => ({ [`options.${accessor}.${path}`]: overlay })
754
754
  });
755
755
  /** An `override` or `extend` body with its `styles` as a list, in the stored form: the record keyed by
756
- * each rule's condition, as `Component.item` stores one. Only a component's `styles` is a rule list —
757
- * every other kind's is a record — so a list here is one whatever accessor it sits under. */
756
+ * each rule's condition, as `Component.item` stores one. Only a component has `styles`, so a list
757
+ * there is a rule list whatever accessor it sits under. A token's `overrides` list is stored as
758
+ * written and keyed where the overlay is read. */
758
759
  function storedPinBody(body) {
759
760
  if (!isPlainObject(body) || !Array.isArray(body.styles)) return body;
760
761
  return {
@@ -1,9 +1,9 @@
1
1
  import { splitRef } from "../../framework/utils/refs.js";
2
2
  import { defineEntity } from "../../framework/defineEntity.js";
3
3
  import { defineSubEntity } from "../../framework/defineSubEntity.js";
4
+ import { createSliceMemo } from "../../framework/memo.js";
4
5
  import { buildFields } from "../../configs/build-options.js";
5
6
  import { parseRootFontSize } from "../../css/root-font-size.js";
6
- import { createSliceMemo } from "../../framework/memo.js";
7
7
  import { resolveInputDir } from "../../framework/registry-dir.js";
8
8
  import { safelistSchema } from "./safelist.js";
9
9
  import { z } from "zod";
@@ -1,8 +1,8 @@
1
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
+ import { createSliceMemo } from "../../framework/memo.js";
3
4
  import { cssValueIssue, isCssProperty } from "../../css/values.js";
4
5
  import { camelCase } from "./style-bag.js";
5
- import { createSliceMemo } from "../../framework/memo.js";
6
6
  import { isIdentifier } from "../../framework/utils/identifier.js";
7
7
  import { cssVar, declaresCssVariable } from "../../framework/projections.js";
8
8
  import { z } from "zod";
@@ -44,10 +44,19 @@ declare function defineOverride<TValue extends z.ZodTypeAny>(value: TValue): Sub
44
44
  declare const tokenFields: z.ZodObject<{
45
45
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
46
46
  type: z.ZodOptional<z.ZodString>;
47
- overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
47
+ overrides: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<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
- }, z.core.$strip>>>;
50
+ }, z.core.$strip>>, z.ZodTransform<Record<string, {
51
+ on: OverrideCondition;
52
+ value: string | number | Ref | ComposedValue;
53
+ }>, {
54
+ on: OverrideCondition;
55
+ value: string | number | Ref | ComposedValue;
56
+ }[]>>, SubEntityClass<z.ZodObject<{
57
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
58
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
59
+ }, z.core.$strip>>]>>;
51
60
  utility: z.ZodOptional<z.ZodBoolean>;
52
61
  property: z.ZodOptional<z.ZodObject<{
53
62
  syntax: z.ZodOptional<z.ZodString>;
@@ -61,10 +70,19 @@ type TokenBody = z.infer<typeof tokenFields>;
61
70
  declare const TokenEntity: EntityClass<z.ZodObject<{
62
71
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
63
72
  type: z.ZodOptional<z.ZodString>;
64
- overrides: z.ZodOptional<SubEntityClass<z.ZodObject<{
73
+ overrides: z.ZodOptional<z.ZodUnion<readonly [z.ZodPipe<z.ZodArray<z.ZodObject<{
74
+ on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
75
+ value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
76
+ }, z.core.$strip>>, z.ZodTransform<Record<string, {
77
+ on: OverrideCondition;
78
+ value: string | number | Ref | ComposedValue;
79
+ }>, {
80
+ on: OverrideCondition;
81
+ value: string | number | Ref | ComposedValue;
82
+ }[]>>, SubEntityClass<z.ZodObject<{
65
83
  on: z.ZodType<OverrideCondition, unknown, z.core.$ZodTypeInternals<OverrideCondition, unknown>>;
66
84
  value: z.ZodType<string | number | Ref | ComposedValue, unknown, z.core.$ZodTypeInternals<string | number | Ref | ComposedValue, unknown>>;
67
- }, z.core.$strip>>>;
85
+ }, z.core.$strip>>]>>;
68
86
  utility: z.ZodOptional<z.ZodBoolean>;
69
87
  property: z.ZodOptional<z.ZodObject<{
70
88
  syntax: z.ZodOptional<z.ZodString>;
@@ -1,7 +1,7 @@
1
1
  import { isRef, kindOf, pathOf, ref, refKey, sourceOf } from "../../framework/utils/refs.js";
2
2
  import { composedValueSchemas } from "./composed-values.js";
3
3
  import { defineEntity } from "../../framework/defineEntity.js";
4
- import { defineSubEntity } from "../../framework/defineSubEntity.js";
4
+ import { defineSubEntity, orList } from "../../framework/defineSubEntity.js";
5
5
  import { Font } from "./Font.js";
6
6
  import { Modifier } from "./Modifier.js";
7
7
  import { spellTokenRead } from "./token-values.js";
@@ -112,7 +112,8 @@ function defineOverride(value) {
112
112
  on: config ? declaredCondition(config) : overrideConditionSchema,
113
113
  value
114
114
  }),
115
- key: (override) => overrideKey(override.on)
115
+ key: (override) => overrideKey(override.on),
116
+ listed: true
116
117
  });
117
118
  }
118
119
  const Override = defineOverride(tokenValue);
@@ -128,7 +129,7 @@ const TokenEntity = defineEntity({
128
129
  fields: z.object({
129
130
  value: tokenValue.describe("The base value: a literal, a ref to another token, or a composed value such as a gradient."),
130
131
  type: z.string().optional().describe("The CSS value type, when the value alone does not say it."),
131
- overrides: Override.optional().describe("Per-mode values, keyed by the condition they apply under."),
132
+ overrides: orList(Override).optional().describe("Per-mode values, a list of `{ on, value }`. Stored keyed by the condition each applies under."),
132
133
  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
134
  property: z.strictObject({
134
135
  syntax: z.string().optional().describe("The `@property` syntax, as `<length>`. Defaults to `*`."),