@joewinke/jatui 0.1.33 → 0.2.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 (209) hide show
  1. package/CLAUDE.md +22 -0
  2. package/dist/actions/attention.d.ts +24 -0
  3. package/dist/actions/attention.js +94 -0
  4. package/dist/actions/floating.d.ts +53 -0
  5. package/dist/actions/floating.js +158 -0
  6. package/dist/actions/staggerEntrance.d.ts +28 -0
  7. package/dist/actions/staggerEntrance.js +71 -0
  8. package/dist/actions/tooltip.d.ts +28 -0
  9. package/dist/actions/tooltip.js +186 -0
  10. package/dist/assets/sounds/README.md +47 -0
  11. package/dist/assets/sounds/error.d.ts +1 -0
  12. package/dist/assets/sounds/error.js +4 -0
  13. package/dist/assets/sounds/notify.d.ts +1 -0
  14. package/dist/assets/sounds/notify.js +4 -0
  15. package/dist/assets/sounds/pop.d.ts +1 -0
  16. package/dist/assets/sounds/pop.js +4 -0
  17. package/dist/assets/sounds/send.d.ts +1 -0
  18. package/dist/assets/sounds/send.js +4 -0
  19. package/dist/assets/sounds/success.d.ts +1 -0
  20. package/dist/assets/sounds/success.js +4 -0
  21. package/dist/assets/sounds/toggle.d.ts +1 -0
  22. package/dist/assets/sounds/toggle.js +4 -0
  23. package/dist/components/AnimatedNumber.svelte +112 -0
  24. package/dist/components/AnimatedNumber.svelte.d.ts +8 -0
  25. package/dist/components/Badge.svelte +75 -4
  26. package/dist/components/Badge.svelte.d.ts +6 -1
  27. package/dist/components/Button.svelte +196 -21
  28. package/dist/components/Button.svelte.d.ts +10 -1
  29. package/dist/components/Carousel.svelte +265 -0
  30. package/dist/components/Carousel.svelte.d.ts +21 -0
  31. package/dist/components/ChipInput.svelte +63 -3
  32. package/dist/components/Collapse.svelte +70 -14
  33. package/dist/components/Collapse.svelte.d.ts +11 -0
  34. package/dist/components/ColorSelector.svelte +21 -24
  35. package/dist/components/CommandPalette.svelte +475 -0
  36. package/dist/components/CommandPalette.svelte.d.ts +3 -0
  37. package/dist/components/ContextMenu.svelte +9 -73
  38. package/dist/components/CopyButton.svelte +156 -0
  39. package/dist/components/CopyButton.svelte.d.ts +12 -0
  40. package/dist/components/HoldToConfirm.svelte +183 -0
  41. package/dist/components/HoldToConfirm.svelte.d.ts +11 -0
  42. package/dist/components/KeyboardShortcutsOverlay.svelte +54 -8
  43. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +3 -0
  44. package/dist/components/PasswordMeter.svelte +87 -0
  45. package/dist/components/PasswordMeter.svelte.d.ts +8 -0
  46. package/dist/components/PinInput.svelte +186 -0
  47. package/dist/components/PinInput.svelte.d.ts +13 -0
  48. package/dist/components/QuantityStepper.svelte +185 -0
  49. package/dist/components/QuantityStepper.svelte.d.ts +12 -0
  50. package/dist/components/Skeleton.svelte +123 -0
  51. package/dist/components/Skeleton.svelte.d.ts +12 -0
  52. package/dist/components/SpeechForm.svelte +37 -0
  53. package/dist/components/StatusBadge.svelte +31 -3
  54. package/dist/components/StatusBadge.svelte.d.ts +0 -5
  55. package/dist/components/Tabs.svelte +270 -0
  56. package/dist/components/Tabs.svelte.d.ts +15 -0
  57. package/dist/components/TextInput.svelte +138 -38
  58. package/dist/components/TextInput.svelte.d.ts +9 -1
  59. package/dist/components/VoicePlayer.svelte +51 -0
  60. package/dist/components/basic/Alert.svelte +36 -0
  61. package/dist/components/basic/Alert.svelte.d.ts +9 -0
  62. package/dist/components/basic/Breadcrumb.svelte +27 -0
  63. package/dist/components/basic/Breadcrumb.svelte.d.ts +11 -0
  64. package/dist/components/basic/ButtonGroup.svelte +15 -0
  65. package/dist/components/basic/ButtonGroup.svelte.d.ts +8 -0
  66. package/dist/components/basic/Checkbox.svelte +85 -0
  67. package/dist/components/basic/Checkbox.svelte.d.ts +15 -0
  68. package/dist/components/basic/Empty.svelte +17 -0
  69. package/dist/components/basic/Empty.svelte.d.ts +8 -0
  70. package/dist/components/basic/Kbd.svelte +16 -0
  71. package/dist/components/basic/Kbd.svelte.d.ts +8 -0
  72. package/dist/components/basic/Pagination.svelte +104 -0
  73. package/dist/components/basic/Pagination.svelte.d.ts +12 -0
  74. package/dist/components/basic/RadioGroup.svelte +71 -0
  75. package/dist/components/basic/RadioGroup.svelte.d.ts +18 -0
  76. package/dist/components/basic/Separator.svelte +19 -0
  77. package/dist/components/basic/Separator.svelte.d.ts +8 -0
  78. package/dist/components/basic/Slider.svelte +72 -0
  79. package/dist/components/basic/Slider.svelte.d.ts +17 -0
  80. package/dist/components/basic/Switch.svelte +88 -0
  81. package/dist/components/basic/Switch.svelte.d.ts +14 -0
  82. package/dist/components/basic/Toggle.svelte +43 -0
  83. package/dist/components/basic/Toggle.svelte.d.ts +14 -0
  84. package/dist/components/graph/GraphCanvas.svelte +103 -0
  85. package/dist/components/graph/GraphCanvas.svelte.d.ts +22 -0
  86. package/dist/components/graph/JatuiNode.svelte +128 -0
  87. package/dist/components/graph/JatuiNode.svelte.d.ts +14 -0
  88. package/dist/components/graph/theme.d.ts +27 -0
  89. package/dist/components/graph/theme.js +136 -0
  90. package/dist/components/messaging/MessageItem.svelte +92 -0
  91. package/dist/components/messaging/MessageItem.svelte.d.ts +5 -0
  92. package/dist/components/messaging/TypingIndicator.svelte +63 -0
  93. package/dist/components/messaging/TypingIndicator.svelte.d.ts +7 -0
  94. package/dist/components/pipeline/PipelineColumn.svelte +44 -3
  95. package/dist/components/progress/ProgressBar.svelte +189 -0
  96. package/dist/components/progress/ProgressBar.svelte.d.ts +17 -0
  97. package/dist/components/progress/ProgressRing.svelte +138 -0
  98. package/dist/components/progress/ProgressRing.svelte.d.ts +12 -0
  99. package/dist/components/progress/Steps.svelte +195 -0
  100. package/dist/components/progress/Steps.svelte.d.ts +14 -0
  101. package/dist/components/shell/NavigationMenu.svelte +422 -0
  102. package/dist/components/shell/NavigationMenu.svelte.d.ts +43 -0
  103. package/dist/components/shell/Sidebar.svelte +276 -0
  104. package/dist/components/shell/Sidebar.svelte.d.ts +32 -0
  105. package/dist/components/shell/SidebarGroup.svelte +180 -0
  106. package/dist/components/shell/SidebarGroup.svelte.d.ts +19 -0
  107. package/dist/components/shell/SidebarItem.svelte +170 -0
  108. package/dist/components/shell/SidebarItem.svelte.d.ts +24 -0
  109. package/dist/components/shell/shell.d.ts +30 -0
  110. package/dist/components/shell/shell.js +65 -0
  111. package/dist/components/table/DataTable.svelte +364 -0
  112. package/dist/components/table/DataTable.svelte.d.ts +4 -0
  113. package/dist/components/table/flexRender.d.ts +31 -0
  114. package/dist/components/table/flexRender.js +32 -0
  115. package/dist/components/table/types.d.ts +28 -0
  116. package/dist/components/table/types.js +1 -0
  117. package/dist/components/toast/ToastHost.svelte +21 -0
  118. package/dist/components/toast/ToastHost.svelte.d.ts +3 -0
  119. package/dist/components/toast/ToastItem.svelte +287 -0
  120. package/dist/components/toast/ToastItem.svelte.d.ts +9 -0
  121. package/dist/index.d.ts +59 -0
  122. package/dist/index.js +67 -0
  123. package/dist/showcase/AnimatedBeam.svelte +183 -0
  124. package/dist/showcase/AnimatedBeam.svelte.d.ts +26 -0
  125. package/dist/showcase/AuroraDrift.svelte +74 -0
  126. package/dist/showcase/AuroraDrift.svelte.d.ts +9 -0
  127. package/dist/showcase/BentoCard.svelte +50 -0
  128. package/dist/showcase/BentoCard.svelte.d.ts +14 -0
  129. package/dist/showcase/BentoGrid.svelte +43 -0
  130. package/dist/showcase/BentoGrid.svelte.d.ts +12 -0
  131. package/dist/showcase/BorderBeam.svelte +79 -0
  132. package/dist/showcase/BorderBeam.svelte.d.ts +12 -0
  133. package/dist/showcase/ClipWipe.svelte +82 -0
  134. package/dist/showcase/ClipWipe.svelte.d.ts +12 -0
  135. package/dist/showcase/Confetti.svelte +71 -0
  136. package/dist/showcase/Confetti.svelte.d.ts +10 -0
  137. package/dist/showcase/CursorSpotlight.svelte +85 -0
  138. package/dist/showcase/CursorSpotlight.svelte.d.ts +9 -0
  139. package/dist/showcase/DeviceMock.svelte +186 -0
  140. package/dist/showcase/DeviceMock.svelte.d.ts +11 -0
  141. package/dist/showcase/Dock.svelte +123 -0
  142. package/dist/showcase/Dock.svelte.d.ts +17 -0
  143. package/dist/showcase/FlipCard.svelte +103 -0
  144. package/dist/showcase/FlipCard.svelte.d.ts +10 -0
  145. package/dist/showcase/FocusReveal.svelte +83 -0
  146. package/dist/showcase/FocusReveal.svelte.d.ts +11 -0
  147. package/dist/showcase/Globe.svelte +202 -0
  148. package/dist/showcase/Globe.svelte.d.ts +16 -0
  149. package/dist/showcase/KenBurns.svelte +105 -0
  150. package/dist/showcase/KenBurns.svelte.d.ts +13 -0
  151. package/dist/showcase/Marquee.svelte +89 -0
  152. package/dist/showcase/Marquee.svelte.d.ts +9 -0
  153. package/dist/showcase/ParallaxTilt.svelte +98 -0
  154. package/dist/showcase/ParallaxTilt.svelte.d.ts +10 -0
  155. package/dist/showcase/ScrambleText.svelte +98 -0
  156. package/dist/showcase/ScrambleText.svelte.d.ts +11 -0
  157. package/dist/showcase/ShineSweep.svelte +81 -0
  158. package/dist/showcase/ShineSweep.svelte.d.ts +9 -0
  159. package/dist/showcase/Terminal.svelte +230 -0
  160. package/dist/showcase/Terminal.svelte.d.ts +18 -0
  161. package/dist/showcase/TextSplitReveal.svelte +146 -0
  162. package/dist/showcase/TextSplitReveal.svelte.d.ts +13 -0
  163. package/dist/showcase/TextWave.svelte +55 -0
  164. package/dist/showcase/TextWave.svelte.d.ts +8 -0
  165. package/dist/showcase/TypewriterText.svelte +130 -0
  166. package/dist/showcase/TypewriterText.svelte.d.ts +9 -0
  167. package/dist/showcase/backdrop/GrainGradient.svelte +126 -0
  168. package/dist/showcase/backdrop/GrainGradient.svelte.d.ts +23 -0
  169. package/dist/showcase/backdrop/MeshGradient.svelte +104 -0
  170. package/dist/showcase/backdrop/MeshGradient.svelte.d.ts +20 -0
  171. package/dist/showcase/backdrop/Metaballs.svelte +116 -0
  172. package/dist/showcase/backdrop/Metaballs.svelte.d.ts +18 -0
  173. package/dist/showcase/backdrop/NeuroNoise.svelte +115 -0
  174. package/dist/showcase/backdrop/NeuroNoise.svelte.d.ts +20 -0
  175. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte +48 -0
  176. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte.d.ts +12 -0
  177. package/dist/showcase/generative/GenerativeBackdrop.svelte +234 -0
  178. package/dist/showcase/generative/GenerativeBackdrop.svelte.d.ts +9 -0
  179. package/dist/showcase/generative/intent.d.ts +7 -0
  180. package/dist/showcase/generative/intent.js +67 -0
  181. package/dist/showcase/generative/manifest.d.ts +9 -0
  182. package/dist/showcase/generative/manifest.js +97 -0
  183. package/dist/showcase/generative/types.d.ts +48 -0
  184. package/dist/showcase/generative/types.js +8 -0
  185. package/dist/showcase/index.d.ts +36 -0
  186. package/dist/showcase/index.js +46 -0
  187. package/dist/showcase/scroll/SmoothScroll.svelte +49 -0
  188. package/dist/showcase/scroll/SmoothScroll.svelte.d.ts +12 -0
  189. package/dist/showcase/scroll/scrollProgress.d.ts +24 -0
  190. package/dist/showcase/scroll/scrollProgress.js +65 -0
  191. package/dist/showcase/scroll/scrollReveal.d.ts +30 -0
  192. package/dist/showcase/scroll/scrollReveal.js +70 -0
  193. package/dist/stores/commandPalette.svelte.d.ts +24 -0
  194. package/dist/stores/commandPalette.svelte.js +82 -0
  195. package/dist/stores/toast.svelte.d.ts +28 -0
  196. package/dist/stores/toast.svelte.js +33 -0
  197. package/dist/styles/motion.css +46 -0
  198. package/dist/styles/tokens.css +31 -0
  199. package/dist/transitions/glide.d.ts +31 -0
  200. package/dist/transitions/glide.js +58 -0
  201. package/dist/utils/sound.d.ts +6 -0
  202. package/dist/utils/sound.js +113 -0
  203. package/dist/utils/splitText.d.ts +25 -0
  204. package/dist/utils/splitText.js +94 -0
  205. package/dist/utils/themeWipe.d.ts +21 -0
  206. package/dist/utils/themeWipe.js +94 -0
  207. package/dist/utils/viewTransition.d.ts +23 -0
  208. package/dist/utils/viewTransition.js +75 -0
  209. package/package.json +50 -3
