@urbicon-ui/blocks 6.19.3 → 6.21.1

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 (105) hide show
  1. package/dist/components/Calendar/Calendar.svelte +3 -0
  2. package/dist/components/Calendar/calendar.variants.d.ts +132 -419
  3. package/dist/components/CommandPalette/commandPalette.variants.d.ts +24 -51
  4. package/dist/components/CompositionBar/CompositionBar.svelte +6 -1
  5. package/dist/components/CompositionBar/composition-bar.variants.d.ts +23 -83
  6. package/dist/components/CompositionBar/composition-bar.variants.js +9 -3
  7. package/dist/components/EmptyState/emptyState.variants.d.ts +11 -27
  8. package/dist/components/FileUpload/fileUpload.variants.d.ts +24 -93
  9. package/dist/components/Guide/GuidePanel.svelte +38 -0
  10. package/dist/components/Guide/guide.variants.d.ts +69 -124
  11. package/dist/components/Guide/guide.variants.js +6 -1
  12. package/dist/components/Planner/planner.variants.d.ts +23 -94
  13. package/dist/components/Sankey/sankey.variants.d.ts +15 -48
  14. package/dist/components/SidebarLayout/sidebar-layout.variants.d.ts +10 -38
  15. package/dist/components/SidebarLayout/sidebar-layout.variants.js +12 -2
  16. package/dist/components/ThemeSwitcher/themeSwitcher.variants.d.ts +9 -36
  17. package/dist/i18n/index.d.ts +2 -372
  18. package/dist/icons/IconProvider.svelte +3 -0
  19. package/dist/internal/charts/variants.d.ts +18 -31
  20. package/dist/mint/styles.css +1 -1
  21. package/dist/primitives/Accordion/Accordion.svelte +14 -0
  22. package/dist/primitives/Accordion/AccordionItem.svelte +9 -3
  23. package/dist/primitives/Accordion/accordion.variants.d.ts +12 -43
  24. package/dist/primitives/Accordion/accordion.variants.js +6 -2
  25. package/dist/primitives/Accordion/index.d.ts +12 -0
  26. package/dist/primitives/Alert/alert.variants.d.ts +21 -62
  27. package/dist/primitives/Avatar/Avatar.svelte +74 -48
  28. package/dist/primitives/Avatar/avatar.variants.d.ts +34 -128
  29. package/dist/primitives/Avatar/avatar.variants.js +43 -18
  30. package/dist/primitives/Avatar/index.d.ts +6 -1
  31. package/dist/primitives/Badge/badge.variants.d.ts +19 -92
  32. package/dist/primitives/Badge/badge.variants.js +11 -4
  33. package/dist/primitives/Breadcrumb/breadcrumb.variants.d.ts +13 -40
  34. package/dist/primitives/Button/button.variants.d.ts +19 -98
  35. package/dist/primitives/Button/button.variants.js +13 -9
  36. package/dist/primitives/ButtonGroup/buttongroup.variants.d.ts +9 -29
  37. package/dist/primitives/ButtonGroup/buttongroup.variants.js +3 -1
  38. package/dist/primitives/Card/card.variants.d.ts +14 -81
  39. package/dist/primitives/Checkbox/checkbox.variants.d.ts +18 -77
  40. package/dist/primitives/Collapsible/Collapsible.svelte +13 -0
  41. package/dist/primitives/Collapsible/collapsible.variants.d.ts +11 -40
  42. package/dist/primitives/Collapsible/collapsible.variants.js +7 -2
  43. package/dist/primitives/Collapsible/index.d.ts +16 -0
  44. package/dist/primitives/Combobox/combobox.variants.d.ts +24 -78
  45. package/dist/primitives/ConfirmDialog/index.d.ts +12 -0
  46. package/dist/primitives/Dialog/Dialog.svelte +23 -10
  47. package/dist/primitives/Dialog/dialog.variants.d.ts +15 -54
  48. package/dist/primitives/Drawer/Drawer.svelte +24 -11
  49. package/dist/primitives/Drawer/drawer.variants.d.ts +14 -49
  50. package/dist/primitives/Input/input.variants.d.ts +32 -139
  51. package/dist/primitives/Input/input.variants.js +12 -10
  52. package/dist/primitives/JourneyTimeline/journey-timeline.variants.d.ts +55 -197
  53. package/dist/primitives/JourneyTimeline/journey-timeline.variants.js +4 -1
  54. package/dist/primitives/Menu/MenuSubmenu.svelte +1 -0
  55. package/dist/primitives/Menu/menu.variants.d.ts +29 -93
  56. package/dist/primitives/Pagination/pagination.variants.d.ts +16 -54
  57. package/dist/primitives/Pagination/pagination.variants.js +16 -14
  58. package/dist/primitives/Popover/Popover.svelte +1 -1
  59. package/dist/primitives/Popover/popover.variants.d.ts +4 -2
  60. package/dist/primitives/Progress/progress.variants.d.ts +19 -83
  61. package/dist/primitives/Progress/progress.variants.js +2 -1
  62. package/dist/primitives/RadioGroup/radioGroup.variants.d.ts +28 -109
  63. package/dist/primitives/SegmentGroup/SegmentGroup.svelte +14 -6
  64. package/dist/primitives/SegmentGroup/segmentgroup.variants.d.ts +12 -53
  65. package/dist/primitives/SegmentGroup/segmentgroup.variants.js +10 -10
  66. package/dist/primitives/Select/Select.svelte +1 -1
  67. package/dist/primitives/Select/select.variants.d.ts +45 -134
  68. package/dist/primitives/Select/select.variants.js +6 -4
  69. package/dist/primitives/Separator/separator.variants.d.ts +4 -2
  70. package/dist/primitives/Sidebar/Sidebar.svelte +10 -15
  71. package/dist/primitives/Sidebar/sidebar.variants.d.ts +11 -29
  72. package/dist/primitives/Skeleton/skeleton.variants.d.ts +9 -36
  73. package/dist/primitives/Slider/slider.variants.d.ts +37 -136
  74. package/dist/primitives/Slider/slider.variants.js +8 -5
  75. package/dist/primitives/Spinner/spinner.variants.d.ts +38 -135
  76. package/dist/primitives/Stepper/stepper.variants.d.ts +32 -130
  77. package/dist/primitives/Tab/Tab.svelte +17 -6
  78. package/dist/primitives/Tab/tab.variants.d.ts +17 -78
  79. package/dist/primitives/Textarea/textarea.variants.d.ts +27 -112
  80. package/dist/primitives/Textarea/textarea.variants.js +12 -10
  81. package/dist/primitives/Toast/Toaster.svelte +16 -3
  82. package/dist/primitives/Toast/index.d.ts +9 -0
  83. package/dist/primitives/Toast/toast.variants.d.ts +22 -78
  84. package/dist/primitives/Toggle/toggle.variants.d.ts +17 -71
  85. package/dist/primitives/Toolbar/toolbar.variants.d.ts +9 -60
  86. package/dist/primitives/Tooltip/Tooltip.svelte +11 -0
  87. package/dist/primitives/Tooltip/index.d.ts +14 -0
  88. package/dist/primitives/Tooltip/tooltip.variants.d.ts +11 -50
  89. package/dist/primitives/Tooltip/tooltip.variants.js +7 -1
  90. package/dist/style/foundation.css +4 -1
  91. package/dist/style/interaction.css +40 -12
  92. package/dist/style/semantic.css +2 -2
  93. package/dist/style/themes/index.css +1 -1
  94. package/dist/system/attachments/ContextIsolation.svelte +2 -0
  95. package/dist/utils/index.d.ts +2 -1
  96. package/dist/utils/index.js +5 -0
  97. package/dist/utils/overlay.d.ts +33 -4
  98. package/dist/utils/overlay.js +61 -18
  99. package/dist/utils/roving.d.ts +15 -0
  100. package/dist/utils/roving.js +44 -0
  101. package/dist/utils/use-floating-panel.svelte.d.ts +4 -1
  102. package/dist/utils/use-floating-panel.svelte.js +23 -1
  103. package/dist/utils/variants.d.ts +60 -16
  104. package/dist/utils/variants.js +424 -123
  105. package/package.json +9 -6
