fancy-ui-svelte 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +4 -3
  2. package/dist/cameleon/FancyProvider.svelte +63 -0
  3. package/dist/cameleon/FancyProvider.svelte.d.ts +14 -0
  4. package/dist/cameleon/cameleon.css +27 -0
  5. package/dist/cameleon/context.d.ts +14 -0
  6. package/dist/cameleon/context.js +15 -0
  7. package/dist/cameleon/index.d.ts +23 -0
  8. package/dist/cameleon/index.js +22 -0
  9. package/dist/cameleon/primitives/Badge.svelte +23 -0
  10. package/dist/cameleon/primitives/Badge.svelte.d.ts +12 -0
  11. package/dist/cameleon/primitives/Button.svelte +60 -0
  12. package/dist/cameleon/primitives/Button.svelte.d.ts +15 -0
  13. package/dist/cameleon/primitives/Checkbox.svelte +56 -0
  14. package/dist/cameleon/primitives/Checkbox.svelte.d.ts +14 -0
  15. package/dist/cameleon/primitives/Input.svelte +37 -0
  16. package/dist/cameleon/primitives/Input.svelte.d.ts +13 -0
  17. package/dist/cameleon/primitives/Radio.svelte +53 -0
  18. package/dist/cameleon/primitives/Radio.svelte.d.ts +15 -0
  19. package/dist/cameleon/primitives/Select.svelte +49 -0
  20. package/dist/cameleon/primitives/Select.svelte.d.ts +14 -0
  21. package/dist/cameleon/primitives/Slider.svelte +34 -0
  22. package/dist/cameleon/primitives/Slider.svelte.d.ts +10 -0
  23. package/dist/cameleon/primitives/Switch.svelte +43 -0
  24. package/dist/cameleon/primitives/Switch.svelte.d.ts +12 -0
  25. package/dist/cameleon/primitives/Textarea.svelte +36 -0
  26. package/dist/cameleon/primitives/Textarea.svelte.d.ts +12 -0
  27. package/dist/cameleon/primitives/Tooltip.svelte +32 -0
  28. package/dist/cameleon/primitives/Tooltip.svelte.d.ts +16 -0
  29. package/dist/cameleon/primitives/index.d.ts +10 -0
  30. package/dist/cameleon/primitives/index.js +10 -0
  31. package/dist/cameleon/skins/aurora/aurora.css +41 -0
  32. package/dist/cameleon/skins/aurora/index.d.ts +3 -0
  33. package/dist/cameleon/skins/aurora/index.js +12 -0
  34. package/dist/cameleon/skins/aurora/meta.d.ts +3 -0
  35. package/dist/cameleon/skins/aurora/meta.js +58 -0
  36. package/dist/cameleon/skins/aurora/recipes.d.ts +24 -0
  37. package/dist/cameleon/skins/aurora/recipes.js +163 -0
  38. package/dist/cameleon/skins/aurora/tokens.d.ts +6 -0
  39. package/dist/cameleon/skins/aurora/tokens.js +19 -0
  40. package/dist/cameleon/skins/brutal/brutal.css +35 -0
  41. package/dist/cameleon/skins/brutal/index.d.ts +3 -0
  42. package/dist/cameleon/skins/brutal/index.js +31 -0
  43. package/dist/cameleon/skins/brutal/meta.d.ts +3 -0
  44. package/dist/cameleon/skins/brutal/meta.js +70 -0
  45. package/dist/cameleon/skins/brutal/ornaments.d.ts +2 -0
  46. package/dist/cameleon/skins/brutal/ornaments.js +13 -0
  47. package/dist/cameleon/skins/brutal/recipes.d.ts +23 -0
  48. package/dist/cameleon/skins/brutal/recipes.js +147 -0
  49. package/dist/cameleon/skins/brutal/tokens.d.ts +8 -0
  50. package/dist/cameleon/skins/brutal/tokens.js +26 -0
  51. package/dist/cameleon/skins/default.d.ts +2 -0
  52. package/dist/cameleon/skins/default.js +95 -0
  53. package/dist/cameleon/skins/glass/glass.css +55 -0
  54. package/dist/cameleon/skins/glass/index.d.ts +3 -0
  55. package/dist/cameleon/skins/glass/index.js +12 -0
  56. package/dist/cameleon/skins/glass/meta.d.ts +3 -0
  57. package/dist/cameleon/skins/glass/meta.js +60 -0
  58. package/dist/cameleon/skins/glass/recipes.d.ts +24 -0
  59. package/dist/cameleon/skins/glass/recipes.js +155 -0
  60. package/dist/cameleon/skins/glass/tokens.d.ts +5 -0
  61. package/dist/cameleon/skins/glass/tokens.js +18 -0
  62. package/dist/cameleon/skins/retro-os/index.d.ts +3 -0
  63. package/dist/cameleon/skins/retro-os/index.js +20 -0
  64. package/dist/cameleon/skins/retro-os/kit/AppWindow.svelte +88 -0
  65. package/dist/cameleon/skins/retro-os/kit/AppWindow.svelte.d.ts +10 -0
  66. package/dist/cameleon/skins/retro-os/kit/Chip.svelte +43 -0
  67. package/dist/cameleon/skins/retro-os/kit/Chip.svelte.d.ts +17 -0
  68. package/dist/cameleon/skins/retro-os/kit/EmptyMediaFrame.svelte +56 -0
  69. package/dist/cameleon/skins/retro-os/kit/EmptyMediaFrame.svelte.d.ts +10 -0
  70. package/dist/cameleon/skins/retro-os/kit/HeaderBar.svelte +62 -0
  71. package/dist/cameleon/skins/retro-os/kit/HeaderBar.svelte.d.ts +15 -0
  72. package/dist/cameleon/skins/retro-os/kit/IconTile.svelte +44 -0
  73. package/dist/cameleon/skins/retro-os/kit/IconTile.svelte.d.ts +13 -0
  74. package/dist/cameleon/skins/retro-os/kit/InlineIndexLabel.svelte +27 -0
  75. package/dist/cameleon/skins/retro-os/kit/InlineIndexLabel.svelte.d.ts +11 -0
  76. package/dist/cameleon/skins/retro-os/kit/Led.svelte +29 -0
  77. package/dist/cameleon/skins/retro-os/kit/Led.svelte.d.ts +9 -0
  78. package/dist/cameleon/skins/retro-os/kit/NotchedFrame.svelte +83 -0
  79. package/dist/cameleon/skins/retro-os/kit/NotchedFrame.svelte.d.ts +11 -0
  80. package/dist/cameleon/skins/retro-os/kit/NumChip.svelte +25 -0
  81. package/dist/cameleon/skins/retro-os/kit/NumChip.svelte.d.ts +9 -0
  82. package/dist/cameleon/skins/retro-os/kit/PixelGlyph.svelte +38 -0
  83. package/dist/cameleon/skins/retro-os/kit/PixelGlyph.svelte.d.ts +13 -0
  84. package/dist/cameleon/skins/retro-os/kit/PlayBadge.svelte +46 -0
  85. package/dist/cameleon/skins/retro-os/kit/PlayBadge.svelte.d.ts +7 -0
  86. package/dist/cameleon/skins/retro-os/kit/RetroButton.svelte +76 -0
  87. package/dist/cameleon/skins/retro-os/kit/RetroButton.svelte.d.ts +21 -0
  88. package/dist/cameleon/skins/retro-os/kit/RetroCard.svelte +92 -0
  89. package/dist/cameleon/skins/retro-os/kit/RetroCard.svelte.d.ts +16 -0
  90. package/dist/cameleon/skins/retro-os/kit/RetroTag.svelte +35 -0
  91. package/dist/cameleon/skins/retro-os/kit/RetroTag.svelte.d.ts +13 -0
  92. package/dist/cameleon/skins/retro-os/kit/SectionWindow.svelte +125 -0
  93. package/dist/cameleon/skins/retro-os/kit/SectionWindow.svelte.d.ts +23 -0
  94. package/dist/cameleon/skins/retro-os/kit/StatusPill.svelte +32 -0
  95. package/dist/cameleon/skins/retro-os/kit/StatusPill.svelte.d.ts +13 -0
  96. package/dist/cameleon/skins/retro-os/kit/Swatches.svelte +33 -0
  97. package/dist/cameleon/skins/retro-os/kit/Swatches.svelte.d.ts +9 -0
  98. package/dist/cameleon/skins/retro-os/kit/VideoFrame.svelte +177 -0
  99. package/dist/cameleon/skins/retro-os/kit/VideoFrame.svelte.d.ts +20 -0
  100. package/dist/cameleon/skins/retro-os/kit/accents.d.ts +25 -0
  101. package/dist/cameleon/skins/retro-os/kit/accents.js +28 -0
  102. package/dist/cameleon/skins/retro-os/kit/index.d.ts +41 -0
  103. package/dist/cameleon/skins/retro-os/kit/index.js +41 -0
  104. package/dist/cameleon/skins/retro-os/kit/kit-windows.test.d.ts +1 -0
  105. package/dist/cameleon/skins/retro-os/kit/kit-windows.test.js +159 -0
  106. package/dist/cameleon/skins/retro-os/kit/kit.css +247 -0
  107. package/dist/cameleon/skins/retro-os/kit/kit.test.d.ts +1 -0
  108. package/dist/cameleon/skins/retro-os/kit/kit.test.js +123 -0
  109. package/dist/cameleon/skins/retro-os/meta.d.ts +3 -0
  110. package/dist/cameleon/skins/retro-os/meta.js +58 -0
  111. package/dist/cameleon/skins/retro-os/ornaments.d.ts +2 -0
  112. package/dist/cameleon/skins/retro-os/ornaments.js +17 -0
  113. package/dist/cameleon/skins/retro-os/recipes.d.ts +35 -0
  114. package/dist/cameleon/skins/retro-os/recipes.js +160 -0
  115. package/dist/cameleon/skins/retro-os/retro-os.css +107 -0
  116. package/dist/cameleon/skins/retro-os/tokens.d.ts +16 -0
  117. package/dist/cameleon/skins/retro-os/tokens.js +34 -0
  118. package/dist/cameleon/skins/terminal/index.d.ts +3 -0
  119. package/dist/cameleon/skins/terminal/index.js +20 -0
  120. package/dist/cameleon/skins/terminal/meta.d.ts +3 -0
  121. package/dist/cameleon/skins/terminal/meta.js +67 -0
  122. package/dist/cameleon/skins/terminal/ornaments.d.ts +2 -0
  123. package/dist/cameleon/skins/terminal/ornaments.js +11 -0
  124. package/dist/cameleon/skins/terminal/recipes.d.ts +25 -0
  125. package/dist/cameleon/skins/terminal/recipes.js +154 -0
  126. package/dist/cameleon/skins/terminal/terminal.css +52 -0
  127. package/dist/cameleon/skins/terminal/tokens.d.ts +5 -0
  128. package/dist/cameleon/skins/terminal/tokens.js +18 -0
  129. package/dist/cameleon/types.d.ts +132 -0
  130. package/dist/cameleon/types.js +1 -0
  131. package/dist/fancy-ui/blur-reveal/BlurReveal.svelte +16 -1
  132. package/dist/fancy-ui/blur-reveal/BlurReveal.svelte.d.ts +2 -0
  133. package/dist/fancy-ui/blur-reveal/BlurReveal.test.js +15 -0
  134. package/dist/fancy-ui/blur-reveal/README.md +22 -11
  135. package/dist/fancy-ui/button/Button.svelte +8 -0
  136. package/dist/fancy-ui/button/Button.svelte.d.ts +5 -0
  137. package/dist/fancy-ui/button/Button.test.js +61 -1
  138. package/dist/fancy-ui/button/README.md +11 -0
  139. package/dist/fancy-ui/checkbox/Checkbox.svelte +8 -0
  140. package/dist/fancy-ui/checkbox/Checkbox.svelte.d.ts +5 -0
  141. package/dist/fancy-ui/checkbox/Checkbox.test.js +93 -0
  142. package/dist/fancy-ui/checkbox/CheckboxFieldHarness.test.svelte +3 -2
  143. package/dist/fancy-ui/checkbox/CheckboxFieldHarness.test.svelte.d.ts +1 -0
  144. package/dist/fancy-ui/checkbox/README.md +10 -0
  145. package/dist/fancy-ui/copy-button/CopyButton.svelte +13 -0
  146. package/dist/fancy-ui/copy-button/CopyButton.svelte.d.ts +5 -0
  147. package/dist/fancy-ui/copy-button/CopyButton.test.js +78 -0
  148. package/dist/fancy-ui/copy-button/README.md +25 -14
  149. package/dist/fancy-ui/dropdown-menu/DropdownMenu.svelte +18 -3
  150. package/dist/fancy-ui/dropdown-menu/DropdownMenu.svelte.d.ts +5 -0
  151. package/dist/fancy-ui/dropdown-menu/DropdownMenu.test.js +182 -0
  152. package/dist/fancy-ui/dropdown-menu/DropdownMenuContent.svelte +3 -0
  153. package/dist/fancy-ui/dropdown-menu/DropdownMenuHarness.test.svelte +3 -1
  154. package/dist/fancy-ui/dropdown-menu/DropdownMenuHarness.test.svelte.d.ts +1 -0
  155. package/dist/fancy-ui/dropdown-menu/DropdownMenuItem.svelte +6 -1
  156. package/dist/fancy-ui/dropdown-menu/DropdownMenuSub.svelte +10 -2
  157. package/dist/fancy-ui/dropdown-menu/DropdownMenuSubContent.svelte +6 -0
  158. package/dist/fancy-ui/dropdown-menu/README.md +26 -0
  159. package/dist/fancy-ui/dropdown-menu/index.d.ts +1 -1
  160. package/dist/fancy-ui/dropdown-menu/types.d.ts +30 -7
  161. package/dist/fancy-ui/image-trail-cursor/ImageTrailCursor.test.js +17 -0
  162. package/dist/fancy-ui/image-trail-cursor/README.md +20 -18
  163. package/dist/fancy-ui/image-trail-cursor/trail-variants.d.ts +6 -1
  164. package/dist/fancy-ui/image-trail-cursor/trail-variants.js +33 -0
  165. package/dist/fancy-ui/image-trail-cursor/trail-variants.test.d.ts +1 -0
  166. package/dist/fancy-ui/image-trail-cursor/trail-variants.test.js +14 -0
  167. package/dist/fancy-ui/index.d.ts +1 -0
  168. package/dist/fancy-ui/index.js +2 -0
  169. package/dist/fancy-ui/interactive-grid-pattern/InteractiveGridPattern.svelte +10 -3
  170. package/dist/fancy-ui/interactive-grid-pattern/InteractiveGridPattern.svelte.d.ts +4 -0
  171. package/dist/fancy-ui/interactive-grid-pattern/InteractiveGridPattern.test.js +37 -1
  172. package/dist/fancy-ui/interactive-grid-pattern/README.md +20 -9
  173. package/dist/fancy-ui/line-hover-link/LineHoverLink.svelte +18 -1
  174. package/dist/fancy-ui/line-hover-link/LineHoverLink.svelte.d.ts +2 -1
  175. package/dist/fancy-ui/line-hover-link/LineHoverLink.test.js +12 -0
  176. package/dist/fancy-ui/line-hover-link/README.md +3 -2
  177. package/dist/fancy-ui/liquid-glass/README.md +4 -0
  178. package/dist/fancy-ui/logo-cloud/README.md +47 -14
  179. package/dist/fancy-ui/logo-cloud/StaticLogoCloud.svelte +43 -6
  180. package/dist/fancy-ui/logo-cloud/StaticLogoCloud.svelte.d.ts +18 -0
  181. package/dist/fancy-ui/logo-cloud/StaticLogoCloud.test.d.ts +1 -0
  182. package/dist/fancy-ui/logo-cloud/StaticLogoCloud.test.js +57 -0
  183. package/dist/fancy-ui/logo-cloud/index.d.ts +2 -2
  184. package/dist/fancy-ui/radio-group/README.md +9 -0
  185. package/dist/fancy-ui/radio-group/RadioGroup.svelte +9 -0
  186. package/dist/fancy-ui/radio-group/RadioGroup.svelte.d.ts +5 -0
  187. package/dist/fancy-ui/radio-group/RadioGroup.test.js +68 -0
  188. package/dist/fancy-ui/radio-group/RadioGroupHarness.test.svelte +13 -1
  189. package/dist/fancy-ui/radio-group/RadioGroupHarness.test.svelte.d.ts +1 -0
  190. package/dist/fancy-ui/registry.js +176 -4
  191. package/dist/fancy-ui/select/README.md +19 -0
  192. package/dist/fancy-ui/select/Select.svelte +33 -11
  193. package/dist/fancy-ui/select/Select.svelte.d.ts +5 -0
  194. package/dist/fancy-ui/select/Select.test.js +114 -0
  195. package/dist/fancy-ui/sound/README.md +288 -0
  196. package/dist/fancy-ui/sound/SoundToggle.svelte +185 -0
  197. package/dist/fancy-ui/sound/SoundToggle.svelte.d.ts +27 -0
  198. package/dist/fancy-ui/sound/SoundToggle.test.d.ts +1 -0
  199. package/dist/fancy-ui/sound/SoundToggle.test.js +251 -0
  200. package/dist/fancy-ui/sound/SoundToggleHarness.test.svelte +18 -0
  201. package/dist/fancy-ui/sound/SoundToggleHarness.test.svelte.d.ts +3 -0
  202. package/dist/fancy-ui/sound/engine.d.ts +67 -0
  203. package/dist/fancy-ui/sound/engine.js +440 -0
  204. package/dist/fancy-ui/sound/engine.test.d.ts +1 -0
  205. package/dist/fancy-ui/sound/engine.test.js +638 -0
  206. package/dist/fancy-ui/sound/index.d.ts +7 -0
  207. package/dist/fancy-ui/sound/index.js +7 -0
  208. package/dist/fancy-ui/sound/sound-feedback.d.ts +50 -0
  209. package/dist/fancy-ui/sound/sound-feedback.js +144 -0
  210. package/dist/fancy-ui/sound/sound-feedback.test.d.ts +1 -0
  211. package/dist/fancy-ui/sound/sound-feedback.test.js +272 -0
  212. package/dist/fancy-ui/sound/sound.barrel.test.d.ts +1 -0
  213. package/dist/fancy-ui/sound/sound.barrel.test.js +161 -0
  214. package/dist/fancy-ui/sound/sound.integration.test.d.ts +1 -0
  215. package/dist/fancy-ui/sound/sound.integration.test.js +541 -0
  216. package/dist/fancy-ui/sound/sound.ssr.test.d.ts +1 -0
  217. package/dist/fancy-ui/sound/sound.ssr.test.js +128 -0
  218. package/dist/fancy-ui/sound/sound.svelte.d.ts +54 -0
  219. package/dist/fancy-ui/sound/sound.svelte.js +349 -0
  220. package/dist/fancy-ui/sound/sound.test.d.ts +1 -0
  221. package/dist/fancy-ui/sound/sound.test.js +318 -0
  222. package/dist/fancy-ui/sound/themes.d.ts +19 -0
  223. package/dist/fancy-ui/sound/themes.js +367 -0
  224. package/dist/fancy-ui/sound/themes.test.d.ts +1 -0
  225. package/dist/fancy-ui/sound/themes.test.js +188 -0
  226. package/dist/fancy-ui/sound/types.d.ts +125 -0
  227. package/dist/fancy-ui/sound/types.js +59 -0
  228. package/dist/fancy-ui/sound/web-audio-mock.d.ts +144 -0
  229. package/dist/fancy-ui/sound/web-audio-mock.js +274 -0
  230. package/dist/fancy-ui/switch/README.md +9 -0
  231. package/dist/fancy-ui/switch/Switch.svelte +8 -0
  232. package/dist/fancy-ui/switch/Switch.svelte.d.ts +5 -0
  233. package/dist/fancy-ui/switch/Switch.test.js +81 -0
  234. package/package.json +13 -2
