@urbicon-ui/blocks 6.2.0 → 6.3.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 (147) hide show
  1. package/dist/components/AreaChart/AreaChart.svelte +2 -6
  2. package/dist/components/BarChart/BarChart.svelte +2 -6
  3. package/dist/components/Calendar/Calendar.svelte +12 -10
  4. package/dist/components/Calendar/calendar.variants.d.ts +2 -1
  5. package/dist/components/Calendar/index.d.ts +2 -2
  6. package/dist/components/CommandPalette/CommandPalette.svelte +106 -39
  7. package/dist/components/CommandPalette/commandPalette.variants.d.ts +3 -7
  8. package/dist/components/CommandPalette/commandPalette.variants.js +0 -1
  9. package/dist/components/CommandPalette/index.d.ts +2 -2
  10. package/dist/components/CompositionBar/CompositionBar.svelte +12 -15
  11. package/dist/components/CompositionBar/composition-bar.variants.d.ts +3 -1
  12. package/dist/components/CompositionBar/index.d.ts +2 -2
  13. package/dist/components/DonutChart/DonutChart.svelte +2 -6
  14. package/dist/components/EmptyState/EmptyState.svelte +8 -9
  15. package/dist/components/EmptyState/emptyState.variants.d.ts +3 -1
  16. package/dist/components/EmptyState/index.d.ts +3 -3
  17. package/dist/components/FileUpload/FileUpload.svelte +17 -17
  18. package/dist/components/FileUpload/fileUpload.variants.d.ts +3 -11
  19. package/dist/components/FileUpload/fileUpload.variants.js +0 -5
  20. package/dist/components/FileUpload/index.d.ts +4 -3
  21. package/dist/components/Guide/Guide.svelte +5 -8
  22. package/dist/components/Guide/GuideArticle.svelte +5 -8
  23. package/dist/components/Guide/GuideBeacon.svelte +5 -8
  24. package/dist/components/Guide/GuideHint.svelte +5 -8
  25. package/dist/components/Guide/GuideMarker.svelte +5 -8
  26. package/dist/components/Guide/GuideMention.svelte +5 -8
  27. package/dist/components/Guide/GuidePanel.svelte +5 -8
  28. package/dist/components/Guide/guide.variants.d.ts +9 -1
  29. package/dist/components/Guide/index.d.ts +12 -9
  30. package/dist/components/LineChart/LineChart.svelte +2 -6
  31. package/dist/components/Planner/Planner.svelte +7 -10
  32. package/dist/components/Planner/index.d.ts +4 -4
  33. package/dist/components/Planner/planner.variants.d.ts +3 -1
  34. package/dist/components/Sankey/Sankey.svelte +8 -9
  35. package/dist/components/Sankey/index.d.ts +2 -2
  36. package/dist/components/Sankey/sankey.variants.d.ts +3 -1
  37. package/dist/components/SidebarLayout/SidebarLayout.svelte +17 -17
  38. package/dist/components/SidebarLayout/index.d.ts +8 -2
  39. package/dist/components/SidebarLayout/sidebar-layout.variants.d.ts +3 -1
  40. package/dist/components/ThemeSwitcher/ThemeSwitcher.svelte +30 -38
  41. package/dist/components/ThemeSwitcher/index.d.ts +6 -4
  42. package/dist/components/ThemeSwitcher/themeSwitcher.variants.d.ts +3 -1
  43. package/dist/primitives/Accordion/Accordion.svelte +7 -9
  44. package/dist/primitives/Accordion/AccordionItem.svelte +8 -20
  45. package/dist/primitives/Accordion/accordion.variants.d.ts +3 -1
  46. package/dist/primitives/Accordion/index.d.ts +6 -6
  47. package/dist/primitives/Alert/Alert.svelte +7 -21
  48. package/dist/primitives/Alert/alert.variants.d.ts +3 -1
  49. package/dist/primitives/Alert/index.d.ts +3 -3
  50. package/dist/primitives/Avatar/Avatar.svelte +17 -20
  51. package/dist/primitives/Avatar/avatar.variants.d.ts +3 -1
  52. package/dist/primitives/Avatar/index.d.ts +3 -3
  53. package/dist/primitives/Badge/Badge.svelte +2 -2
  54. package/dist/primitives/Badge/badge.variants.d.ts +3 -1
  55. package/dist/primitives/Badge/index.d.ts +2 -2
  56. package/dist/primitives/Breadcrumb/Breadcrumb.svelte +30 -32
  57. package/dist/primitives/Breadcrumb/breadcrumb.variants.d.ts +3 -1
  58. package/dist/primitives/Breadcrumb/index.d.ts +2 -2
  59. package/dist/primitives/Button/Button.svelte +21 -21
  60. package/dist/primitives/Button/button.variants.d.ts +3 -1
  61. package/dist/primitives/Button/index.d.ts +3 -3
  62. package/dist/primitives/ButtonGroup/ButtonGroup.svelte +15 -14
  63. package/dist/primitives/ButtonGroup/buttongroup.variants.d.ts +3 -1
  64. package/dist/primitives/ButtonGroup/index.d.ts +3 -2
  65. package/dist/primitives/Card/Card.svelte +15 -18
  66. package/dist/primitives/Card/card.variants.d.ts +3 -1
  67. package/dist/primitives/Card/index.d.ts +3 -3
  68. package/dist/primitives/Checkbox/Checkbox.svelte +20 -20
  69. package/dist/primitives/Checkbox/checkbox.variants.d.ts +3 -1
  70. package/dist/primitives/Checkbox/index.d.ts +7 -3
  71. package/dist/primitives/Collapsible/Collapsible.svelte +30 -30
  72. package/dist/primitives/Collapsible/collapsible.variants.d.ts +3 -1
  73. package/dist/primitives/Collapsible/index.d.ts +3 -3
  74. package/dist/primitives/Combobox/Combobox.svelte +20 -31
  75. package/dist/primitives/Combobox/combobox.variants.d.ts +3 -1
  76. package/dist/primitives/Combobox/index.d.ts +5 -3
  77. package/dist/primitives/Dialog/Dialog.svelte +7 -10
  78. package/dist/primitives/Dialog/dialog.variants.d.ts +3 -1
  79. package/dist/primitives/Dialog/index.d.ts +2 -2
  80. package/dist/primitives/Drawer/Drawer.svelte +7 -10
  81. package/dist/primitives/Drawer/drawer.variants.d.ts +3 -1
  82. package/dist/primitives/Drawer/index.d.ts +2 -2
  83. package/dist/primitives/Input/Input.svelte +54 -30
  84. package/dist/primitives/Input/index.d.ts +7 -3
  85. package/dist/primitives/Input/input.variants.d.ts +3 -1
  86. package/dist/primitives/Menu/Menu.svelte +23 -23
  87. package/dist/primitives/Menu/index.d.ts +4 -1
  88. package/dist/primitives/Menu/menu.variants.d.ts +3 -1
  89. package/dist/primitives/Pagination/Pagination.svelte +18 -16
  90. package/dist/primitives/Pagination/index.d.ts +2 -2
  91. package/dist/primitives/Pagination/pagination.variants.d.ts +3 -1
  92. package/dist/primitives/Popover/Popover.svelte +2 -6
  93. package/dist/primitives/Progress/Progress.svelte +17 -17
  94. package/dist/primitives/Progress/index.d.ts +8 -3
  95. package/dist/primitives/Progress/progress.variants.d.ts +3 -1
  96. package/dist/primitives/RadioGroup/RadioGroup.svelte +16 -16
  97. package/dist/primitives/RadioGroup/RadioItem.svelte +19 -19
  98. package/dist/primitives/RadioGroup/index.d.ts +3 -3
  99. package/dist/primitives/RadioGroup/radioGroup.variants.d.ts +4 -1
  100. package/dist/primitives/SegmentGroup/SegmentGroup.svelte +14 -11
  101. package/dist/primitives/SegmentGroup/SegmentItem.svelte +12 -11
  102. package/dist/primitives/SegmentGroup/index.d.ts +3 -3
  103. package/dist/primitives/SegmentGroup/segmentgroup.variants.d.ts +3 -1
  104. package/dist/primitives/Select/Select.svelte +31 -22
  105. package/dist/primitives/Select/index.d.ts +8 -3
  106. package/dist/primitives/Select/select.variants.d.ts +3 -1
  107. package/dist/primitives/Separator/Separator.svelte +2 -6
  108. package/dist/primitives/Sidebar/Sidebar.svelte +6 -9
  109. package/dist/primitives/Sidebar/index.d.ts +2 -2
  110. package/dist/primitives/Sidebar/sidebar.variants.d.ts +3 -1
  111. package/dist/primitives/Skeleton/Skeleton.svelte +7 -9
  112. package/dist/primitives/Skeleton/index.d.ts +3 -3
  113. package/dist/primitives/Skeleton/skeleton.variants.d.ts +3 -1
  114. package/dist/primitives/Skeleton/skeleton.variants.js +5 -6
  115. package/dist/primitives/Slider/Slider.svelte +19 -19
  116. package/dist/primitives/Slider/index.d.ts +8 -3
  117. package/dist/primitives/Slider/slider.variants.d.ts +3 -1
  118. package/dist/primitives/Spinner/Spinner.svelte +7 -9
  119. package/dist/primitives/Spinner/index.d.ts +5 -3
  120. package/dist/primitives/Spinner/spinner.variants.d.ts +3 -1
  121. package/dist/primitives/Stepper/Stepper.svelte +13 -18
  122. package/dist/primitives/Stepper/StepperStep.svelte +18 -19
  123. package/dist/primitives/Stepper/index.d.ts +5 -5
  124. package/dist/primitives/Stepper/stepper.variants.d.ts +3 -1
  125. package/dist/primitives/Tab/Tab.svelte +14 -17
  126. package/dist/primitives/Tab/TabItem.svelte +18 -19
  127. package/dist/primitives/Tab/TabPanel.svelte +10 -13
  128. package/dist/primitives/Tab/index.d.ts +7 -7
  129. package/dist/primitives/Tab/tab.variants.d.ts +3 -1
  130. package/dist/primitives/Textarea/Textarea.svelte +23 -23
  131. package/dist/primitives/Textarea/index.d.ts +7 -3
  132. package/dist/primitives/Textarea/textarea.variants.d.ts +3 -1
  133. package/dist/primitives/Toast/Toaster.svelte +5 -6
  134. package/dist/primitives/Toast/index.d.ts +2 -2
  135. package/dist/primitives/Toast/toast.variants.d.ts +3 -1
  136. package/dist/primitives/Toggle/Toggle.svelte +19 -19
  137. package/dist/primitives/Toggle/index.d.ts +7 -3
  138. package/dist/primitives/Toggle/toggle.variants.d.ts +3 -1
  139. package/dist/primitives/Toolbar/Toolbar.svelte +7 -9
  140. package/dist/primitives/Toolbar/index.d.ts +4 -3
  141. package/dist/primitives/Toolbar/toolbar.variants.d.ts +3 -1
  142. package/dist/primitives/Tooltip/Tooltip.svelte +7 -9
  143. package/dist/primitives/Tooltip/index.d.ts +2 -2
  144. package/dist/primitives/Tooltip/tooltip.variants.d.ts +3 -1
  145. package/dist/style/semantic.css +5 -0
  146. package/dist/utils/variants.d.ts +17 -0
  147. package/package.json +3 -3
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- import type { SidebarLayoutVariants } from './sidebar-layout.variants';
3
+ import type { SidebarLayoutSlots, SidebarLayoutVariants } from './sidebar-layout.variants';
4
4
  /**
5
5
  * Snippet payload for the `mobileHeader` slot. Receives an opener for the
6
6
  * sidebar overlay so the consumer can wire a hamburger button without
@@ -12,7 +12,13 @@ export interface MobileHeaderContext {
12
12
  /** Current open state of the sidebar. */