@@ -37,23 +37,37 @@ export function trapFocus(event, container) {
37
37
  }
38
38
  let bodyScrollLockCount = 0;
39
39
  let savedBodyOverflow;
40
+ /**
41
+ * Acquire a shared body-scroll lock; returns the matching release function.
42
+ *
43
+ * The count is module-global — the body stays `overflow: hidden` while at
44
+ * least one overlay (Dialog, Drawer, mobile Sidebar) holds a lock. Ownership
45
+ * is per-acquisition: only the returned release can decrement this holder's
46
+ * share, and it is idempotent, so calling it from multiple teardown paths
47
+ * (outro end + onDestroy safety net) releases exactly once and can never free
48
+ * a lock held by another overlay instance. There is deliberately no standalone
49
+ * `unlock` export — an unpaired decrement is exactly the bug this design
50
+ * removes.
51
+ */
40
52
  export function lockBodyScroll() {
41
53
  if (!isBrowser)
42
- return;
54
+ return () => { };
43
55
  if (bodyScrollLockCount === 0) {
44
56
  savedBodyOverflow = document.body.style.overflow;
45
57
  document.body.style.overflow = 'hidden';
46
58
  }
47
59
  bodyScrollLockCount += 1;
48
- }
49
- export function unlockBodyScroll() {
50
- if (!isBrowser)
51
- return;
52
- bodyScrollLockCount = Math.max(0, bodyScrollLockCount - 1);
53
- if (bodyScrollLockCount === 0) {
54
- document.body.style.overflow = savedBodyOverflow ?? '';
55
- savedBodyOverflow = undefined;
56
- }
60
+ let released = false;
61
+ return () => {
62
+ if (released)
63
+ return;
64
+ released = true;
65
+ bodyScrollLockCount -= 1;
66
+ if (bodyScrollLockCount === 0) {
67
+ document.body.style.overflow = savedBodyOverflow ?? '';
68
+ savedBodyOverflow = undefined;
69
+ }
70
+ };
57
71
  }
