/** * Call-site class value — mirrors Svelte 5's `ClassValue` (clsx shape): * strings, nested arrays, `{ class: condition }` records, falsy values * (dropped). Accepted by `cx()` and every `class` input (component props, * slot-function overrides). Config-side values (variants, compound classes) * deliberately do NOT take the record form — there an object is a slot map. */ type ClassInput = string | number | ClassInput[] | Record | undefined | null | false; type VariantPropType = V extends Record ? 'true' extends Extract ? boolean | Exclude, 'true' | 'false'> : Extract : never; type VariantPropsMap>> = { [K in keyof V]?: VariantPropType; }; type SlotFn>> = (props?: VariantPropsMap & { class?: ClassInput; }) => string; /** Per-slot class map used config-side (variant values, compound classes). */ type SlotClassMap = { [K in keyof S]?: string | string[]; }; /** * Validation companion for slot-mode `variants`: intersecting the inferred * `V` with this mapped type turns every slot-map key that is not a declared * slot into an assignability error AT that key — the compiler catches * `wrapeer` typos that structural checking alone would let through. */ type ValidSlotVariants = { [A in keyof V]: { [Q in keyof V[A]]: V[A][Q] extends string | readonly string[] ? unknown : { [K in keyof V[A][Q]]: K extends keyof S ? unknown : ['unknown slot', K]; }; }; }; type CompoundVariant>> = { [K in keyof V]?: VariantPropType | VariantPropType[]; } & { class?: string | string[]; }; type SlotCompoundVariant>, S> = { [K in keyof V]?: VariantPropType | VariantPropType[]; } & { class?: string | string[] | SlotClassMap; }; export type TVProps>> = VariantPropsMap & { class?: ClassInput; }; export type VariantProps unknown> = Omit[0], undefined>, 'class'>; /** * Extracts the slot-name union from a slotted `tv()` config function — the * companion to {@link VariantProps}. The slot-mode overload returns * `(props?) => { [K in keyof S]: SlotFn }`, so `keyof ReturnType` is exactly * the set of slot names a component declares in `tv({ slots: … })`. * * Use it to type a component's `slotClasses` prop from the single source of * truth (its `*.variants.ts`) instead of hand-maintaining a parallel union * that silently drifts when a slot is added or renamed: * * @example * // button.variants.ts * export type ButtonSlots = SlotNames; // 'base' | 'content' | 'spinner' * // index.ts * slotClasses?: Partial>; */ export type SlotNames unknown> = keyof ReturnType & string; /** * Concatenate class inputs into a single string. Accepts strings, nested * arrays, `{ class: condition }` records (Svelte 5 `ClassValue` / clsx * shape — keys with truthy values are included) and falsy values (filtered * out). Trims and joins with single spaces. * * Note: `cx()` does **not** deduplicate — `cx('foo', 'foo bar')` returns * `'foo foo bar'`. Duplicate Tailwind classes are harmless at runtime * (CSS ignores them), but DOM output is longer than after a `twMerge` * pass. See `docs/ARCHITECTURE.md` "tv() engine — explicit trade-offs". */ export declare function cx(...inputs: ClassInput[]): string; /** * @internal Exported for `scripts/variants-lint.ts`, which compares this table * against the real Tailwind compiler. Not part of the package's public API — * `utils/index.ts` does not re-export it. */ export declare function tailwindBucket(cls: string): string | null; /** * Merge class strings with the same Tailwind conflict resolution `tv()` * applies between stages: a later source's classes strip any earlier class * that shares their conflict bucket, so the last source wins per bucket. * Within a single source, order is preserved and conflicts fall through to * the CSS cascade. Falsy / empty sources are skipped. * * Reuses `tokenize` + `stripConflicts`. Powers the BlocksProvider slot-class * cascade (`resolveSlotClasses`) so a conditional `overrides` entry * deterministically defeats an unconditional `slotClasses` entry in the same * bucket — instead of emitting both and leaving the winner to stylesheet * order. * * It is also the `unstyled` half of every component's override ladder: with * the library classes gone the consumer's rungs (`slotClasses`, then `class`) * are all that is left, and they have to resolve against each other exactly * as they do inside `tv()` — otherwise the same two inputs would render * differently depending on the flag. * * @example * // The two consumer rungs, later winning per bucket. * resolveClassChain('py-8', 'py-4'); // → 'py-4' */ export declare function resolveClassChain(...sources: (string | false | null | undefined)[]): string; /** * The variant values a component's own condition object stands for: every axis * the component **names**, at its written value, or at the config's * `defaultVariants` value where it wrote `undefined`. * * Deliberately **not** `tv()`'s fold (`{ ...defaultVariants, ...stripUndefined(props) }`), * and the difference is the whole point. `tv()` may fill in an axis the object * never mentioned, because it styles one slot call at a time and the caller can * hand it more per call. A conditional `overrides` rule is matched **once per * component** against **one** object, so an axis that object does not carry is * an axis the component cannot speak for — filling it in there states something * about the component that nothing measured. * * Two shapes in this tree make that concrete, both measured: * * - *A shared config.* `segmentGroupVariants` declares `disabled` and * `SegmentGroup` passes it; `SegmentItem` resolves against the same config * and names only `size`/`variant`/`tier`. Folding the config's * `disabled: false` into the item's condition hands the item its * **sibling's** axis: measured, `{ disabled: false }` then painted both items * of a group, the disabled one included. Nine configs are shared by 2–5 * components, over 28 of the 90 call sites. * - *A per-slot-call axis.* `inputVariants.iconPosition` defaults to `'left'` * and `Input` never puts it in `variantProps` — it passes it at each of its * three `iconContainer` calls. Folded in, `{ iconPosition: 'left' }` matched * an Input carrying only a **right** icon, and one resolved record is applied * to every slot alike. * * The key is the claim: `Card` writes `disabled: disabled || undefined`, so the * key is there and the default answers for it — `{ disabled: false }` fires. * `SegmentItem` never writes the key, so nothing is folded and no rule can * claim it. * * @example * effectiveVariants(cardVariants.config, { disabled: undefined }); // → { disabled: false } * effectiveVariants(inputVariants.config, { size: 'md' }); // → { size: 'md' } — no iconPosition */ export declare function effectiveVariants(config: TVConfig, props: Record): Record; export declare function matchesCompound(compound: Record, effectiveProps: Record): boolean; /** * Config record exposed on every resolver as `.config` (tooling/linting). * Read-only by convention: it is the live config object — mutating it after * init bypasses validateTvConfig. Tools must treat it as immutable. */ export type TVConfig = { readonly base?: string | string[]; readonly slots?: Record; readonly variants?: Record>; readonly compoundVariants?: Record[]; readonly defaultVariants?: Record; }; export declare function tv> = {}>(config: { base?: string | string[]; variants?: V; compoundVariants?: CompoundVariant[]; defaultVariants?: VariantPropsMap; }): ((props?: TVProps) => string) & { readonly config: TVConfig; }; export declare function tv> = {}, S extends Record = Record>(config: { slots: S; variants?: V & ValidSlotVariants; compoundVariants?: SlotCompoundVariant[]; defaultVariants?: VariantPropsMap; }): ((props?: VariantPropsMap) => { [K in keyof S]: SlotFn; }) & { readonly config: TVConfig; }; export {};