13
13
  sidebarOpen: boolean;
14
14
  }
15
- type SidebarLayoutSlot = 'root' | 'mobileHeader' | 'main' | 'inner' | 'sidebar' | 'sidebarBackdrop' | 'sidebarHeader' | 'sidebarContent' | 'sidebarFooter';
15
+ /**
16
+ * Slot keys for `slotClasses`. The tv-driven slots (`SidebarLayoutSlots`:
17
+ * root | mobileHeader | main | inner) are the layout's own; the `sidebar*`
18
+ * keys are forwarded to the embedded `<Sidebar>` (mapped to its
19
+ * `slotClasses.panel`/`backdrop`/`header`/`content`/`footer`).
20
+ */
21
+ type SidebarLayoutSlot = SidebarLayoutSlots | 'sidebar' | 'sidebarBackdrop' | 'sidebarHeader' | 'sidebarContent' | 'sidebarFooter';
16
22
  /**
17
23
  * @description App-shell layout that wires a `<Sidebar>` to a main content
18
24
  * region and an optional mobile header. Use this whenever you want a
@@ -1,4 +1,4 @@
1
- import { type VariantProps } from '../../utils/variants';
1
+ import { type SlotNames, type VariantProps } from '../../utils/variants';
2
2
  export declare const sidebarLayoutVariants: (props?: import("../../utils/variants").TVProps<{
3
3
  side: {
4
4
  left: {
@@ -59,3 +59,5 @@ export declare const sidebarLayoutVariants: (props?: import("../../utils/variant
59
59
  }) | undefined) => string;
60
60
  };
61
61
  export type SidebarLayoutVariants = VariantProps<typeof sidebarLayoutVariants>;
62
+ /** Slot names derived from the `tv()` config above — single source of truth for `slotClasses`. */
63
+ export type SidebarLayoutSlots = SlotNames<typeof sidebarLayoutVariants>;
@@ -1,8 +1,9 @@
1
1
  <script lang="ts">