58
72
  export function focusFirstElement(container) {
59
73
  if (!isBrowser)
@@ -64,21 +78,50 @@ export function focusFirstElement(container) {
64
78
  focusable[0].focus();
65
79
  });
66
80
  }
81
+ /**
82
+ * Promote a `<dialog>` to the top layer and move focus into its panel.
83
+ *
84
+ * **Precondition: `dialogEl` must already be bound.** Call this only once the
85
+ * `bind:this` target exists — e.g. from `tick().then(...)` in the opener effect,
86
+ * after the `{#if isVisible}` block has rendered. An earlier version deferred the
87
+ * `showModal()` call with its own `tick()` in the hope of waiting for the bind,
88
+ * but the ref is captured by value: if the caller passes an as-yet-unbound
89
+ * `dialogEl`, the `tick()` still runs `undefined?.showModal()` — a silent no-op
90
+ * that left Dialog/Drawer never actually modal. Sequencing is the caller's job;
91
+ * the top-layer promotion here is synchronous (only the focus move inside
92
+ * `focusFirstElement` defers by a tick).
93
+ *
94
+ * Acquires a body-scroll lock and returns its release function — the caller
95
+ * owns the lock and must call the release on every teardown path (outro end
96
+ * and destroy; it is idempotent, so overlapping paths are safe). Pass it back
97
+ * through {@link closeDialogModal} on the regular close path.
98
+ */
67
99
  export function showDialogModal(dialogEl, panelEl) {
68
100
  if (!isBrowser)
69
- return;
70
- lockBodyScroll();
71
- tick().then(() => {
72
- dialogEl?.showModal();
73
- focusFirstElement(panelEl);
74
- });
101
+ return () => { };
102
+ if (!dialogEl) {
103
+ // Precondition violation: nothing to promote, so also take no lock — a
104
+ // lock acquired here would scroll-freeze the page under a non-modal
105
+ // overlay even if the caller dutifully releases the returned handle.
106
+ if (import.meta.env?.DEV) {
107
+ console.warn('[blocks] showDialogModal called before the <dialog> ref was bound — ' +
108
+ 'the overlay will not enter the top layer (no :modal, no initial focus, ' +
109
+ 'no scroll lock). Defer the call until after the render that binds it ' +
110
+ '(e.g. tick().then(...)).');
111
+ }
112
+ return () => { };
113
+ }
114
+ const releaseScrollLock = lockBodyScroll();
115
+ dialogEl.showModal();
116
+ focusFirstElement(panelEl);
117
+ return releaseScrollLock;
75
118
  }
