praxis-kit 7.3.0 → 7.4.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.
@@ -1,5 +1,5 @@
1
- import { U as UnknownProps, E as ElementType, a as EmptyRecord, V as VariantMap, R as RecipeMap, A as AnyClassPluginFactory, b as AnyRecord, c as ReactFactoryOptions, P as PolymorphicComponent, d as PolymorphicGenerics, e as ExtractPluginProps } from '../react-options-DLDsA4Tn.js';
2
- export { f as AnyFactoryOptions, g as ElementRef, F as FactoryOptions, h as PolymorphicProps, i as PolymorphicWithAsChild, j as PolymorphicWithRender, k as RenderCallbackProps, S as Slottable, l as SlottableProps, m as composeRefs, n as defineContractComponent, m as mergeRefs } from '../react-options-DLDsA4Tn.js';
1
+ import { U as UnknownProps, E as ElementType, a as EmptyRecord, V as VariantMap, N as NoVariants, R as RecipeMap, b as NoPreset, A as AnyClassPluginFactory, c as AnyRecord, d as ReactFactoryOptions, M as MergeRecords, P as PolymorphicComponent, e as PolymorphicGenerics, f as ExtractPluginProps } from '../react-options-BnjZpdVh.js';
2
+ export { g as AnyFactoryOptions, h as ElementRef, F as FactoryOptions, i as PolymorphicProps, j as PolymorphicWithAsChild, k as PolymorphicWithRender, l as RenderCallbackProps, S as Slottable, m as SlottableProps, n as composeRefs, o as defineContractComponent, n as mergeRefs } from '../react-options-BnjZpdVh.js';
3
3
  import * as react from 'react';
4
4
  import { ReactElement, Ref } from 'react';
5
5
  import 'type-fest';
@@ -22,9 +22,30 @@ type CloneInput = {
22
22
  ref: NormalizedRef;
23
23
  };
24
24
 
25
- declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: ReactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed> & {
25
+ /**
26
+ * Creates a polymorphic React 19 component with praxis-kit contracts applied.
27
+ *
28
+ * ```tsx
29
+ * const Button = createContractComponent({
30
+ * tag: 'button',
31
+ * name: 'Button',
32
+ * styling: {
33
+ * base: 'btn',
34
+ * variants: { intent: { primary: 'btn--primary', ghost: 'btn--ghost' } },
35
+ * defaults: { intent: 'primary' },
36
+ * },
37
+ * })
38
+ *
39
+ * <Button intent="ghost" as="a" href="/home">Home</Button>
40
+ * ```
41
+ *
42
+ * `ref` is accepted as a plain prop (React 19) and forwarded to the rendered host element or,
43
+ * with `asChild`, to the consumer's own element. Pass `subComponents` to attach named
44
+ * sub-components (`Card.Header`) and `onElement` to run setup once the real DOM element exists.
45
+ */
46
+ declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: ReactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed> & {
26
47
  readonly subComponents?: TSubComponents;
27
- }): PolymorphicComponent<PolymorphicGenerics<TDefault, Props & ExtractPluginProps<TPlugin>, Variants, TPreset, TAllowed>> & TSubComponents;
48
+ }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset, TAllowed>>, TSubComponents>;
28
49
 