2
2
  import type { ThemeSwitcherProps, Theme } from './index';
3
- import { themeSwitcherVariants } from './themeSwitcher.variants';
3
+ import { themeSwitcherVariants, type ThemeSwitcherVariants } from './themeSwitcher.variants';
4
4
  import { onMount } from 'svelte';
5
- import { getBlocksConfig, mergeSlotClasses, resolvePresetSlotClasses } from '../../provider';
5
+ import { MediaQuery } from 'svelte/reactivity';
6
+ import { getBlocksConfig, resolveSlotClasses } from '../../provider';
6
7
  import { resolveIcon } from '../../icons';
7
8
  import SunIconDefault from '../../icons/SunIcon.svelte';
8
9
  import MoonIconDefault from '../../icons/MoonIcon.svelte';
@@ -31,31 +32,22 @@
31
32
 
32
33
  const blocksConfig = getBlocksConfig();
33
34
  const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
34
- const slotClasses = $derived(
35
- mergeSlotClasses(
36
- blocksConfig?.defaults?.ThemeSwitcher?.slotClasses,
37
- resolvePresetSlotClasses(blocksConfig?.presets, 'ThemeSwitcher', preset),
38
- slotClassesProp
39
- )
40
- );
41
35
 
42
- let mounted = $state(false);
36
+ // Reactive, SSR-safe `prefers-color-scheme` reader (svelte/reactivity)
37
+ // re-evaluates `resolvedTheme` on OS changes without a manual listener.
38
+ const prefersDark = new MediaQuery('(prefers-color-scheme: dark)');
43
39
 
44
- const styles = $derived(
45
- unstyled ? undefined : themeSwitcherVariants({ variant, size, disabled })
46
- );
40
+ const variantProps: ThemeSwitcherVariants = $derived({ variant, size, disabled });
47
41
 
48
- function slot(name: 'button' | 'icon') {
49
- const base = styles?.[name]?.() ?? '';
50
- const override = slotClasses?.[name] ?? '';
51
- return override ? `${base} ${override}` : base;
52
- }
42
+ const styles = $derived(themeSwitcherVariants(variantProps));
53
43
 