76
- export function closeDialogModal(dialogEl, previouslyFocused) {
119
+ export function closeDialogModal(dialogEl, previouslyFocused, releaseScrollLock) {
77
120
  if (!isBrowser)
78
121
  return;
79
122
  if (dialogEl?.open)
80
123
  dialogEl.close();
81
- unlockBodyScroll();
124
+ releaseScrollLock?.();
82
125
  previouslyFocused?.focus();
83
126
  }
84
127
  /**
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The next enabled index walking `dir` (+1 / -1) from `from`, wrapping around.
3
+ *
4
+ * - `from` may be -1 (nothing active yet): the walk anchors just off the leading
5
+ * edge, so the first probe is index 0 for `dir` +1 and the last index for -1.
6
+ * - Returns `from` when it is a valid index but every *other* item is disabled
7
+ * (nowhere to move — the caller's `next !== from` guard makes it a no-op).
8
+ * - Returns -1 when there is no enabled item at all (or `length` is 0).
9
+ */
10
+ export declare function nextEnabledIndex(length: number, from: number, dir: 1 | -1, isDisabled: (index: number) => boolean): number;
11
+ /**
12
+ * The first enabled index scanning from an edge: `dir` +1 from the start (Home),
13
+ * -1 from the end (End). Returns -1 when every item is disabled.
14
+ */
15
+ export declare function edgeEnabledIndex(length: number, dir: 1 | -1, isDisabled: (index: number) => boolean): number;
@@ -0,0 +1,44 @@
1
+ // Roving-focus index helpers for keyboard navigation across a set of items where
2
+ // some may be disabled. Shared by the button-based composite widgets (Tab,
3
+ // SegmentGroup): a disabled item there renders a native `<button disabled>`,
4
+ // which can't hold focus, so navigation must step over it — otherwise selection
5
+ // strands on an unfocusable item (aria state set, focus stuck on the previous
6
+ // one). RadioGroup doesn't use these: its items are native radios, so it filters
7
+ // with a `:not(:disabled)` DOM query the browser understands directly.
8
+ //
9
+ // Pure index math — the caller supplies `isDisabled(index)`; nothing here reads
10
+ // the DOM, so the edge cases (wrap-around, all-disabled, nothing-active) are
11
+ // unit-tested in isolation.
12
+ /**
13
+ * The next enabled index walking `dir` (+1 / -1) from `from`, wrapping around.
14
+ *
15
+ * - `from` may be -1 (nothing active yet): the walk anchors just off the leading
16
+ * edge, so the first probe is index 0 for `dir` +1 and the last index for -1.
17
+ * - Returns `from` when it is a valid index but every *other* item is disabled
18
+ * (nowhere to move — the caller's `next !== from` guard makes it a no-op).
19
+ * - Returns -1 when there is no enabled item at all (or `length` is 0).
20
+ */
21
+ export function nextEnabledIndex(length, from, dir, isDisabled) {
22
+ if (length <= 0)
23
+ return -1;
24
+ const inRange = from >= 0 && from < length;
25
+ const anchored = inRange ? from : dir === 1 ? -1 : length;
26
+ for (let i = 1; i <= length; i++) {
27
+ const idx = (((anchored + dir * i) % length) + length) % length;
28
+ if (!isDisabled(idx))
29
+ return idx;
30
+ }
31
+ return inRange ? from : -1;
32
+ }
33
+ /**
34
+ * The first enabled index scanning from an edge: `dir` +1 from the start (Home),
35
+ * -1 from the end (End). Returns -1 when every item is disabled.
36
+ */
37
+ export function edgeEnabledIndex(length, dir, isDisabled) {
38
+ for (let i = 0; i < length; i++) {
39
+ const idx = dir === 1 ? i : length - 1 - i;
40
+ if (!isDisabled(idx))
41
+ return idx;
42
+ }
43
+ return -1;
44
+ }
@@ -65,7 +65,10 @@ export interface FloatingPanelState {
65
65
  * `<dialog>`, where a second top-layer element is invisible on iOS/WebKit
66
66
  * (Codeberg #23) — the panel then renders `position: fixed` in place. Callers
67
67
  * mirror the returned `topLayer`/`strategy` in their markup via per-property
68
- * `style:` directives and {@link floatingPanelHidden}.
68
+ * `style:` directives and {@link floatingPanelHidden}. Stacking needs no
69
+ * caller wiring: in the two in-place modes the helper itself stamps the
70
+ * panel's `z-index` imperatively (see the `zIndex` derivation below), so an
71
+ * in-place panel always paints above later positioned siblings.
69
72
  *
70
73
  * The Floating-UI `size` middleware feeds the room actually left between the
71
74
  * anchor and the (visual) viewport edge into `--blocks-overlay-available-height`.
@@ -17,7 +17,10 @@ import { isAnchoredInModalDialog } from './overlay.js';
17
17
  * `<dialog>`, where a second top-layer element is invisible on iOS/WebKit
18
18
  * (Codeberg #23) — the panel then renders `position: fixed` in place. Callers
19
19
  * mirror the returned `topLayer`/`strategy` in their markup via per-property
20
- * `style:` directives and {@link floatingPanelHidden}.
20
+ * `style:` directives and {@link floatingPanelHidden}. Stacking needs no
21
+ * caller wiring: in the two in-place modes the helper itself stamps the
22
+ * panel's `z-index` imperatively (see the `zIndex` derivation below), so an
23
+ * in-place panel always paints above later positioned siblings.
21
24
  *
22
25
  * The Floating-UI `size` middleware feeds the room actually left between the
23
26
  * anchor and the (visual) viewport edge into `--blocks-overlay-available-height`.
@@ -39,12 +42,29 @@ export function useFloatingPanel(opts) {
39
42
  // inline mode (`portal=false`) falls back to `absolute`.
40
43
  const topLayer = $derived(portalHint && !inModalDialog);
41
44
  const strategy = $derived(portalHint ? 'fixed' : 'absolute');
45
+ // z-index for the two in-place modes; the top layer needs none (it stacks by
46
+ // insertion order and ignores z-index). An in-place panel stacks at `auto`,
47
+ // so any positioned element AFTER it in the DOM (Button/Input/Select roots
48
+ // are all `position: relative`) would paint over the open panel in document
49
+ // order — the table FilterMenu overdraw. Stamped imperatively on the show
50
+ // path alongside Floating UI's `left`/`top` writes, never via a `style:`
51
+ // directive: Svelte reserves a directive's property even while its value is
52
+ // null, which would strip a consumer-supplied `style="z-index: …"` in
53
+ // top-layer mode. Set-only (no unset on hide/mode-flip — a leftover value is
54
+ // inert in the top layer and wanted in place). The literal fallback keeps
55
+ // the panel stacked when a consumer's Tailwind build prunes the unused
56
+ // `@theme` token (an unresolvable var() computes to `auto` and would
57
+ // silently revert the fix). Consumers override via an important utility on
58
+ // the panel slot (e.g. `slotClasses={{ listbox: 'z-20!' }}`) — plain
59
+ // utilities lose to this inline style by design, like `position`/`display`.
60
+ const zIndex = $derived(topLayer ? null : 'var(--z-dropdown, 1150)');
42
61
  $effect(() => {
43
62
  const ref = opts.reference();
44
63
  const floating = opts.floating();
45
64
  const isOpen = opts.open();
46
65
  const useTopLayer = topLayer;
47
66
  const positionStrategy = strategy;
67
+ const zIndexValue = zIndex;
48
68
  const placement = opts.placement?.() ?? 'bottom-start';
49
69
  const offsetDistance = opts.offsetDistance?.() ?? 4;
50
70
  const shiftPadding = opts.shiftPadding?.() ?? 8;
@@ -80,6 +100,8 @@ export function useFloatingPanel(opts) {
80
100
  }
81
101
  return;
82
102
  }
103
+ if (zIndexValue)
104
+ floating.style.zIndex = zIndexValue;
83
105
  if (useTopLayer && !floating.matches(':popover-open')) {
84
106
  try {
85
107
  floating.showPopover();
@@ -1,21 +1,49 @@
1
- type ClassInput = string | ClassInput[] | undefined | null | false;
1
+ /**
2
+ * Call-site class value — mirrors Svelte 5's `ClassValue` (clsx shape):
3
+ * strings, nested arrays, `{ class: condition }` records, falsy values
4
+ * (dropped). Accepted by `cx()` and every `class` input (component props,
5
+ * slot-function overrides). Config-side values (variants, compound classes)
6
+ * deliberately do NOT take the record form — there an object is a slot map.
7
+ */
8
+ type ClassInput = string | number | ClassInput[] | Record<string, boolean | undefined | null> | undefined | null | false;
2
9
  type VariantPropType<V> = V extends Record<string, unknown> ? 'true' extends Extract<keyof V, string> ? boolean | Exclude<Extract<keyof V, string>, 'true' | 'false'> : Extract<keyof V, string> : never;
3
10
  type VariantPropsMap<V extends Record<string, Record<string, unknown>>> = {
4
11
  [K in keyof V]?: VariantPropType<V[K]>;
5
12
  };
6
- type TVClassOverrides = {
13
+ type SlotFn<V extends Record<string, Record<string, unknown>>> = (props?: VariantPropsMap<V> & {
7
14
  class?: ClassInput;
8
- className?: ClassInput;
15
+ }) => string;
16
+ /** Per-slot class map used config-side (variant values, compound classes). */
17
+ type SlotClassMap<S> = {
18
+ [K in keyof S]?: string | string[];
19
+ };
20
+ /**
21
+ * Validation companion for slot-mode `variants`: intersecting the inferred
22
+ * `V` with this mapped type turns every slot-map key that is not a declared
23
+ * slot into an assignability error AT that key — the compiler catches
24
+ * `wrapeer` typos that structural checking alone would let through.
25
+ */
26
+ type ValidSlotVariants<V, S> = {
27
+ [A in keyof V]: {
28
+ [Q in keyof V[A]]: V[A][Q] extends string | readonly string[] ? unknown : {
29
+ [K in keyof V[A][Q]]: K extends keyof S ? unknown : ['unknown slot', K];
30
+ };
31
+ };
9
32
  };
10
- type SlotFn<V extends Record<string, Record<string, unknown>>> = (props?: VariantPropsMap<V> & TVClassOverrides) => string;
11
33
  type CompoundVariant<V extends Record<string, Record<string, unknown>>> = {
12
34
  [K in keyof V]?: VariantPropType<V[K]> | VariantPropType<V[K]>[];
13
35
  } & {
14
- class?: string | string[] | Record<string, string | string[]>;
15
- className?: string | string[] | Record<string, string | string[]>;
36
+ class?: string | string[];
16
37
  };
17
- export type TVProps<V extends Record<string, Record<string, unknown>>> = VariantPropsMap<V> & TVClassOverrides;
18
- export type VariantProps<T extends (...args: never[]) => unknown> = Omit<Exclude<Parameters<T>[0], undefined>, 'class' | 'className'>;
38
+ type SlotCompoundVariant<V extends Record<string, Record<string, unknown>>, S> = {
39
+ [K in keyof V]?: VariantPropType<V[K]> | VariantPropType<V[K]>[];
40
+ } & {
41
+ class?: string | string[] | SlotClassMap<S>;
42
+ };
43
+ export type TVProps<V extends Record<string, Record<string, unknown>>> = VariantPropsMap<V> & {
44
+ class?: ClassInput;
45
+ };
46
+ export type VariantProps<T extends (...args: never[]) => unknown> = Omit<Exclude<Parameters<T>[0], undefined>, 'class'>;
19
47
  /**
20
48
  * Extracts the slot-name union from a slotted `tv()` config function — the
21
49
  * companion to {@link VariantProps}. The slot-mode overload returns
@@ -35,8 +63,9 @@ export type VariantProps<T extends (...args: never[]) => unknown> = Omit<Exclude
35
63
  export type SlotNames<T extends (...args: never[]) => unknown> = keyof ReturnType<T> & string;
36
64
  /**
37
65
  * Concatenate class inputs into a single string. Accepts strings, nested
38
- * arrays, and falsy values (filtered out). Trims and joins with single
39
- * spaces.
66
+ * arrays, `{ class: condition }` records (Svelte 5 `ClassValue` / clsx
67
+ * shape — keys with truthy values are included) and falsy values (filtered
68
+ * out). Trims and joins with single spaces.
40
69
  *
41
70
  * Note: `cx()` does **not** deduplicate — `cx('foo', 'foo bar')` returns
42
71
  * `'foo foo bar'`. Duplicate Tailwind classes are harmless at runtime
@@ -59,19 +88,34 @@ export declare function cx(...inputs: ClassInput[]): string;
59
88
  */
60
89
  export declare function resolveClassChain(...sources: (string | null | undefined)[]): string;
61
90
  export declare function matchesCompound(compound: Record<string, unknown>, effectiveProps: Record<string, unknown>): boolean;
62
- export declare function tv<V extends Record<string, Record<string, unknown>> = {}>(config: {
91
+ /**
92
+ * Config record exposed on every resolver as `.config` (tooling/linting).
93
+ * Read-only by convention: it is the live config object — mutating it after
94
+ * init bypasses validateTvConfig. Tools must treat it as immutable.
95
+ */
96
+ export type TVConfig = {
97
+ readonly base?: string | string[];
98
+ readonly slots?: Record<string, string | string[]>;
99
+ readonly variants?: Record<string, Record<string, unknown>>;
100
+ readonly compoundVariants?: Record<string, unknown>[];
101
+ readonly defaultVariants?: Record<string, unknown>;
102
+ };
103
+ export declare function tv<V extends Record<string, Record<string, string | string[]>> = {}>(config: {
63
104
  base?: string | string[];
64
105
  variants?: V;
65
106
  compoundVariants?: CompoundVariant<V>[];
66
107
  defaultVariants?: VariantPropsMap<V>;
67
- }): (props?: TVProps<V>) => string;
108
+ }): ((props?: TVProps<V>) => string) & {
109
+ readonly config: TVConfig;
110
+ };
68
111
  export declare function tv<V extends Record<string, Record<string, unknown>> = {}, S extends Record<string, string | string[]> = Record<string, string>>(config: {
69
- base?: string | string[];
70
112
  slots: S;
71
- variants?: V;
72
- compoundVariants?: CompoundVariant<V>[];
113
+ variants?: V & ValidSlotVariants<V, S>;
114
+ compoundVariants?: SlotCompoundVariant<V, S>[];
73
115
  defaultVariants?: VariantPropsMap<V>;
74
- }): (props?: TVProps<V>) => {
116
+ }): ((props?: VariantPropsMap<V>) => {
75
117
  [K in keyof S]: SlotFn<V>;
118
+ }) & {
119
+ readonly config: TVConfig;
76
120
  };
77
121
  export {};