29
50
  type SlotProps = {
30
51
  ref?: Ref<unknown> | null;
@@ -1,10 +1,32 @@
1
- import { E as ElementType, U as UnknownProps, a as EmptyRecord, V as VariantMap, R as RecipeMap, A as AnyClassPluginFactory, c as ReactFactoryOptions, P as PolymorphicComponent, d as PolymorphicGenerics, e as ExtractPluginProps } from '../react-options-DLDsA4Tn.js';
2
- export { f as AnyFactoryOptions, g as ElementRef, F as FactoryOptions, h as PolymorphicProps, i as PolymorphicWithAsChild, j as PolymorphicWithRender, k as RenderCallbackProps, S as Slottable, l as SlottableProps, n as defineContractComponent, m as mergeRefs } from '../react-options-DLDsA4Tn.js';
1
+ import { E as ElementType, U as UnknownProps, a as EmptyRecord, V as VariantMap, N as NoVariants, R as RecipeMap, b as NoPreset, A as AnyClassPluginFactory, d as ReactFactoryOptions, P as PolymorphicComponent, e as PolymorphicGenerics, M as MergeRecords, f as ExtractPluginProps } from '../react-options-BnjZpdVh.js';
2
+ export { g as AnyFactoryOptions, h as ElementRef, F as FactoryOptions, i as PolymorphicProps, j as PolymorphicWithAsChild, k as PolymorphicWithRender, l as RenderCallbackProps, S as Slottable, m as SlottableProps, o as defineContractComponent, n as mergeRefs } from '../react-options-BnjZpdVh.js';
3
3
  import * as react from 'react';
4
4
  import 'type-fest';
5
5
  import '../_shared/diagnostics.js';
6
6
 
7
- declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType>(options: ReactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed>): PolymorphicComponent<PolymorphicGenerics<TDefault, Props & ExtractPluginProps<TPlugin>, Variants, TPreset, TAllowed>>;
7
+ /**
8
+ * Creates a polymorphic React component with praxis-kit contracts applied, for React 18 and
9
+ * earlier (use `praxis-kit/react` instead on React 19, which accepts `ref` as a plain prop).
10
+ *
11
+ * ```tsx
12
+ * const Button = createContractComponent({
13
+ * tag: 'button',
14
+ * name: 'Button',
15
+ * styling: {
16
+ * base: 'btn',
17
+ * variants: { intent: { primary: 'btn--primary', ghost: 'btn--ghost' } },
18
+ * defaults: { intent: 'primary' },
19
+ * },
20
+ * })
21
+ *
22
+ * <Button intent="ghost" as="a" href="/home">Home</Button>
23
+ * ```
24
+ *
25
+ * Returns a `forwardRef` component — `ref` is forwarded to the rendered host element the same
26
+ * way it works in `praxis-kit/react`. Pass `onElement` to run setup once the real DOM element
27
+ * exists; this adapter doesn't support `subComponents`.
28
+ */
29
+ declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType>(options: ReactFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed>): PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset, TAllowed>>;
8
30
 
9
31
  type SlotProps = {
10
32
  [key: string]: unknown;
@@ -2,11 +2,72 @@ import { Ref, PropsWithChildren, ReactElement, ComponentType, JSX, ReactNode } f
2
2
  import { RequireAtLeastOne, Simplify, ReadonlyDeep, NonEmptyTuple } from 'type-fest';
3
3
  import { Diagnostics, DiagnosticInput, DiagnosticsMode } from './_shared/diagnostics.js';
4
4
 
5
+ /**
6
+ * A string-keyed object whose values are of type `T`.
7
+ */
5
8
  type StringMap<T = unknown> = Record<string, T>;
9
+ /**
10
+ * A string-keyed object with values of unknown type.
11
+ */
6
12
  type AnyRecord = StringMap<unknown>;
13
+ /**
14
+ * An object type with no named properties.
15
+ *
16
+ * Unlike `{}`, this excludes arbitrary properties during type operations while
17
+ * still satisfying `extends object`.
18
+ */
7
19
  type EmptyRecord = Record<never, never>;
8
- /** A compound component's named sub-components, e.g. `{ Header, Content, Footer }`. */
20
+ /**
21
+ * A compound component's named sub-components, for example
22
+ * `{ Header, Content, Footer }`.
23
+ */
9
24
  type SubComponentMap = Readonly<AnyRecord>;
25
+ /**
26
+ * Default `Variants` type for components that declare no variants.
27
+ *
28
+ * Structurally identical to `Readonly<EmptyRecord>`, but named separately so
29
+ * editor hovers remain self-descriptive.
30
+ */
31
+ type NoVariants = Readonly<EmptyRecord>;
32
+ /**
33
+ * Default `TPreset` type for components that declare no named presets.
34
+ *
35
+ * Structurally identical to `Readonly<EmptyRecord>`, but named separately so
36
+ * editor hovers remain self-descriptive.
37
+ */
38
+ type NoPreset = Readonly<EmptyRecord>;
39
+ /**
40
+ * Fallback for `ExtractPluginProps<TPlugin>` when a plugin contributes no
41
+ * props, including the no-plugin case.
42
+ *
43
+ * Structurally identical to `EmptyRecord`, but named separately so editor
44
+ * hovers remain self-descriptive.
45
+ */
46
+ type NoPluginProps = EmptyRecord;
47
+ /**
48
+ * Determines whether an object type should be treated as empty.
49
+ *
50
+ * `keyof T` ignores call and construct signatures...
51
+ */
52
+ type IsEmptyRecord<T extends object> = T extends (...args: never[]) => unknown ? false : T extends new (...args: never[]) => unknown ? false : keyof T extends never ? true : false;
53
+ /**
54
+ * Merges two object types while eliding empty operands.
55
+ *
56
+ * If either operand is {@link EmptyRecord}, the other operand is returned
57
+ * directly instead of producing intersections such as
58
+ * `Component & EmptyRecord` in editor hovers.
59
+ *
60
+ * Unlike a homomorphic mapped type (for example `Simplify<T>`), this preserves
61
+ * call and construct signatures. Many component types are callable objects,
62
+ * and mapped types silently discard those signatures.
63
+ *
64
+ * @remarks
65
+ * Instantiate `MergeRecords` directly. Introducing an intermediate alias for
66
+ * one operand (for example `type C = PolymorphicComponent<G>`) can prevent
67
+ * `IsEmptyRecord` from evaluating eagerly, which breaks assignability under
68
+ * `exactOptionalPropertyTypes`.
69
+ */
70
+ type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends true ? B : IsEmptyRecord<B> extends true ? A : A & B;
10
71
 
11
72
  type IntrinsicTag = keyof HTMLElementTagNameMap;
12
73
 
@@ -190,7 +251,7 @@ type ClassPluginFactory<TProps extends AnyRecord = EmptyRecord> = <V extends Var
190
251
  * wherever a factory's concrete plugin-props shape isn't tracked (factory generics,
191
252
  * capability wiring). */
192
253
  type AnyClassPluginFactory = ClassPluginFactory<AnyRecord> | undefined;
193
- type ExtractPluginProps<TPlugin extends AnyClassPluginFactory> = TPlugin extends ClassPluginFactory<infer T> ? string extends keyof T ? EmptyRecord : T : EmptyRecord;
254
+ type ExtractPluginProps<TPlugin extends AnyClassPluginFactory> = TPlugin extends ClassPluginFactory<infer T> ? string extends keyof T ? NoPluginProps : T : NoPluginProps;
194
255
 
195
256
  type AriaContext = {
196
257
  readonly tag: IntrinsicTag;
@@ -254,6 +315,12 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
254
315
  * `@praxis-kit/diagnostics`.
255
316
  */
256
317
  readonly diagnostics?: Diagnostics | DiagnosticsMode;
318
+ /**
319
+ * ARIA/accessibility rules evaluated against the resolved tag and props on every render.
320
+ * Each rule is a function receiving the current context and returning zero or more
321
+ * violations, some of which can carry an auto-applicable fix (see `createRemoveAttributeRule`
322
+ * and friends in `praxis-kit/contract`).
323
+ */
257
324
  readonly aria?: readonly AriaRule[];
258
325
  /**
259
326
  * Rules that need `AriaPolicyEngine`'s fix-application/caching machinery
@@ -265,6 +332,11 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
265
332
  * misleading `aria` name to get the machinery it needs.
266
333
  */
267
334
  readonly rules?: readonly AriaRule[];
335
+ /**
336
+ * Declares which children are valid, by name, match predicate, and cardinality (e.g. "at
337
+ * least 1, at most 4 `Button` children"). Open by default — children matching no rule are
338
+ * still allowed unless `exclusiveChildren` is set.
339
+ */
268
340
  readonly children?: readonly ChildRuleInput[];
269
341
  /**
270
342
  * When true, only children matching a `children` rule (or text, per `allowText`)
@@ -277,19 +349,49 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
277
349
  * or any listed rule. Default: true.
278
350
  */
279
351
  readonly allowText?: boolean;
352
+ /**
353
+ * Prop transforms composed with the component's own `normalize` (from `FactoryOptions`) and
354
+ * run before it. Unlike `normalize`, these live in the enforcement bucket because they
355
+ * typically encode a built-in HTML/ARIA fact rather than component-specific behavior.
356
+ */
280
357
  readonly props?: readonly PropNormalizer[];
281
358
  /** Restricts the `as` prop to this set of tags. Violations route through diagnostics. */
282
359
  readonly allowedAs?: readonly TAllowed[];
283
360
  };
284
361
 
285
362
  type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = {
363
+ /** Class applied to every instance regardless of variant selection. */
286
364
  readonly base?: ClassName;
365
+ /**
366
+ * Named variant groups (e.g. `intent`, `size`), each mapping its possible values to a
367
+ * class string. A consumer selects a value per group as a prop (`<Button intent="primary">`).
368
+ */
287
369
  readonly variants?: V;
370
+ /** Value used for a variant group when the consumer doesn't pass one explicitly. */
288
371
  readonly defaults?: Partial<DefaultVariants<V>>;
372
+ /**
373
+ * Applies an extra class only when a specific *combination* of variant selections matches —
374
+ * for cases `variants` alone can't express (e.g. `intent: 'primary'` + `size: 'lg'` together
375
+ * need a class neither variant would add on its own).
376
+ */
289
377
  readonly compounds?: readonly CompoundVariant<V>[];
378
+ /**
379
+ * Named bundles of variant values, selectable as a single unit via the `recipe` prop (e.g.
380
+ * `<Button recipe="cta">` instead of setting `intent`/`size` individually).
381
+ */
290
382
  readonly presets?: TPreset;
383
+ /** Maps a resolved tag directly to a raw class string, independent of the variant system. */
291
384
  readonly tags?: Readonly<TagMap>;
385
+ /**
386
+ * A `ClassPluginFactory` (e.g. the Tailwind layout pipeline) that extends class resolution
387
+ * with its own owned props, layered on top of `variants`/`presets`/`tags`.
388
+ */
292
389
  readonly plugin?: TPlugin;
390
+ /**
391
+ * A cache-key → resolved-class-string lookup for every statically-known variant
392
+ * combination, skipping runtime class computation entirely when a match is found. Normally
393
+ * generated by a build-time class-extraction plugin rather than hand-authored.
394
+ */
293
395
  readonly precomputedClasses?: Readonly<Record<string, string>>;
294
396
  };
295
397
 
@@ -298,11 +400,21 @@ type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
298
400
  }['normalize'];
299
401
  type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, RecipeMap<VariantMap>, AnyClassPluginFactory>;
300
402
  type FactoryOptions<TDefault extends ElementType = ElementType, Props extends AnyRecord = EmptyRecord, V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> = {
403
+ /** The intrinsic tag the component renders by default. Overridable per instance via `as`. */
301
404
  readonly tag?: TDefault;
405
+ /** Display name used in diagnostics, dev tools, and generated component naming. */
302
406
  readonly name?: string;
407
+ /** Values used for the component's own (non-variant) props when the consumer omits them. */
303
408
  readonly defaults?: Partial<NoInfer<Props>>;
409
+ /**
410
+ * A pure `(props) => props` transform run on every render, after `enforcement.props`'s
411
+ * normalizers see the same input. Use this for component-specific prop shaping — anything
412
+ * that depends on live instance state or the real DOM element belongs in `onElement` instead.
413
+ */
304
414
  readonly normalize?: NormalizeFn<NoInfer<Props>>;
415
+ /** Variant groups, base classes, presets, and the optional class-resolution plugin. */
305
416
  readonly styling?: StylingOptions<V, TPreset, TPlugin>;
417
+ /** ARIA rules, child-content contracts, and other runtime validation for this component. */
306
418
  readonly enforcement?: EnforcementOptions<TAllowed>;
307
419
  /**
308
420
  * Adapter-resolved diagnostics default, spread in by `resolveAdapterCommonOptions`. Not meant to
@@ -533,7 +645,7 @@ interface CompiledArtifact {
533
645
  * Extends FactoryOptions with React-specific configuration.
534
646
  * slotComponent is intentionally not in core — it is a React rendering concern.
535
647
  */
536
- type ReactFactoryOptions<TDefault extends ElementType, Props extends UnknownProps, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> = FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed> & {
648
+ type ReactFactoryOptions<TDefault extends ElementType, Props extends UnknownProps, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> = FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin, TAllowed> & {
537
649
  /** Component used to render the asChild slot. Defaults to the built-in Slot. */
538
650
  slotComponent?: SlotComponent;
539
651
  /**
@@ -547,4 +659,4 @@ type ReactFactoryOptions<TDefault extends ElementType, Props extends UnknownProp
547
659
  artifact?: CompiledArtifact;
548
660
  };
549
661
 
550
- export { type AnyClassPluginFactory as A, type ElementType as E, type FactoryOptions as F, type PolymorphicComponent as P, type RecipeMap as R, Slottable as S, type UnknownProps as U, type VariantMap as V, type EmptyRecord as a, type AnyRecord as b, type ReactFactoryOptions as c, type PolymorphicGenerics as d, type ExtractPluginProps as e, type AnyFactoryOptions as f, type ElementRef as g, type PolymorphicProps as h, type PolymorphicWithAsChild as i, type PolymorphicWithRender as j, type RenderCallbackProps as k, type SlottableProps as l, mergeRefs as m, defineContractComponent as n };
662
+ export { type AnyClassPluginFactory as A, type ElementType as E, type FactoryOptions as F, type MergeRecords as M, type NoVariants as N, type PolymorphicComponent as P, type RecipeMap as R, Slottable as S, type UnknownProps as U, type VariantMap as V, type EmptyRecord as a, type NoPreset as b, type AnyRecord as c, type ReactFactoryOptions as d, type PolymorphicGenerics as e, type ExtractPluginProps as f, type AnyFactoryOptions as g, type ElementRef as h, type PolymorphicProps as i, type PolymorphicWithAsChild as j, type PolymorphicWithRender as k, type RenderCallbackProps as l, type SlottableProps as m, mergeRefs as n, defineContractComponent as o };
@@ -2,11 +2,72 @@ import { RequireAtLeastOne, Simplify, ReadonlyDeep, OmitIndexSignature } from 't
2
2
  import { Diagnostics, DiagnosticInput, DiagnosticsMode } from '../_shared/diagnostics.js';
3
3
  import { JSX } from 'solid-js';
4
4
 
5
+ /**
6
+ * A string-keyed object whose values are of type `T`.
7
+ */
5
8
  type StringMap<T = unknown> = Record<string, T>;
9
+ /**
10
+ * A string-keyed object with values of unknown type.
11
+ */
6
12
  type AnyRecord = StringMap<unknown>;
13
+ /**
14
+ * An object type with no named properties.
15
+ *
16
+ * Unlike `{}`, this excludes arbitrary properties during type operations while
17
+ * still satisfying `extends object`.
18
+ */
7
19
  type EmptyRecord = Record<never, never>;
8
- /** A compound component's named sub-components, e.g. `{ Header, Content, Footer }`. */
20
+ /**
21
+ * A compound component's named sub-components, for example
22
+ * `{ Header, Content, Footer }`.
23
+ */
9
24
  type SubComponentMap = Readonly<AnyRecord>;
25
+ /**
26
+ * Default `Variants` type for components that declare no variants.
27
+ *
28
+ * Structurally identical to `Readonly<EmptyRecord>`, but named separately so
29
+ * editor hovers remain self-descriptive.
30
+ */
31
+ type NoVariants = Readonly<EmptyRecord>;
32
+ /**
33
+ * Default `TPreset` type for components that declare no named presets.
34
+ *
35
+ * Structurally identical to `Readonly<EmptyRecord>`, but named separately so
36
+ * editor hovers remain self-descriptive.
37
+ */
38
+ type NoPreset = Readonly<EmptyRecord>;
39
+ /**
40
+ * Fallback for `ExtractPluginProps<TPlugin>` when a plugin contributes no
41
+ * props, including the no-plugin case.
42
+ *
43
+ * Structurally identical to `EmptyRecord`, but named separately so editor
44
+ * hovers remain self-descriptive.
45
+ */
46
+ type NoPluginProps = EmptyRecord;
47
+ /**
48
+ * Determines whether an object type should be treated as empty.
49
+ *
50
+ * `keyof T` ignores call and construct signatures...
51
+ */
52
+ type IsEmptyRecord<T extends object> = T extends (...args: never[]) => unknown ? false : T extends new (...args: never[]) => unknown ? false : keyof T extends never ? true : false;
53
+ /**
54
+ * Merges two object types while eliding empty operands.
55
+ *
56
+ * If either operand is {@link EmptyRecord}, the other operand is returned
57
+ * directly instead of producing intersections such as
58
+ * `Component & EmptyRecord` in editor hovers.
59
+ *
60
+ * Unlike a homomorphic mapped type (for example `Simplify<T>`), this preserves
61
+ * call and construct signatures. Many component types are callable objects,
62
+ * and mapped types silently discard those signatures.
63
+ *
64
+ * @remarks
65
+ * Instantiate `MergeRecords` directly. Introducing an intermediate alias for
66
+ * one operand (for example `type C = PolymorphicComponent<G>`) can prevent
67
+ * `IsEmptyRecord` from evaluating eagerly, which breaks assignability under
68
+ * `exactOptionalPropertyTypes`.
69
+ */
70
+ type MergeRecords<A extends object, B extends object> = IsEmptyRecord<A> extends true ? B : IsEmptyRecord<B> extends true ? A : A & B;
10
71
 
11
72
  type IntrinsicTag = keyof HTMLElementTagNameMap;
12
73
 
@@ -189,7 +250,7 @@ type ClassPluginFactory<TProps extends AnyRecord = EmptyRecord> = <V extends Var
189
250
  * wherever a factory's concrete plugin-props shape isn't tracked (factory generics,
190
251
  * capability wiring). */
191
252
  type AnyClassPluginFactory = ClassPluginFactory<AnyRecord> | undefined;
192
- type ExtractPluginProps<TPlugin extends AnyClassPluginFactory> = TPlugin extends ClassPluginFactory<infer T> ? string extends keyof T ? EmptyRecord : T : EmptyRecord;
253
+ type ExtractPluginProps<TPlugin extends AnyClassPluginFactory> = TPlugin extends ClassPluginFactory<infer T> ? string extends keyof T ? NoPluginProps : T : NoPluginProps;
193
254
 
194
255
  type AriaContext = {
195
256
  readonly tag: IntrinsicTag;
@@ -253,6 +314,12 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
253
314
  * `@praxis-kit/diagnostics`.
254
315
  */
255
316
  readonly diagnostics?: Diagnostics | DiagnosticsMode;
317
+ /**
318
+ * ARIA/accessibility rules evaluated against the resolved tag and props on every render.
319
+ * Each rule is a function receiving the current context and returning zero or more
320
+ * violations, some of which can carry an auto-applicable fix (see `createRemoveAttributeRule`
321
+ * and friends in `praxis-kit/contract`).
322
+ */
256
323
  readonly aria?: readonly AriaRule[];
257
324
  /**
258
325
  * Rules that need `AriaPolicyEngine`'s fix-application/caching machinery
@@ -264,6 +331,11 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
264
331
  * misleading `aria` name to get the machinery it needs.
265
332
  */
266
333
  readonly rules?: readonly AriaRule[];
334
+ /**
335
+ * Declares which children are valid, by name, match predicate, and cardinality (e.g. "at
336
+ * least 1, at most 4 `Button` children"). Open by default — children matching no rule are
337
+ * still allowed unless `exclusiveChildren` is set.
338
+ */
267
339
  readonly children?: readonly ChildRuleInput[];
268
340
  /**
269
341
  * When true, only children matching a `children` rule (or text, per `allowText`)
@@ -276,19 +348,49 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
276
348
  * or any listed rule. Default: true.
277
349
  */
278
350
  readonly allowText?: boolean;
351
+ /**
352
+ * Prop transforms composed with the component's own `normalize` (from `FactoryOptions`) and
353
+ * run before it. Unlike `normalize`, these live in the enforcement bucket because they
354
+ * typically encode a built-in HTML/ARIA fact rather than component-specific behavior.
355
+ */
279
356
  readonly props?: readonly PropNormalizer[];
280
357
  /** Restricts the `as` prop to this set of tags. Violations route through diagnostics. */
281
358
  readonly allowedAs?: readonly TAllowed[];
282
359
  };
283
360
 
284
361
  type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = {
362
+ /** Class applied to every instance regardless of variant selection. */
285
363
  readonly base?: ClassName;
364
+ /**
365
+ * Named variant groups (e.g. `intent`, `size`), each mapping its possible values to a
366
+ * class string. A consumer selects a value per group as a prop (`<Button intent="primary">`).
367
+ */
286
368
  readonly variants?: V;
369
+ /** Value used for a variant group when the consumer doesn't pass one explicitly. */
287
370
  readonly defaults?: Partial<DefaultVariants<V>>;
371
+ /**
372
+ * Applies an extra class only when a specific *combination* of variant selections matches —
373
+ * for cases `variants` alone can't express (e.g. `intent: 'primary'` + `size: 'lg'` together
374
+ * need a class neither variant would add on its own).
375
+ */
288
376
  readonly compounds?: readonly CompoundVariant<V>[];
377
+ /**
378
+ * Named bundles of variant values, selectable as a single unit via the `recipe` prop (e.g.
379
+ * `<Button recipe="cta">` instead of setting `intent`/`size` individually).
380
+ */
289
381
  readonly presets?: TPreset;
382
+ /** Maps a resolved tag directly to a raw class string, independent of the variant system. */
290
383
  readonly tags?: Readonly<TagMap>;
384
+ /**
385
+ * A `ClassPluginFactory` (e.g. the Tailwind layout pipeline) that extends class resolution
386
+ * with its own owned props, layered on top of `variants`/`presets`/`tags`.
387
+ */
291
388
  readonly plugin?: TPlugin;
389
+ /**
390
+ * A cache-key → resolved-class-string lookup for every statically-known variant
391
+ * combination, skipping runtime class computation entirely when a match is found. Normally
392
+ * generated by a build-time class-extraction plugin rather than hand-authored.
393
+ */
292
394
  readonly precomputedClasses?: Readonly<Record<string, string>>;
293
395
  };
294
396
 
@@ -297,11 +399,21 @@ type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
297
399
  }['normalize'];
298
400
  type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, RecipeMap<VariantMap>, AnyClassPluginFactory>;
299
401
  type FactoryOptions<TDefault extends ElementType = ElementType, Props extends AnyRecord = EmptyRecord, V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> = {
402
+ /** The intrinsic tag the component renders by default. Overridable per instance via `as`. */
300
403
  readonly tag?: TDefault;
404
+ /** Display name used in diagnostics, dev tools, and generated component naming. */
301
405
  readonly name?: string;
406
+ /** Values used for the component's own (non-variant) props when the consumer omits them. */
302
407
  readonly defaults?: Partial<NoInfer<Props>>;
408
+ /**
409
+ * A pure `(props) => props` transform run on every render, after `enforcement.props`'s
410
+ * normalizers see the same input. Use this for component-specific prop shaping — anything
411
+ * that depends on live instance state or the real DOM element belongs in `onElement` instead.
412
+ */
303
413
  readonly normalize?: NormalizeFn<NoInfer<Props>>;
414
+ /** Variant groups, base classes, presets, and the optional class-resolution plugin. */
304
415
  readonly styling?: StylingOptions<V, TPreset, TPlugin>;
416
+ /** ARIA rules, child-content contracts, and other runtime validation for this component. */
305
417
  readonly enforcement?: EnforcementOptions<TAllowed>;
306
418
  /**
307
419
  * Adapter-resolved diagnostics default, spread in by `resolveAdapterCommonOptions`. Not meant to
@@ -340,7 +452,7 @@ type UnknownProps = AnyRecord;
340
452
  type SolidElement = JSX.Element;
341
453
  type SlotRenderFn = (props: UnknownProps) => SolidElement;
342
454
 
343
- type SolidFactoryOptions<TDefault extends ElementType, Props extends UnknownProps, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
455
+ type SolidFactoryOptions<TDefault extends ElementType, Props extends UnknownProps, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = FactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
344
456
  /**
345
457
  * Return true for any prop key that should be consumed but not forwarded to the DOM.
346
458
  * Receives `runtime.options.variantKeys` as a convenience if needed.
@@ -383,8 +495,28 @@ type PolymorphicComponent<G extends PolymorphicGenerics> = {
383
495
  displayName?: string;
384
496
  };
385
497
 
386
- declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<Variants> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: SolidFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
498
+ /**
499
+ * Creates a polymorphic Solid component with praxis-kit contracts applied.
500
+ *
501
+ * ```tsx
502
+ * const Button = createContractComponent({
503
+ * tag: 'button',
504
+ * name: 'Button',
505
+ * styling: {
506
+ * base: 'btn',
507
+ * variants: { intent: { primary: 'btn--primary', ghost: 'btn--ghost' } },
508
+ * defaults: { intent: 'primary' },
509
+ * },
510
+ * })
511
+ *
512
+ * <Button intent="ghost" as="a" href="/home">Home</Button>
513
+ * ```
514
+ *
515
+ * `ref` is forwarded as an ordinary Solid ref callback. Pass `subComponents` to attach named
516
+ * sub-components (`Card.Header`) and `onElement` to run setup once the real DOM element exists.
517
+ */
518
+ declare function createContractComponent<TDefault extends ElementType, Props extends UnknownProps = EmptyRecord, Variants extends Readonly<VariantMap> = NoVariants, TPreset extends RecipeMap<Variants> = NoPreset, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: SolidFactoryOptions<TDefault, Props, Variants, TPreset, TPlugin> & {
387
519
  readonly subComponents?: TSubComponents;
388
- }): PolymorphicComponent<PolymorphicGenerics<TDefault, Props & ExtractPluginProps<TPlugin>, Variants, TPreset>> & TSubComponents;
520
+ }): MergeRecords<PolymorphicComponent<PolymorphicGenerics<TDefault, MergeRecords<Props, ExtractPluginProps<TPlugin>>, Variants, TPreset>>, TSubComponents>;
389
521
 
390
522
  export { type AnyFactoryOptions, type ElementRef, type ElementType, type EmptyRecord, type PolymorphicComponent, type PolymorphicGenerics, type PolymorphicProps, type SolidFactoryOptions, createContractComponent, defineContractComponent };
@@ -1,3 +1,16 @@
1
+ <!--
2
+ @component
3
+ Renders a `createContractComponent` bundle. Every praxis-kit component in the Svelte adapter
4
+ is a bundle passed to this component via the `bundle` prop:
5
+
6
+ ```svelte
7
+ <Polymorphic {bundle} intent="ghost" as="a" href="/home">Home</Polymorphic>
8
+ ```
9
+
10
+ Resolves the tag (`as` or the bundle's default), variant classes, filtered props, and ARIA
11
+ attributes, then renders the result via `<svelte:element>` — or, with `asChild`, renders
12
+ `children` as a snippet receiving the resolved props instead of a host element.
13
+ -->
1
14
  <script module lang="ts">
2
15
  declare const process: { env: { NODE_ENV: string } }
3
16
  </script>