54
- const resolvedTheme: Theme = $derived.by(() => {
55
- if (theme !== 'system' || typeof window === 'undefined') return theme;
56
- if (!mounted) return 'light';
57
- return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
58
- });
44
+ const slotClasses = $derived(
45
+ resolveSlotClasses(blocksConfig, 'ThemeSwitcher', preset, variantProps, slotClassesProp)
46
+ );
47
+
48
+ const resolvedTheme: Theme = $derived(
49
+ theme === 'system' ? (prefersDark.current ? 'dark' : 'light') : theme
50
+ );
59
51
 
60
52
  const label = $derived(
61
53
  theme === 'light'
@@ -93,16 +85,15 @@
93
85
  root.classList.add('dark');
94
86
  if (storageKey) localStorage.setItem(storageKey, 'dark');
95
87
  } else {
88
+ // System: drop both overrides and let `:root { color-scheme: light dark }`
89
+ // resolve `light-dark()` against the OS preference natively — this keeps
90
+ // following OS changes live, on every page, without a JS listener.
96
91
  root.classList.remove('light', 'dark');
97
92
  if (storageKey) localStorage.removeItem(storageKey);
98
- const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
99
- root.classList.toggle('dark', prefersDark);
100
93
  }
101
94
  }
102
95
 
103
96
  onMount(() => {
104
- mounted = true;
105
-
106
97
  if (storageKey) {
107
98
  const saved = localStorage.getItem(storageKey);
108
99
  if (saved === 'light' || saved === 'dark') {
@@ -110,13 +101,6 @@
110
101
  }
111
102
  }
112
103
  applyTheme();
113
-
114
- const mq = window.matchMedia('(prefers-color-scheme: dark)');
115
- const handler = () => {
116
- if (theme === 'system') applyTheme();
117
- };
118
- mq.addEventListener('change', handler);
119
- return () => mq.removeEventListener('change', handler);
120
104
  });
121
105
 