@@ -0,0 +1,186 @@
1
+ <script lang="ts">
2
+ // pattern after magicui.design (MIT)
3
+ import type { Snippet } from 'svelte';
4
+
5
+ type Variant = 'iphone' | 'safari' | 'android';
6
+
7
+ interface Props {
8
+ variant?: Variant;
9
+ width?: number;
10
+ class?: string;
11
+ children?: Snippet;
12
+ }
13
+
14
+ let { variant = 'iphone', width = 300, class: className = '', children }: Props = $props();
15
+
16
+ // Viewport sizes relative to SVG viewBox
17
+ const configs = {
18
+ iphone: {
19
+ viewBox: '0 0 390 844',
20
+ // Screen area inside the device frame
21
+ screenX: 15,
22
+ screenY: 55,
23
+ screenW: 360,
24
+ screenH: 734
25
+ },
26
+ safari: {
27
+ viewBox: '0 0 1280 832',
28
+ screenX: 1,
29
+ screenY: 88,
30
+ screenW: 1278,
31
+ screenH: 743
32
+ },
33
+ android: {
34
+ viewBox: '0 0 390 844',
35
+ screenX: 12,
36
+ screenY: 24,
37
+ screenW: 366,
38
+ screenH: 796
39
+ }
40
+ };
41
+
42
+ const cfg = $derived(configs[variant]);
43
+ const aspectRatio = $derived(
44
+ (() => {
45
+ const [vw, vh] = cfg.viewBox.split(' ').slice(2).map(Number);
46
+ return vh / vw;
47
+ })()
48
+ );
49
+ const height = $derived(Math.round(width * aspectRatio));
50
+ const screenAspect = $derived(cfg.screenH / cfg.screenW);
51
+ </script>
52
+
53
+ <div
54
+ class="device-mock {className}"
55
+ style="width: {width}px; height: {height}px;"
56
+ role="img"
57
+ aria-label="{variant} device mockup"
58
+ >
59
+ <svg
60
+ viewBox={cfg.viewBox}
61
+ fill="none"
62
+ xmlns="http://www.w3.org/2000/svg"
63
+ class="device-frame"
64
+ aria-hidden="true"
65
+ >
66
+ {#if variant === 'iphone'}
67
+ <!-- iPhone frame -->
68
+ <rect
69
+ x="1"
70
+ y="1"
71
+ width="388"
72
+ height="842"
73
+ rx="55"
74
+ ry="55"
75
+ fill="oklch(var(--b2, var(--b1)))"
76
+ stroke="oklch(var(--bc) / 0.15)"
77
+ stroke-width="2"
78
+ />
79
+ <!-- Side buttons -->
80
+ <rect x="-2" y="160" width="4" height="60" rx="2" fill="oklch(var(--bc) / 0.2)" />
81
+ <rect x="-2" y="230" width="4" height="90" rx="2" fill="oklch(var(--bc) / 0.2)" />
82
+ <rect x="-2" y="330" width="4" height="90" rx="2" fill="oklch(var(--bc) / 0.2)" />
83
+ <rect x="388" y="220" width="4" height="120" rx="2" fill="oklch(var(--bc) / 0.2)" />
84
+ <!-- Screen bezel -->
85
+ <rect x="15" y="55" width="360" height="734" rx="42" ry="42" fill="oklch(var(--b1))" />
86
+ <!-- Dynamic island -->
87
+ <rect x="155" y="20" width="80" height="30" rx="15" fill="oklch(var(--n))" />
88
+ <!-- Home indicator -->
89
+ <rect x="150" y="808" width="90" height="5" rx="2.5" fill="oklch(var(--bc) / 0.25)" />
90
+ {:else if variant === 'safari'}
91
+ <!-- Safari browser window -->
92
+ <rect
93
+ x="0"
94
+ y="0"
95
+ width="1280"
96
+ height="832"
97
+ rx="10"
98
+ ry="10"
99
+ fill="oklch(var(--b2, var(--b1)))"
100
+ stroke="oklch(var(--bc) / 0.15)"
101
+ stroke-width="1"
102
+ />
103
+ <!-- Title bar -->
104
+ <rect x="0" y="0" width="1280" height="88" rx="10" fill="oklch(var(--b3, var(--b2, var(--b1))))" />
105
+ <rect x="10" y="44" width="1260" height="44" rx="0" fill="oklch(var(--b3, var(--b2, var(--b1))))" />
106
+ <!-- Traffic lights -->
107
+ <circle cx="20" cy="22" r="7" fill="#ff5f57" />
108
+ <circle cx="44" cy="22" r="7" fill="#febc2e" />
109
+ <circle cx="68" cy="22" r="7" fill="#28c840" />
110
+ <!-- Address bar -->
111
+ <rect
112
+ x="320"
113
+ y="52"
114
+ width="640"
115
+ height="28"
116
+ rx="14"
117
+ fill="oklch(var(--b1))"
118
+ stroke="oklch(var(--bc) / 0.1)"
119
+ stroke-width="1"
120
+ />
121
+ <!-- Screen content area -->
122
+ <rect x="1" y="88" width="1278" height="743" fill="oklch(var(--b1))" />
123
+ <!-- Bottom round corners clip -->
124
+ <rect x="0" y="822" width="1280" height="10" rx="0" fill="oklch(var(--b2, var(--b1)))" />
125
+ {:else}
126
+ <!-- Android frame -->
127
+ <rect
128
+ x="1"
129
+ y="1"
130
+ width="388"
131
+ height="842"
132
+ rx="42"
133
+ ry="42"
134
+ fill="oklch(var(--b2, var(--b1)))"
135
+ stroke="oklch(var(--bc) / 0.15)"
136
+ stroke-width="2"
137
+ />
138
+ <!-- Side buttons -->
139
+ <rect x="-2" y="200" width="4" height="80" rx="2" fill="oklch(var(--bc) / 0.2)" />
140
+ <rect x="388" y="180" width="4" height="60" rx="2" fill="oklch(var(--bc) / 0.2)" />
141
+ <rect x="388" y="250" width="4" height="100" rx="2" fill="oklch(var(--bc) / 0.2)" />
142
+ <!-- Screen -->
143
+ <rect x="12" y="24" width="366" height="796" rx="34" fill="oklch(var(--b1))" />
144
+ <!-- Front camera -->
145
+ <circle cx="195" cy="44" r="6" fill="oklch(var(--n))" />
146
+ <!-- Nav bar -->
147
+ <rect x="130" y="820" width="130" height="5" rx="2.5" fill="oklch(var(--bc) / 0.25)" />
148
+ {/if}
149
+ </svg>
150
+
151
+ <!-- Slot content rendered inside screen area -->
152
+ <div
153
+ class="device-screen"
154
+ style="
155
+ left: {(cfg.screenX / Number(cfg.viewBox.split(' ')[2])) * 100}%;
156
+ top: {(cfg.screenY / Number(cfg.viewBox.split(' ')[3])) * 100}%;
157
+ width: {(cfg.screenW / Number(cfg.viewBox.split(' ')[2])) * 100}%;
158
+ height: {(cfg.screenH / Number(cfg.viewBox.split(' ')[3])) * 100}%;
159
+ "
160
+ >
161
+ {#if children}
162
+ {@render children()}
163
+ {/if}
164
+ </div>
165
+ </div>
166
+
167
+ <style>
168
+ .device-mock {
169
+ position: relative;
170
+ display: inline-block;
171
+ flex-shrink: 0;
172
+ }
173
+
174
+ .device-frame {
175
+ position: absolute;
176
+ inset: 0;
177
+ width: 100%;
178
+ height: 100%;
179
+ }
180
+
181
+ .device-screen {
182
+ position: absolute;
183
+ overflow: hidden;
184
+ border-radius: inherit;
185
+ }
186
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Variant = 'iphone' | 'safari' | 'android';
3
+ interface Props {
4
+ variant?: Variant;
5
+ width?: number;
6
+ class?: string;
7
+ children?: Snippet;
8
+ }
9
+ declare const DeviceMock: import("svelte").Component<Props, {}, "">;
10
+ type DeviceMock = ReturnType<typeof DeviceMock>;
11
+ export default DeviceMock;
@@ -0,0 +1,123 @@
1
+ <script lang="ts">
2
+ // pattern after magicui.design (MIT)
3
+
4
+ export interface DockItem {
5
+ label: string;
6
+ /** Inline SVG string for the icon */
7
+ iconSvg?: string;
8
+ onclick?: () => void;
9
+ }
10
+
11
+ interface Props {
12
+ items: DockItem[];
13
+ /** Max scale factor at pointer proximity */
14
+ magnification?: number;
15
+ /** Radius in px around pointer that triggers magnification */
16
+ radius?: number;
17
+ class?: string;
18
+ }
19
+
20
+ let { items, magnification = 1.6, radius = 120, class: className = '' }: Props = $props();
21
+
22
+ let pointerX = $state<number | null>(null);
23
+ let reducedMotion = $state(false);
24
+ let dockEl: HTMLElement | undefined = $state();
25
+ let pointerFine = $state(false);
26
+
27
+ $effect(() => {
28
+ reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
29
+ pointerFine = window.matchMedia('(pointer: fine)').matches;
30
+ });
31
+
32
+ function scaleForItem(index: number): number {
33
+ if (reducedMotion || !pointerFine || pointerX === null || !dockEl) return 1;
34
+
35
+ const itemEls = dockEl.querySelectorAll<HTMLElement>('.dock-item');
36
+ const itemEl = itemEls[index];
37
+ if (!itemEl) return 1;
38
+
39
+ const dockRect = dockEl.getBoundingClientRect();
40
+ const itemRect = itemEl.getBoundingClientRect();
41
+ const itemCenter = itemRect.left + itemRect.width / 2 - dockRect.left;
42
+ const dist = Math.abs(pointerX - itemCenter);
43
+ if (dist >= radius) return 1;
44
+
45
+ const t = Math.cos((dist / radius) * (Math.PI / 2));
46
+ return 1 + (magnification - 1) * t;
47
+ }
48
+
49
+ function onMouseMove(e: MouseEvent) {
50
+ if (!dockEl) return;
51
+ const rect = dockEl.getBoundingClientRect();
52
+ pointerX = e.clientX - rect.left;
53
+ }
54
+
55
+ function onMouseLeave() {
56
+ pointerX = null;
57
+ }
58
+ </script>
59
+
60
+ <nav
61
+ bind:this={dockEl}
62
+ class="dock {className}"
63
+ onmousemove={onMouseMove}
64
+ onmouseleave={onMouseLeave}
65
+ aria-label="Dock"
66
+ >
67
+ {#each items as item, i}
68
+ <button
69
+ type="button"
70
+ class="dock-item"
71
+ style="transform: scale({scaleForItem(i)});"
72
+ onclick={item.onclick}
73
+ aria-label={item.label}
74
+ title={item.label}
75
+ >
76
+ {#if item.iconSvg}
77
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
78
+ {@html item.iconSvg}
79
+ {/if}
80
+ </button>
81
+ {/each}
82
+ </nav>
83
+
84
+ <style>
85
+ .dock {
86
+ display: inline-flex;
87
+ align-items: flex-end;
88
+ gap: 8px;
89
+ padding: 10px 16px;
90
+ background: oklch(var(--b2, var(--b1)) / 0.8);
91
+ border: var(--jatui-border-w) solid oklch(var(--bc) / 0.1);
92
+ border-radius: var(--jatui-radius-box);
93
+ backdrop-filter: blur(12px);
94
+ }
95
+
96
+ .dock-item {
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ width: 56px;
101
+ height: 56px;
102
+ border-radius: var(--jatui-radius-box);
103
+ background: oklch(var(--b1));
104
+ border: var(--jatui-border-w) solid oklch(var(--bc) / 0.08);
105
+ cursor: pointer;
106
+ transform-origin: bottom center;
107
+ transition: transform var(--motion-fast) var(--motion-glide);
108
+ color: oklch(var(--bc));
109
+ }
110
+
111
+ .dock-item:hover,
112
+ .dock-item:focus-visible {
113
+ background: oklch(var(--b2, var(--b1)));
114
+ outline: 2px solid oklch(var(--p));
115
+ outline-offset: 2px;
116
+ }
117
+
118
+ @media (prefers-reduced-motion: reduce) {
119
+ .dock-item {
120
+ transition: none;
121
+ }
122
+ }
123
+ </style>
@@ -0,0 +1,17 @@
1
+ export interface DockItem {
2
+ label: string;
3
+ /** Inline SVG string for the icon */
4
+ iconSvg?: string;
5
+ onclick?: () => void;
6
+ }
7
+ interface Props {
8
+ items: DockItem[];
9
+ /** Max scale factor at pointer proximity */
10
+ magnification?: number;
11
+ /** Radius in px around pointer that triggers magnification */
12
+ radius?: number;
13
+ class?: string;
14
+ }
15
+ declare const Dock: import("svelte").Component<Props, {}, "">;
16
+ type Dock = ReturnType<typeof Dock>;
17
+ export default Dock;
@@ -0,0 +1,103 @@
1
+ <script lang="ts">
2
+ import { type Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ flipOn?: 'hover' | 'click';
6
+ class?: string;
7
+ front: Snippet;
8
+ back: Snippet;
9
+ }
10
+
11
+ let { flipOn = 'hover', class: className = '', front, back }: Props = $props();
12
+
13
+ let flipped = $state(false);
14
+
15
+ function handleMouseEnter() {
16
+ if (flipOn === 'hover') flipped = true;
17
+ }
18
+
19
+ function handleMouseLeave() {
20
+ if (flipOn === 'hover') flipped = false;
21
+ }
22
+
23
+ function handleClick() {
24
+ if (flipOn === 'click') flipped = !flipped;
25
+ }
26
+ </script>
27
+
28
+ <div
29
+ class="flip-card-outer {className}"
30
+ onmouseenter={handleMouseEnter}
31
+ onmouseleave={handleMouseLeave}
32
+ onclick={handleClick}
33
+ role={flipOn === 'click' ? 'button' : undefined}
34
+ tabindex={flipOn === 'click' ? 0 : undefined}
35
+ onkeydown={flipOn === 'click' ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); flipped = !flipped; } } : undefined}
36
+ >
37
+ <div class="flip-card-inner" class:flipped>
38
+ <div class="flip-card-face flip-card-front">
39
+ {@render front()}
40
+ </div>
41
+ <div class="flip-card-face flip-card-back">
42
+ {@render back()}
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <style>
48
+ .flip-card-outer {
49
+ perspective: 800px;
50
+ }
51
+
52
+ .flip-card-inner {
53
+ position: relative;
54
+ width: 100%;
55
+ height: 100%;
56
+ transform-style: preserve-3d;
57
+ transition: transform 0.6s var(--motion-spring, cubic-bezier(0.34, 1.56, 0.64, 1));
58
+ }
59
+
60
+ .flip-card-inner.flipped {
61
+ transform: rotateY(180deg);
62
+ }
63
+
64
+ .flip-card-face {
65
+ position: absolute;
66
+ inset: 0;
67
+ width: 100%;
68
+ height: 100%;
69
+ backface-visibility: hidden;
70
+ -webkit-backface-visibility: hidden;
71
+ }
72
+
73
+ .flip-card-front {
74
+ transform: rotateY(0deg);
75
+ }
76
+
77
+ .flip-card-back {
78
+ transform: rotateY(180deg);
79
+ }
80
+
81
+ @media (prefers-reduced-motion: reduce) {
82
+ .flip-card-inner {
83
+ transition: none;
84
+ }
85
+
86
+ .flip-card-inner.flipped {
87
+ transform: none;
88
+ }
89
+
90
+ .flip-card-inner.flipped .flip-card-front {
91
+ visibility: hidden;
92
+ }
93
+
94
+ .flip-card-inner.flipped .flip-card-back {
95
+ transform: none;
96
+ visibility: visible;
97
+ }
98
+
99
+ .flip-card-back {
100
+ visibility: hidden;
101
+ }
102
+ }
103
+ </style>
@@ -0,0 +1,10 @@
1
+ import { type Snippet } from 'svelte';
2
+ interface Props {
3
+ flipOn?: 'hover' | 'click';
4
+ class?: string;
5
+ front: Snippet;
6
+ back: Snippet;
7
+ }
8
+ declare const FlipCard: import("svelte").Component<Props, {}, "">;
9
+ type FlipCard = ReturnType<typeof FlipCard>;
10
+ export default FlipCard;
@@ -0,0 +1,83 @@
1
+ // after animista.net (Ana Travas), re-authored on jatui motion tokens
2
+ <script lang="ts">
3
+ import type { Snippet } from 'svelte';
4
+ import { onMount } from 'svelte';
5
+
6
+ type TriggerMode = 'mount' | 'intersect';
7
+
8
+ interface Props {
9
+ triggerMode?: TriggerMode;
10
+ durationMs?: number;
11
+ class?: string;
12
+ children: Snippet;
13
+ }
14
+
15
+ let {
16
+ triggerMode = 'mount',
17
+ durationMs = 800,
18
+ class: className = '',
19
+ children,
20
+ }: Props = $props();
21
+
22
+ let reducedMotion = $state(false);
23
+ let revealed = $state(false);
24
+ let el: HTMLDivElement | undefined = $state();
25
+
26
+ onMount(() => {
27
+ reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
28
+
29
+ if (reducedMotion) {
30
+ revealed = true;
31
+ return;
32
+ }
33
+
34
+ if (triggerMode === 'mount') {
35
+ requestAnimationFrame(() => {
36
+ revealed = true;
37
+ });
38
+ } else {
39
+ const observer = new IntersectionObserver(
40
+ (entries) => {
41
+ for (const entry of entries) {
42
+ if (entry.intersectionRatio >= 0.5) {
43
+ revealed = true;
44
+ observer.disconnect();
45
+ }
46
+ }
47
+ },
48
+ { threshold: 0.5 }
49
+ );
50
+
51
+ if (el) observer.observe(el);
52
+
53
+ return () => observer.disconnect();
54
+ }
55
+ });
56
+ </script>
57
+
58
+ <div
59
+ bind:this={el}
60
+ class="focus-reveal-root {className}"
61
+ style:filter={reducedMotion || revealed ? 'blur(0px)' : 'blur(8px)'}
62
+ style:opacity={reducedMotion || revealed ? '1' : '0.6'}
63
+ style:transition={reducedMotion
64
+ ? 'none'
65
+ : `filter ${durationMs}ms var(--motion-glide), opacity ${durationMs}ms var(--motion-glide)`}
66
+ >
67
+ {@render children()}
68
+ </div>
69
+
70
+ <style>
71
+ .focus-reveal-root {
72
+ will-change: filter, opacity;
73
+ }
74
+
75
+ @media (prefers-reduced-motion: reduce) {
76
+ .focus-reveal-root {
77
+ filter: none !important;
78
+ opacity: 1 !important;
79
+ transition: none !important;
80
+ will-change: auto;
81
+ }
82
+ }
83
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ type TriggerMode = 'mount' | 'intersect';
3
+ interface Props {
4
+ triggerMode?: TriggerMode;
5
+ durationMs?: number;
6
+ class?: string;
7
+ children: Snippet;
8
+ }
9
+ declare const FocusReveal: import("svelte").Component<Props, {}, "">;
10
+ type FocusReveal = ReturnType<typeof FocusReveal>;
11
+ export default FocusReveal;