package/README.md CHANGED
@@ -9,7 +9,7 @@ Beautiful animation and UI components for **Svelte 5**.
9
9
  ![Svelte 5](https://img.shields.io/badge/Svelte-5-FF3E00?logo=svelte&logoColor=white)
10
10
  ![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-4-06B6D4?logo=tailwindcss&logoColor=white)
11
11
  ![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white)
12
- ![Components](https://img.shields.io/badge/Components-132-8B5CF6)
12
+ ![Components](https://img.shields.io/badge/Components-133-8B5CF6)
13
13
  ![MIT License](https://img.shields.io/badge/License-MIT-green)
14
14
 
15
15
  <p align="center">
@@ -36,8 +36,9 @@ Beautiful animation and UI components for **Svelte 5**.
36
36
  - **Svelte 5 Runes** &mdash; Built with `$state`, `$derived`, `$effect`, and `$props`
37
37
  - **Tailwind CSS 4** &mdash; Utility-first styling with theme tokens
38
38
  - **TypeScript** &mdash; Fully typed props and events
39
- - **132 Components** &mdash; Core primitives (forms, navigation, overlays) alongside buttons, text animations, backgrounds and effects
39
+ - **133 Components** &mdash; Core primitives (forms, navigation, overlays) alongside buttons, text animations, backgrounds and effects
40
40
  - **Dark Mode** &mdash; All components support light and dark themes
41
+ - **Opt-in Sound** &mdash; Eleven synthesised interface cues, a `SoundToggle` switch and a `soundFeedback` action; silent until the user turns it on
41
42
  - **Tested** &mdash; 2,900+ unit tests with Vitest and Testing Library
42
43
 
43
44
  ## Quick Start
@@ -151,7 +152,7 @@ Component tests are colocated with their component (see `RainbowButton.test.ts`
151
152
 
152
153
  ## Contributing
153
154
 
154
- Contributions are welcome! 132 components and counting — PRs for new components, bug fixes, and improvements are appreciated.
155
+ Contributions are welcome! 133 components and counting — PRs for new components, bug fixes, and improvements are appreciated.
155
156
 
156
157
  ### Adding a new component
157
158
 
@@ -0,0 +1,63 @@
1
+ <script lang="ts" module>
2
+ export interface FancyProviderProps {
3
+ /** The active skin. Changing it re-flows the whole subtree live. */
4
+ skin: Skin;
5
+ /** Scope `.dark` to this subtree for skins whose colorScheme is "dark". */
6
+ manageColorScheme?: boolean;
7
+ class?: string;
8
+ children?: Snippet;
9
+ }
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ import type { Snippet } from "svelte";
14
+ import { browser } from "$app/environment";
15
+ import { cn } from "../utils.js";
16
+ import type { Skin } from "./types.js";
17
+ import { setSkinContext } from "./context.js";
18
+ import "./cameleon.css";
19
+
20
+ let {
21
+ skin,
22
+ manageColorScheme = false,
23
+ class: className = "",
24
+ children,
25
+ }: FancyProviderProps = $props();
26
+
27
+ // Getter, not a plain value — this is what makes a live skin switch reactive.
28
+ setSkinContext({
29
+ get skin() {
30
+ return skin;
31
+ },
32
+ });
33
+
34
+ // Skin tokens as scoped inline CSS variables. Overriding these var names on
35
+ // this element re-flows every utility that reads them, for THIS subtree only.
36
+ // The provider never touches <html>, so it can't fight the global theme store.
37
+ const styleVars = $derived(
38
+ Object.entries(skin.tokens)
39
+ .map(([k, v]) => `${k}:${v}`)
40
+ .join(";")
41
+ );
42
+
43
+ // Inject skin webfonts once (deduped by id), client-only so SSR stays clean.
44
+ $effect(() => {
45
+ if (!browser || !skin.fonts) return;
46
+ for (const font of skin.fonts) {
47
+ if (document.getElementById(font.id)) continue;
48
+ const link = document.createElement("link");
49
+ link.id = font.id;
50
+ link.rel = "stylesheet";
51
+ link.href = font.href;
52
+ document.head.appendChild(link);
53
+ }
54
+ });
55
+ </script>
56
+
57
+ <div
58
+ data-skin={skin.name}
59
+ class={cn("cameleon-root", manageColorScheme && skin.colorScheme === "dark" && "dark", className)}
60
+ style={styleVars}
61
+ >
62
+ {@render children?.()}
63
+ </div>
@@ -0,0 +1,14 @@
1
+ export interface FancyProviderProps {
2
+ /** The active skin. Changing it re-flows the whole subtree live. */
3
+ skin: Skin;
4
+ /** Scope `.dark` to this subtree for skins whose colorScheme is "dark". */
5
+ manageColorScheme?: boolean;
6
+ class?: string;
7
+ children?: Snippet;
8
+ }
9
+ import type { Snippet } from "svelte";
10
+ import type { Skin } from "./types.js";
11
+ import "./cameleon.css";
12
+ declare const FancyProvider: import("svelte").Component<FancyProviderProps, {}, "">;
13
+ type FancyProvider = ReturnType<typeof FancyProvider>;
14
+ export default FancyProvider;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Base styles for the Cameleon provider root. The skin's tokens are set as
3
+ * inline CSS variables on the same element; these rules consume them so the
4
+ * whole "world" (page background, text color, base font) changes per skin.
5
+ */
6
+ .cameleon-root {
7
+ background: var(--skin-page-bg, transparent);
8
+ color: var(--skin-page-fg, inherit);
9
+ font-family: var(--skin-font-sans, inherit);
10
+ -webkit-font-smoothing: antialiased;
11
+ }
12
+
13
+ @media (prefers-reduced-motion: reduce) {
14
+ .cameleon-root * {
15
+ transition-duration: 0.01ms !important;
16
+ animation-duration: 0.01ms !important;
17
+ }
18
+ }
19
+
20
+ /* Skin-agnostic error affordance for the native range slider: the per-skin
21
+ .cam-range CSS styles the track/thumb but not the invalid state, so give
22
+ <Slider error> a consistent red outline driven by the skin's red token. */
23
+ .cam-range[aria-invalid="true"] {
24
+ outline: 2px solid var(--skin-red, #e5372b);
25
+ outline-offset: 3px;
26
+ border-radius: 2px;
27
+ }
@@ -0,0 +1,14 @@
1
+ import type { Skin } from "./types.js";
2
+ /**
3
+ * The context value is a GETTER object, not the skin itself. This is what makes
4
+ * a live skin switch reactive: consumers read `ctx.skin` inside a `$derived`, so
5
+ * changing the provider's `skin` prop re-runs their recipe resolution. (Same
6
+ * pattern as src/lib/fancy-ui/dock/Dock.svelte and card-3d — a plain value would
7
+ * be captured once and never update.)
8
+ */
9
+ export interface SkinContext {
10
+ readonly skin: Skin;
11
+ }
12
+ export declare function setSkinContext(ctx: SkinContext): void;
13
+ /** Read the active skin. Falls back to the default skin with no provider. */
14
+ export declare function useSkin(): SkinContext;
@@ -0,0 +1,15 @@
1
+ import { getContext, setContext } from "svelte";
2
+ import { defaultSkin } from "./skins/default.js";
3
+ const SKIN_KEY = Symbol("cameleon-skin");
4
+ export function setSkinContext(ctx) {
5
+ setContext(SKIN_KEY, ctx);
6
+ }
7
+ const FALLBACK = {
8
+ get skin() {
9
+ return defaultSkin;
10
+ },
11
+ };
12
+ /** Read the active skin. Falls back to the default skin with no provider. */
13
+ export function useSkin() {
14
+ return getContext(SKIN_KEY) ?? FALLBACK;
15
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Cameleon Engine — multi-skin UI primitives for fancy-ui.
3
+ *
4
+ * Same component API, radically different art directions ("skins"): each skin
5
+ * carries tokens (CSS custom properties), recipes (pure functions returning
6
+ * class strings per primitive), optional ornaments and per-skin CSS.
7
+ *
8
+ * Published as `fancy-ui-svelte/cameleon` (with the retro-os component kit at
9
+ * `fancy-ui-svelte/cameleon/retro-kit`). Consumers wrap a subtree:
10
+ *
11
+ * import { FancyProvider, Button, brutalSkin } from "fancy-ui-svelte/cameleon";
12
+ * <FancyProvider skin={brutalSkin}> ... </FancyProvider>
13
+ */
14
+ export { default as FancyProvider, type FancyProviderProps } from "./FancyProvider.svelte";
15
+ export { useSkin, setSkinContext, type SkinContext } from "./context.js";
16
+ export type { Skin, SkinRecipes, SkinOrnaments, SkinFont, SkinMeta, RecipeFn, RecipeArgs, RecipeResult, PartState, } from "./types.js";
17
+ export * from "./primitives/index.js";
18
+ export { defaultSkin } from "./skins/default.js";
19
+ export { auroraSkin } from "./skins/aurora/index.js";
20
+ export { brutalSkin } from "./skins/brutal/index.js";
21
+ export { glassSkin } from "./skins/glass/index.js";
22
+ export { terminalSkin } from "./skins/terminal/index.js";
23
+ export { retroOsSkin } from "./skins/retro-os/index.js";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Cameleon Engine — multi-skin UI primitives for fancy-ui.
3
+ *
4
+ * Same component API, radically different art directions ("skins"): each skin
5
+ * carries tokens (CSS custom properties), recipes (pure functions returning
6
+ * class strings per primitive), optional ornaments and per-skin CSS.
7
+ *
8
+ * Published as `fancy-ui-svelte/cameleon` (with the retro-os component kit at
9
+ * `fancy-ui-svelte/cameleon/retro-kit`). Consumers wrap a subtree:
10
+ *
11
+ * import { FancyProvider, Button, brutalSkin } from "fancy-ui-svelte/cameleon";
12
+ * <FancyProvider skin={brutalSkin}> ... </FancyProvider>
13
+ */
14
+ export { default as FancyProvider } from "./FancyProvider.svelte";
15
+ export { useSkin, setSkinContext } from "./context.js";
16
+ export * from "./primitives/index.js";
17
+ export { defaultSkin } from "./skins/default.js";
18
+ export { auroraSkin } from "./skins/aurora/index.js";
19
+ export { brutalSkin } from "./skins/brutal/index.js";
20
+ export { glassSkin } from "./skins/glass/index.js";
21
+ export { terminalSkin } from "./skins/terminal/index.js";
22
+ export { retroOsSkin } from "./skins/retro-os/index.js";
@@ -0,0 +1,23 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+ import type { PartState } from "../types.js";
4
+ export interface BadgeProps {
5
+ variant?: "default" | "solid" | "blue" | "red" | "yellow" | "green" | "purple";
6
+ /** Force a visual state for the /skins state matrix. */
7
+ demoState?: PartState;
8
+ class?: string;
9
+ children?: Snippet;
10
+ }
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import { cn } from "../../utils.js";
15
+ import { useSkin } from "../context.js";
16
+
17
+ let { variant = "default", demoState, class: className, children }: BadgeProps = $props();
18
+
19
+ const ctx = useSkin();
20
+ const parts = $derived(ctx.skin.recipes.badge({ variant, state: demoState }));
21
+ </script>
22
+
23
+ <span class={cn(parts.root, className)}>{@render children?.()}</span>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { PartState } from "../types.js";
3
+ export interface BadgeProps {
4
+ variant?: "default" | "solid" | "blue" | "red" | "yellow" | "green" | "purple";
5
+ /** Force a visual state for the /skins state matrix. */
6
+ demoState?: PartState;
7
+ class?: string;
8
+ children?: Snippet;
9
+ }
10
+ declare const Badge: import("svelte").Component<BadgeProps, {}, "">;
11
+ type Badge = ReturnType<typeof Badge>;
12
+ export default Badge;
@@ -0,0 +1,60 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLButtonAttributes } from "svelte/elements";
4
+
5
+ export interface ButtonProps extends Omit<HTMLButtonAttributes, "class"> {
6
+ variant?: "primary" | "secondary" | "destructive";
7
+ size?: "sm" | "md" | "lg";
8
+ loading?: boolean;
9
+ /** Force a visual state for the /skins state matrix (non-interactive). */
10
+ demoState?: "hover" | "active" | "focus";
11
+ class?: string;
12
+ children?: Snippet;
13
+ ref?: HTMLButtonElement | null;
14
+ }
15
+ </script>
16
+
17
+ <script lang="ts">
18
+ import { cn } from "../../utils.js";
19
+ import { useSkin } from "../context.js";
20
+ import type { RecipeArgs } from "../types.js";
21
+
22
+ let {
23
+ variant = "primary",
24
+ size = "md",
25
+ loading = false,
26
+ disabled = false,
27
+ type = "button",
28
+ demoState,
29
+ class: className,
30
+ children,
31
+ ref = $bindable(null),
32
+ ...rest
33
+ }: ButtonProps = $props();
34
+
35
+ const ctx = useSkin();
36
+ const args = $derived<RecipeArgs>({
37
+ variant,
38
+ size,
39
+ state: demoState ?? (loading ? "loading" : disabled ? "disabled" : "default"),
40
+ });
41
+ // Re-runs whenever the active skin changes → live art-direction swap.
42
+ const parts = $derived(ctx.skin.recipes.button(args));
43
+ const trailing = $derived(ctx.skin.ornaments?.buttonTrailing);
44
+ </script>
45
+
46
+ <button
47
+ bind:this={ref}
48
+ {type}
49
+ class={cn("cam-btn", parts.root, className)}
50
+ disabled={disabled || loading}
51
+ data-variant={variant}
52
+ {...rest}
53
+ >
54
+ {#if parts.label !== undefined}
55
+ <span class={parts.label}>{@render children?.()}</span>
56
+ {:else}
57
+ {@render children?.()}
58
+ {/if}
59
+ {#if trailing}{@render trailing(args)}{/if}
60
+ </button>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLButtonAttributes } from "svelte/elements";
3
+ export interface ButtonProps extends Omit<HTMLButtonAttributes, "class"> {
4
+ variant?: "primary" | "secondary" | "destructive";
5
+ size?: "sm" | "md" | "lg";
6
+ loading?: boolean;
7
+ /** Force a visual state for the /skins state matrix (non-interactive). */
8
+ demoState?: "hover" | "active" | "focus";
9
+ class?: string;
10
+ children?: Snippet;
11
+ ref?: HTMLButtonElement | null;
12
+ }
13
+ declare const Button: import("svelte").Component<ButtonProps, {}, "ref">;
14
+ type Button = ReturnType<typeof Button>;
15
+ export default Button;
@@ -0,0 +1,56 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLInputAttributes } from "svelte/elements";
4
+ import type { PartState } from "../types.js";
5
+ export interface CheckboxProps extends Omit<HTMLInputAttributes, "class" | "type"> {
6
+ checked?: boolean;
7
+ error?: boolean;
8
+ demoState?: PartState;
9
+ class?: string;
10
+ children?: Snippet;
11
+ ref?: HTMLInputElement | null;
12
+ }
13
+ </script>
14
+
15
+ <script lang="ts">
16
+ import { cn } from "../../utils.js";
17
+ import { useSkin } from "../context.js";
18
+
19
+ let {
20
+ checked = $bindable(false),
21
+ error = false,
22
+ demoState,
23
+ class: className,
24
+ children,
25
+ ref = $bindable(null),
26
+ ...rest
27
+ }: CheckboxProps = $props();
28
+
29
+ const ctx = useSkin();
30
+ const parts = $derived(ctx.skin.recipes.checkbox({ state: demoState }));
31
+ </script>
32
+
33
+ <label class={cn(parts.root, className)}>
34
+ <input
35
+ type="checkbox"
36
+ class="peer sr-only"
37
+ bind:this={ref}
38
+ bind:checked
39
+ aria-invalid={error}
40
+ {...rest}
41
+ />
42
+ <span class={parts.box}>
43
+ <svg
44
+ width="12"
45
+ height="12"
46
+ viewBox="0 0 24 24"
47
+ fill="none"
48
+ stroke="currentColor"
49
+ stroke-width="3.5"
50
+ aria-hidden="true"
51
+ >
52
+ <path d="M5 13l4 4L19 7" stroke-linecap="round" stroke-linejoin="round" />
53
+ </svg>
54
+ </span>
55
+ {#if children}{@render children()}{/if}
56
+ </label>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLInputAttributes } from "svelte/elements";
3
+ import type { PartState } from "../types.js";
4
+ export interface CheckboxProps extends Omit<HTMLInputAttributes, "class" | "type"> {
5
+ checked?: boolean;
6
+ error?: boolean;
7
+ demoState?: PartState;
8
+ class?: string;
9
+ children?: Snippet;
10
+ ref?: HTMLInputElement | null;
11
+ }
12
+ declare const Checkbox: import("svelte").Component<CheckboxProps, {}, "ref" | "checked">;
13
+ type Checkbox = ReturnType<typeof Checkbox>;
14
+ export default Checkbox;
@@ -0,0 +1,37 @@
1
+ <script lang="ts" module>
2
+ import type { HTMLInputAttributes } from "svelte/elements";
3
+ import type { PartState } from "../types.js";
4
+ export interface InputProps extends Omit<HTMLInputAttributes, "class"> {
5
+ error?: boolean;
6
+ /** Force a visual state for the /skins state matrix. */
7
+ demoState?: PartState;
8
+ class?: string;
9
+ value?: string;
10
+ ref?: HTMLInputElement | null;
11
+ }
12
+ </script>
13
+
14
+ <script lang="ts">
15
+ import { cn } from "../../utils.js";
16
+ import { useSkin } from "../context.js";
17
+
18
+ let {
19
+ error = false,
20
+ demoState,
21
+ class: className,
22
+ value = $bindable(""),
23
+ ref = $bindable(null),
24
+ ...rest
25
+ }: InputProps = $props();
26
+
27
+ const ctx = useSkin();
28
+ const parts = $derived(ctx.skin.recipes.input({ state: demoState }));
29
+ </script>
30
+
31
+ <input
32
+ bind:this={ref}
33
+ bind:value
34
+ class={cn(parts.root, className)}
35
+ aria-invalid={error}
36
+ {...rest}
37
+ />
@@ -0,0 +1,13 @@
1
+ import type { HTMLInputAttributes } from "svelte/elements";
2
+ import type { PartState } from "../types.js";
3
+ export interface InputProps extends Omit<HTMLInputAttributes, "class"> {
4
+ error?: boolean;
5
+ /** Force a visual state for the /skins state matrix. */
6
+ demoState?: PartState;
7
+ class?: string;
8
+ value?: string;
9
+ ref?: HTMLInputElement | null;
10
+ }
11
+ declare const Input: import("svelte").Component<InputProps, {}, "ref" | "value">;
12
+ type Input = ReturnType<typeof Input>;
13
+ export default Input;
@@ -0,0 +1,53 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLInputAttributes } from "svelte/elements";
4
+ import type { PartState } from "../types.js";
5
+ export interface RadioProps extends Omit<HTMLInputAttributes, "class" | "type"> {
6
+ group?: string;
7
+ value?: string;
8
+ error?: boolean;
9
+ demoState?: PartState;
10
+ class?: string;
11
+ children?: Snippet;
12
+ ref?: HTMLInputElement | null;
13
+ }
14
+ </script>
15
+
16
+ <script lang="ts">
17
+ import { cn } from "../../utils.js";
18
+ import { useSkin } from "../context.js";
19
+
20
+ let {
21
+ group = $bindable(),
22
+ value,
23
+ name,
24
+ error = false,
25
+ demoState,
26
+ class: className,
27
+ children,
28
+ ref = $bindable(null),
29
+ ...rest
30
+ }: RadioProps = $props();
31
+
32
+ const ctx = useSkin();
33
+ const parts = $derived(ctx.skin.recipes.radio({ state: demoState }));
34
+ </script>
35
+
36
+ <label class={cn(parts.root, className)}>
37
+ <!-- radio invalidity belongs on the group, not the input; drive the demo
38
+ error visual off a data-attribute so we don't misuse aria-invalid.
39
+ name defaults to the group so same-group radios stay mutually exclusive
40
+ natively even when the parent doesn't share a bound group variable. -->
41
+ <input
42
+ type="radio"
43
+ class="peer sr-only"
44
+ bind:this={ref}
45
+ bind:group
46
+ name={name ?? group}
47
+ {value}
48
+ data-invalid={error ? "true" : undefined}
49
+ {...rest}
50
+ />
51
+ <span class={parts.box}></span>
52
+ {#if children}{@render children()}{/if}
53
+ </label>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLInputAttributes } from "svelte/elements";
3
+ import type { PartState } from "../types.js";
4
+ export interface RadioProps extends Omit<HTMLInputAttributes, "class" | "type"> {
5
+ group?: string;
6
+ value?: string;
7
+ error?: boolean;
8
+ demoState?: PartState;
9
+ class?: string;
10
+ children?: Snippet;
11
+ ref?: HTMLInputElement | null;
12
+ }
13
+ declare const Radio: import("svelte").Component<RadioProps, {}, "ref" | "group">;
14
+ type Radio = ReturnType<typeof Radio>;
15
+ export default Radio;
@@ -0,0 +1,49 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLSelectAttributes } from "svelte/elements";
4
+ import type { PartState } from "../types.js";
5
+ export interface SelectProps extends Omit<HTMLSelectAttributes, "class"> {
6
+ error?: boolean;
7
+ demoState?: PartState;
8
+ class?: string;
9
+ value?: string;
10
+ children?: Snippet;
11
+ ref?: HTMLSelectElement | null;
12
+ }
13
+ </script>
14
+
15
+ <script lang="ts">
16
+ import { cn } from "../../utils.js";
17
+ import { useSkin } from "../context.js";
18
+
19
+ let {
20
+ error = false,
21
+ demoState,
22
+ class: className,
23
+ value = $bindable(""),
24
+ children,
25
+ ref = $bindable(null),
26
+ ...rest
27
+ }: SelectProps = $props();
28
+
29
+ const ctx = useSkin();
30
+ const parts = $derived(ctx.skin.recipes.select({ state: demoState }));
31
+ </script>
32
+
33
+ <div class={cn(parts.root, className)}>
34
+ <select bind:this={ref} bind:value class={parts.field} aria-invalid={error} {...rest}>
35
+ {@render children?.()}
36
+ </select>
37
+ <span class={parts.chevron} aria-hidden="true">
38
+ <svg
39
+ width="14"
40
+ height="14"
41
+ viewBox="0 0 24 24"
42
+ fill="none"
43
+ stroke="currentColor"
44
+ stroke-width="2.5"
45
+ >
46
+ <path d="M6 9l6 6 6-6" stroke-linecap="round" stroke-linejoin="round" />
47
+ </svg>
48
+ </span>
49
+ </div>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLSelectAttributes } from "svelte/elements";
3
+ import type { PartState } from "../types.js";
4
+ export interface SelectProps extends Omit<HTMLSelectAttributes, "class"> {
5
+ error?: boolean;
6
+ demoState?: PartState;
7
+ class?: string;
8
+ value?: string;
9
+ children?: Snippet;
10
+ ref?: HTMLSelectElement | null;
11
+ }
12
+ declare const Select: import("svelte").Component<SelectProps, {}, "ref" | "value">;
13
+ type Select = ReturnType<typeof Select>;
14
+ export default Select;
@@ -0,0 +1,34 @@
1
+ <script lang="ts" module>
2
+ import type { HTMLInputAttributes } from "svelte/elements";
3
+ export interface SliderProps extends Omit<HTMLInputAttributes, "class" | "type"> {
4
+ error?: boolean;
5
+ class?: string;
6
+ value?: number;
7
+ ref?: HTMLInputElement | null;
8
+ }
9
+ </script>
10
+
11
+ <script lang="ts">
12
+ import { cn } from "../../utils.js";
13
+ import { useSkin } from "../context.js";
14
+
15
+ let {
16
+ error = false,
17
+ class: className,
18
+ value = $bindable(50),
19
+ ref = $bindable(null),
20
+ ...rest
21
+ }: SliderProps = $props();
22
+
23
+ const ctx = useSkin();
24
+ const parts = $derived(ctx.skin.recipes.slider({ state: error ? "error" : undefined }));
25
+ </script>
26
+
27
+ <input
28
+ type="range"
29
+ bind:this={ref}
30
+ bind:value
31
+ class={cn(parts.root, className)}
32
+ aria-invalid={error || undefined}
33
+ {...rest}
34
+ />
@@ -0,0 +1,10 @@
1
+ import type { HTMLInputAttributes } from "svelte/elements";
2
+ export interface SliderProps extends Omit<HTMLInputAttributes, "class" | "type"> {
3
+ error?: boolean;
4
+ class?: string;
5
+ value?: number;
6
+ ref?: HTMLInputElement | null;
7
+ }
8
+ declare const Slider: import("svelte").Component<SliderProps, {}, "ref" | "value">;
9
+ type Slider = ReturnType<typeof Slider>;
10
+ export default Slider;