122
106
  export function getTheme(): Theme {
@@ -131,16 +115,24 @@
131
115
  <button
132
116
  type="button"
133
117
  onclick={cycle}
134
- class="{slot('button')} {className}"
118
+ class={unstyled
119
+ ? [slotClasses?.button, className].filter(Boolean).join(' ')
120
+ : styles.button({ class: [slotClasses?.button, className] })}
135
121
  aria-label={label}
136
122
  title={label}
137
123
  {disabled}
138
124
  >
139
125
  {#if theme === 'light'}
140
- <SunIcon class={slot('icon')} />
126
+ <SunIcon
127
+ class={unstyled ? (slotClasses?.icon ?? '') : styles.icon({ class: slotClasses?.icon })}
128
+ />
141
129
  {:else if theme === 'dark'}
142
- <MoonIcon class={slot('icon')} />
130
+ <MoonIcon
131
+ class={unstyled ? (slotClasses?.icon ?? '') : styles.icon({ class: slotClasses?.icon })}
132
+ />
143
133
  {:else}
144
- <MonitorIcon class={slot('icon')} />
134
+ <MonitorIcon
135
+ class={unstyled ? (slotClasses?.icon ?? '') : styles.icon({ class: slotClasses?.icon })}
136
+ />
145
137
  {/if}
146
138
  </button>
@@ -1,10 +1,12 @@
1
- import type { ThemeSwitcherVariants } from './themeSwitcher.variants';
1
+ import type { ThemeSwitcherSlots, ThemeSwitcherVariants } from './themeSwitcher.variants';
2
2
  export type Theme = 'light' | 'dark' | 'system';
3
3
  /**
4
4
  * Props for the ThemeSwitcher component.
5
5
  *
6
- * @description Cycles between light, dark, and system color schemes.
7
- * Applies light/dark classes on the html element and persists to localStorage.
6
+ * @description Cycles between light, dark, and system color schemes. Sets a
7
+ * `light`/`dark` class on the `<html>` element for explicit choices and clears
8
+ * it in system mode (so the CSS `light-dark()` function follows the OS natively).
9
+ * Persists the choice to localStorage.
8
10
  *
9
11
  * @tag action
10
12
  * @related LocaleSwitcher
@@ -42,7 +44,7 @@ export interface ThemeSwitcherProps {
42
44
  /** Strip all default styles. @default false */
43
45
  unstyled?: boolean;
44
46
  /** Per-slot class overrides. */
45
- slotClasses?: Partial<Record<'button' | 'icon', string>>;
47
+ slotClasses?: Partial<Record<ThemeSwitcherSlots, string>>;
46
48
  /**
47
49
  * Apply a named preset registered via `<BlocksProvider presets={{ ThemeSwitcher: {...} }}>`.
48
50
  * Prefer this over `class` overrides when the requested look falls outside the
@@ -1,4 +1,4 @@
1
- import { type VariantProps } from '../../utils/variants';
1
+ import { type SlotNames, type VariantProps } from '../../utils/variants';
2
2
  export declare const themeSwitcherVariants: (props?: import("../../utils/variants").TVProps<{
3
3
  variant: {
4
4
  ghost: {
@@ -49,3 +49,5 @@ export declare const themeSwitcherVariants: (props?: import("../../utils/variant
49
49
  }) | undefined) => string;
50
50
  };
51
51
  export type ThemeSwitcherVariants = VariantProps<typeof themeSwitcherVariants>;
52
+ /** Slot names derived from the `tv()` config above — single source of truth for `slotClasses`. */
53
+ export type ThemeSwitcherSlots = SlotNames<typeof themeSwitcherVariants>;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
- import { getBlocksConfig, mergeSlotClasses, resolvePresetSlotClasses } from '../../provider';
3
- import { accordionVariants } from './accordion.variants';
2
+ import { getBlocksConfig, resolveSlotClasses } from '../../provider';
3
+ import { accordionVariants, type AccordionVariants } from './accordion.variants';
4
4
  import { setAccordionContext } from './accordion.context';
5
5
  import type { AccordionProps, AccordionContext } from './index';
6
6
 
@@ -23,16 +23,14 @@
23
23
 
24
24
  const blocksConfig = getBlocksConfig();
25
25
  const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
26
+
27
+ const variantProps: AccordionVariants = $derived({ variant, size });
28
+ const styles = $derived(accordionVariants(variantProps));
29
+
26
30
  const slotClasses = $derived(
27
- mergeSlotClasses(
28
- blocksConfig?.defaults?.Accordion?.slotClasses,
29
- resolvePresetSlotClasses(blocksConfig?.presets, 'Accordion', preset),
30
- slotClassesProp
31
- )
31
+ resolveSlotClasses(blocksConfig, 'Accordion', preset, variantProps, slotClassesProp)
32
32
  );
33
33
 
34
- const styles = $derived(unstyled ? { base: () => '' } : accordionVariants({ variant, size }));
35
-
36
34
  function normalise(v: string | string[] | undefined): string[] {
37
35
  if (v === undefined) return [];
38
36
  return Array.isArray(v) ? v : [v];
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
- import { getBlocksConfig, mergeSlotClasses, resolvePresetSlotClasses } from '../../provider';
3
- import { accordionVariants } from './accordion.variants';
2
+ import { getBlocksConfig, resolveSlotClasses } from '../../provider';
3
+ import { accordionVariants, type AccordionVariants } from './accordion.variants';
4
4
  import { getAccordionContext } from './accordion.context';
5
5
  import { resolveIcon } from '../../icons';
6
6
  import ChevronDownIconDefault from '../../icons/ChevronDownIcon.svelte';
@@ -24,31 +24,19 @@
24
24
 
25
25
  const blocksConfig = getBlocksConfig();
26
26
  const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
27
- const slotClasses = $derived(
28
- mergeSlotClasses(
29
- blocksConfig?.defaults?.AccordionItem?.slotClasses,
30
- resolvePresetSlotClasses(blocksConfig?.presets, 'AccordionItem', preset),
31
- slotClassesProp
32
- )
33
- );
34
27
 
35
28
  const ctx = getAccordionContext();
36
29
  const open = $derived(ctx.isOpen(value));
37
30
  const isDisabled = $derived(itemDisabled || ctx.disabled);
38
31
 
39
- const styles = $derived(
40
- unstyled
41
- ? {
42
- item: () => '',
43
- trigger: () => '',
44
- chevron: () => '',
45
- content: () => '',
46
- contentInner: () => ''
47
- }
48
- : accordionVariants({ variant: ctx.variant, size: ctx.size })
32
+ const variantProps: AccordionVariants = $derived({ variant: ctx.variant, size: ctx.size });
33
+ const styles = $derived(accordionVariants(variantProps));
34
+
35
+ const slotClasses = $derived(
36
+ resolveSlotClasses(blocksConfig, 'AccordionItem', preset, variantProps, slotClassesProp)
49
37
  );
50
38
 
51
- function slot(key: keyof typeof styles, extra?: string) {
39
+ function slot(key: Exclude<keyof typeof styles, 'base'>, extra?: string) {
52
40
  const overrides = [slotClasses?.[key], extra].filter(Boolean).join(' ');
53
41
  if (unstyled) return overrides;
54
42
  return styles[key]({ class: overrides });
@@ -1,4 +1,4 @@
1
- import { type VariantProps } from '../../utils/variants';
1
+ import { type SlotNames, type VariantProps } from '../../utils/variants';
2
2
  export declare const accordionVariants: (props?: import("../../utils/variants").TVProps<{
3
3
  variant: {
4
4
  default: {};
@@ -74,3 +74,5 @@ export declare const accordionVariants: (props?: import("../../utils/variants").
74
74
  }) | undefined) => string;
75
75
  };
76
76
  export type AccordionVariants = VariantProps<typeof accordionVariants>;
77
+ /** Slot names derived from the tv() config — single source of truth for slotClasses. */
78
+ export type AccordionSlots = SlotNames<typeof accordionVariants>;
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- import type { AccordionVariants } from './accordion.variants';
3
+ import type { AccordionSlots, AccordionVariants } from './accordion.variants';
4
4
  /** Context for Accordion ↔ AccordionItem communication */
5
5
  export interface AccordionContext {
6
6
  toggle: (value: string) => void;
@@ -26,7 +26,7 @@ export interface AccordionContext {
26
26
  * A Svelte 5 component library with built-in i18n and design tokens.
27
27
  * </AccordionItem>
28
28
  * <AccordionItem value="faq-2" title="How does dark mode work?">
29
- * Semantic tokens automatically handle light/dark via prefers-color-scheme.
29
+ * Semantic tokens automatically handle light/dark via the CSS light-dark() function.
30
30
  * </AccordionItem>
31
31
  * </Accordion>
32
32
  * ```
@@ -62,8 +62,8 @@ export interface AccordionProps extends AccordionVariants, Omit<HTMLAttributes<H
62
62
  class?: string;
63
63
  /** Remove default styles */
64
64
  unstyled?: boolean;
65
- /** Per-slot class overrides */
66
- slotClasses?: Partial<Record<'base' | 'item' | 'trigger' | 'chevron' | 'content' | 'contentInner', string>>;
65
+ /** Per-slot class overrides. Slots: base | item | trigger | chevron | content | contentInner */
66
+ slotClasses?: Partial<Record<AccordionSlots, string>>;
67
67
  /**
68
68
  * Apply a named preset registered via `<BlocksProvider presets={{ Accordion: {...} }}>`.
69
69
  * Prefer this over `class` overrides when the requested look falls outside the
@@ -107,8 +107,8 @@ export interface AccordionItemProps extends Omit<HTMLAttributes<HTMLDivElement>,
107
107
  class?: string;
108
108
  /** Remove default styles */
109
109
  unstyled?: boolean;
110
- /** Per-slot class overrides */
111
- slotClasses?: Partial<Record<'item' | 'trigger' | 'chevron' | 'content' | 'contentInner', string>>;
110
+ /** Per-slot class overrides. Slots: item | trigger | chevron | content | contentInner */
111
+ slotClasses?: Partial<Record<Exclude<AccordionSlots, 'base'>, string>>;
112
112
  /**
113
113
  * Apply a named preset registered via `<BlocksProvider presets={{ AccordionItem: {...} }}>`.
114
114
  * Prefer this over `class` overrides when the requested look falls outside the
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { useBlocksI18n } from '../..';
3
- import { alertVariants } from './alert.variants';
4
- import { getBlocksConfig, mergeSlotClasses, resolvePresetSlotClasses } from '../../provider';
3
+ import { alertVariants, type AlertVariants } from './alert.variants';
4
+ import { getBlocksConfig, resolveSlotClasses } from '../../provider';
5
5
  import { resolveIcon } from '../../icons';
6
6
  import CloseIconDefault from '../../icons/CloseIcon.svelte';
7
7
  import type { AlertProps } from './index';
@@ -29,26 +29,12 @@
29
29
 
30
30
  const blocksConfig = getBlocksConfig();
31
31
  const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
32
- const slotClasses = $derived(
33
- mergeSlotClasses(
34
- blocksConfig?.defaults?.Alert?.slotClasses,
35
- resolvePresetSlotClasses(blocksConfig?.presets, 'Alert', preset),
36
- slotClassesProp
37
- )
38
- );
39
32
 
40
- const styles = $derived(
41
- unstyled
42
- ? {
43
- base: () => '',
44
- icon: () => '',
45
- content: () => '',
46
- title: () => '',
47
- description: () => '',
48
- actions: () => '',
49
- dismissButton: () => ''
50
- }
51
- : alertVariants({ intent, variant, size })
33
+ const variantProps: AlertVariants = $derived({ intent, variant, size });
34
+ const styles = $derived(alertVariants(variantProps));
35
+
36
+ const slotClasses = $derived(
37
+ resolveSlotClasses(blocksConfig, 'Alert', preset, variantProps, slotClassesProp)
52
38
  );
53
39
  </script>
54
40
 
@@ -1,4 +1,4 @@
1
- import { type VariantProps } from '../../utils/variants';
1
+ import { type SlotNames, type VariantProps } from '../../utils/variants';
2
2
  export declare const alertVariants: (props?: import("../../utils/variants").TVProps<{
3
3
  intent: {
4
4
  primary: {};
@@ -98,3 +98,5 @@ export declare const alertVariants: (props?: import("../../utils/variants").TVPr
98
98
  }) | undefined) => string;
99
99
  };
100
100
  export type AlertVariants = VariantProps<typeof alertVariants>;
101
+ /** Slot names derived from the tv() config — single source of truth for slotClasses. */
102
+ export type AlertSlots = SlotNames<typeof alertVariants>;
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- import type { AlertVariants } from './alert.variants';
3
+ import type { AlertSlots, AlertVariants } from './alert.variants';
4
4
  /**
5
5
  * Props interface for Alert component
6
6
  *
@@ -50,8 +50,8 @@ export interface AlertProps extends AlertVariants, Omit<HTMLAttributes<HTMLDivEl
50
50
  class?: string;
51
51
  /** Remove default styles */
52
52
  unstyled?: boolean;
53
- /** Per-slot class overrides */
54
- slotClasses?: Partial<Record<'base' | 'icon' | 'content' | 'title' | 'description' | 'actions' | 'dismissButton', string>>;
53
+ /** Per-slot class overrides. Slots: base | icon | content | title | description | actions | dismissButton */
54
+ slotClasses?: Partial<Record<AlertSlots, string>>;
55
55
  /**
56
56
  * Apply a named preset registered via `<BlocksProvider presets={{ Alert: {...} }}>`.
57
57
  * Prefer this over `class` overrides when the requested look falls outside the
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { mintRegistry } from '../..';
3
- import { getBlocksConfig, mergeSlotClasses, resolvePresetSlotClasses } from '../../provider';
4
- import { avatarVariants } from '..';
3
+ import { getBlocksConfig, resolveSlotClasses } from '../../provider';
4
+ import { avatarVariants, type AvatarVariants } from '..';
5
5
  import { useBlocksI18n } from '../..';
6
6
  import type { AvatarProps } from './index';
7
7
 
@@ -35,13 +35,6 @@
35
35
 
36
36
  const blocksConfig = getBlocksConfig();
37
37
  const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
38
- const slotClasses = $derived(
39
- mergeSlotClasses(
40
- blocksConfig?.defaults?.Avatar?.slotClasses,
41
- resolvePresetSlotClasses(blocksConfig?.presets, 'Avatar', preset),
42
- slotClassesProp
43
- )
44
- );
45
38
 
46
39
  let avatarElement = $state<HTMLElement>();
47
40
  let imageError = $state(false);
@@ -92,17 +85,21 @@
92
85
 
93
86
  const randomBg = $derived(randomColor ? getRandomColor(name) : undefined);
94
87
 
95
- const styles = $derived(
96
- avatarVariants({
97
- size,
98
- variant,
99
- intent: randomColor ? undefined : intent,
100
- status,
101
- statusPosition,
102
- ring,
103
- ringIntent: ringColor ? undefined : ringIntent,
104
- interactive: isInteractive
105
- })
88
+ const variantProps: AvatarVariants = $derived({
89
+ size,
90
+ variant,
91
+ intent: randomColor ? undefined : intent,
92
+ status,
93
+ statusPosition,
94
+ ring,
95
+ ringIntent: ringColor ? undefined : ringIntent,
96
+ interactive: isInteractive
97
+ });
98
+
99
+ const styles = $derived(avatarVariants(variantProps));
100
+
101
+ const slotClasses = $derived(
102
+ resolveSlotClasses(blocksConfig, 'Avatar', preset, variantProps, slotClassesProp)
106
103
  );
107
104
 
108
105
  const dynamicStyles = $derived.by(() => {
@@ -1,4 +1,4 @@
1
- import { type VariantProps } from '../../utils/variants';
1
+ import { type SlotNames, type VariantProps } from '../../utils/variants';
2
2
  export declare const avatarVariants: (props?: import("../../utils/variants").TVProps<{
3
3
  size: {
4
4
  xs: {
@@ -173,3 +173,5 @@ export declare const avatarVariants: (props?: import("../../utils/variants").TVP
173
173
  }) | undefined) => string;
174
174
  };
175
175
  export type AvatarVariants = VariantProps<typeof avatarVariants>;
176
+ /** Slot names derived from the tv() config — single source of truth for slotClasses. */
177
+ export type AvatarSlots = SlotNames<typeof avatarVariants>;
@@ -1,7 +1,7 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
3
  import type { MintProp } from '../../mint';
4
- import type { AvatarVariants } from './avatar.variants';
4
+ import type { AvatarSlots, AvatarVariants } from './avatar.variants';
5
5
  /**
6
6
  * @description User profile image component with fallback initials, multiple sizes, and interactive states.
7
7
  *
@@ -43,8 +43,8 @@ export interface AvatarProps extends AvatarVariants, Omit<HTMLAttributes<HTMLDiv
43
43
  class?: string;
44
44
  /** Remove all default tv classes. */
45
45
  unstyled?: boolean;
46
- /** Per-slot class overrides merged with tv styles. */
47
- slotClasses?: Partial<Record<'base' | 'image' | 'fallback' | 'status', string>>;
46
+ /** Per-slot class overrides merged with tv styles. Slots: base | image | fallback | status */
47
+ slotClasses?: Partial<Record<AvatarSlots, string>>;
48
48
  /**
49
49
  * Apply a named preset registered via `<BlocksProvider presets={{ Avatar: {...} }}>`.
50
50
  * Prefer this over `class` overrides when the requested look falls outside the
@@ -6,7 +6,7 @@
6
6
  import { useBlocksI18n } from '../..';
7
7
 
8
8
  const CloseIcon = resolveIcon('close', CloseIconDefault);
9
- import { badgeVariants } from '..';
9
+ import { badgeVariants, type BadgeVariants } from '..';
10
10
  import { getTierContext } from '../../utils/tier-context';
11
11
  import type { BadgeProps } from './index';
12
12
 
@@ -50,7 +50,7 @@
50
50
  const isInteractive = $derived(interactive || !!onclick);
51
51
  const isRemovable = $derived(removable && !isDot);
52
52
 
53
- const variantProps = $derived({
53
+ const variantProps: BadgeVariants = $derived({
54
54
  tier: effectiveTier,
55
55
  intent,
56
56
  variant,
@@ -1,4 +1,4 @@
1
- import { type VariantProps } from '../../utils/variants';
1
+ import { type SlotNames, type VariantProps } from '../../utils/variants';
2
2
  export declare const PLACEMENT_VALUES: readonly ["top", "top-start", "top-end", "bottom", "bottom-start", "bottom-end", "left", "right"];
3
3
  export declare const badgeVariants: (props?: import("../../utils/variants").TVProps<{
4
4
  tier: {
@@ -151,4 +151,6 @@ export declare const badgeVariants: (props?: import("../../utils/variants").TVPr
151
151
  }) | undefined) => string;
152
152
  };
153
153
  export type BadgeVariants = VariantProps<typeof badgeVariants>;
154
+ /** Slot names derived from the tv() config — single source of truth for slotClasses. */
155
+ export type BadgeSlots = SlotNames<typeof badgeVariants>;
154
156
  export type BadgePlacement = (typeof PLACEMENT_VALUES)[number];
@@ -1,7 +1,7 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
3
  import type { MintProp } from '../../mint';
4
- import type { BadgePlacement, BadgeVariants } from './badge.variants';
4
+ import type { BadgePlacement, BadgeSlots, BadgeVariants } from './badge.variants';
5
5
  /**
6
6
  * Shared fields that apply to every Badge variant — `dot` and the
7
7
  * label-style variants both accept these.
@@ -24,7 +24,7 @@ interface BadgeBaseProps extends Omit<BadgeVariants, 'variant'>, Omit<HTMLAttrib
24
24
  /** Remove all default tv classes. */
25
25
  unstyled?: boolean;
26
26
  /** Per-slot class overrides merged with tv styles. */
27
- slotClasses?: Partial<Record<'base' | 'content' | 'removeButton' | 'removeIcon', string>>;
27
+ slotClasses?: Partial<Record<BadgeSlots, string>>;
28
28
  /**
29
29
  * Apply a named preset registered via `<BlocksProvider presets={{ Badge: {...} }}>`.
30
30
  * Prefer this over `class` overrides when the requested look falls outside the
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
- import { getBlocksConfig, mergeSlotClasses, resolvePresetSlotClasses } from '../../provider';
3
- import { breadcrumbVariants } from './breadcrumb.variants';
2
+ import { getBlocksConfig, resolveSlotClasses } from '../../provider';
3
+ import { breadcrumbVariants, type BreadcrumbVariants } from './breadcrumb.variants';
4
4
  import type { BreadcrumbProps } from './index';
5
5
 
6
6
  let {
@@ -17,52 +17,50 @@
17
17
 
18
18
  const blocksConfig = getBlocksConfig();
19
19
  const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
20
- const slotClasses = $derived(
21
- mergeSlotClasses(
22
- blocksConfig?.defaults?.Breadcrumb?.slotClasses,
23
- resolvePresetSlotClasses(blocksConfig?.presets, 'Breadcrumb', preset),
24
- slotClassesProp
25
- )
26
- );
27
20
 
28
- const styles = $derived(
29
- unstyled
30
- ? {
31
- nav: () => '',
32
- list: () => '',
33
- item: () => '',
34
- link: () => '',
35
- currentPage: () => '',
36
- separator: () => ''
37
- }
38
- : breadcrumbVariants({ size })
39
- );
21
+ const variantProps: BreadcrumbVariants = $derived({ size });
22
+
23
+ const styles = $derived(breadcrumbVariants(variantProps));
40
24
 
41
- function slot(key: keyof typeof styles, extra?: string) {
42
- const overrides = [slotClasses?.[key], extra].filter(Boolean).join(' ');
43
- if (unstyled) return overrides;
44
- return styles[key]({ class: overrides });
45
- }
25
+ const slotClasses = $derived(
26
+ resolveSlotClasses(blocksConfig, 'Breadcrumb', preset, variantProps, slotClassesProp)
27
+ );
46
28
  </script>
47
29
 
48
- <nav class={slot('nav', className)} aria-label={ariaLabel} {...restProps}>
49
- <ol class={slot('list')}>
30
+ <nav
31
+ class={unstyled
32
+ ? [slotClasses?.nav, className].filter(Boolean).join(' ')
33
+ : styles.nav({ class: [slotClasses?.nav, className] })}
34
+ aria-label={ariaLabel}
35
+ {...restProps}
36
+ >
37
+ <ol class={unstyled ? (slotClasses?.list ?? '') : styles.list({ class: slotClasses?.list })}>
50
38
  {#each items as item, i (`${item.label}-${i}`)}
51
- <li class={slot('item')}>
39
+ <li class={unstyled ? (slotClasses?.item ?? '') : styles.item({ class: slotClasses?.item })}>
52
40
  {#if i === items.length - 1}
53
- <span class={slot('currentPage')} aria-current="page">{item.label}</span>
41
+ <span
42
+ class={unstyled
43
+ ? (slotClasses?.currentPage ?? '')
44
+ : styles.currentPage({ class: slotClasses?.currentPage })}
45
+ aria-current="page">{item.label}</span
46
+ >
54
47
  {:else}
55
48
  <!-- eslint-disable svelte/no-navigation-without-resolve -- BreadcrumbItem.href is opaque to the library -->
56
49
  <a
57
50
  href={item.href}
58
- class={slot('link')}
51
+ class={unstyled ? (slotClasses?.link ?? '') : styles.link({ class: slotClasses?.link })}
59
52
  aria-label={item['aria-label']}
60
53
  onclick={item.onclick}
61
54
  >
62
55
  {item.label}
63
56
  </a>
64
57
  <!-- eslint-enable svelte/no-navigation-without-resolve -->
65
- <span class={slot('separator')} aria-hidden="true">
58
+ <span
59
+ class={unstyled
60
+ ? (slotClasses?.separator ?? '')
61
+ : styles.separator({ class: slotClasses?.separator })}
62
+ aria-hidden="true"
63
+ >
66
64
  {#if separator}
67
65
  {@render separator()}
68
66
  {:else}
@@ -1,4 +1,4 @@
1
- import { type VariantProps } from '../../utils/variants';
1
+ import { type SlotNames, type VariantProps } from '../../utils/variants';
2
2
  export declare const breadcrumbVariants: (props?: import("../../utils/variants").TVProps<{
3
3
  size: {
4
4
  sm: {
@@ -53,3 +53,5 @@ export declare const breadcrumbVariants: (props?: import("../../utils/variants")
53
53
  }) | undefined) => string;
54
54
  };
55
55
  export type BreadcrumbVariants = VariantProps<typeof breadcrumbVariants>;
56
+ /** Slot names derived from the `tv()` config above — single source of truth for `slotClasses`. */
57
+ export type BreadcrumbSlots = SlotNames<typeof breadcrumbVariants>;