react-marketing-ui 0.0.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 (318) hide show
  1. package/README.md +34 -0
  2. package/dist/accordion.d.cts +21 -0
  3. package/dist/accordion.d.ts +21 -0
  4. package/dist/accordion.d.ts.map +1 -0
  5. package/dist/accordion.js +230 -0
  6. package/dist/accordion.js.map +1 -0
  7. package/dist/accordion.mjs +4 -0
  8. package/dist/accordion.mjs.map +1 -0
  9. package/dist/action-swap.d.cts +23 -0
  10. package/dist/action-swap.d.ts +23 -0
  11. package/dist/action-swap.d.ts.map +1 -0
  12. package/dist/action-swap.js +81 -0
  13. package/dist/action-swap.js.map +1 -0
  14. package/dist/action-swap.mjs +5 -0
  15. package/dist/action-swap.mjs.map +1 -0
  16. package/dist/animated-beam.d.cts +24 -0
  17. package/dist/animated-beam.d.ts +24 -0
  18. package/dist/animated-beam.d.ts.map +1 -0
  19. package/dist/animated-beam.js +130 -0
  20. package/dist/animated-beam.js.map +1 -0
  21. package/dist/animated-beam.mjs +4 -0
  22. package/dist/animated-beam.mjs.map +1 -0
  23. package/dist/button.d.cts +55 -0
  24. package/dist/button.d.ts +55 -0
  25. package/dist/button.d.ts.map +1 -0
  26. package/dist/button.js +119 -0
  27. package/dist/button.js.map +1 -0
  28. package/dist/button.mjs +4 -0
  29. package/dist/button.mjs.map +1 -0
  30. package/dist/card.d.cts +20 -0
  31. package/dist/card.d.ts +20 -0
  32. package/dist/card.d.ts.map +1 -0
  33. package/dist/card.js +146 -0
  34. package/dist/card.js.map +1 -0
  35. package/dist/card.mjs +5 -0
  36. package/dist/card.mjs.map +1 -0
  37. package/dist/chunk-3SB6HUBA.mjs +31 -0
  38. package/dist/chunk-3SB6HUBA.mjs.map +1 -0
  39. package/dist/chunk-4PBVBXGY.mjs +84 -0
  40. package/dist/chunk-4PBVBXGY.mjs.map +1 -0
  41. package/dist/chunk-4VCFWVSJ.mjs +42 -0
  42. package/dist/chunk-4VCFWVSJ.mjs.map +1 -0
  43. package/dist/chunk-5QQAPGR2.mjs +109 -0
  44. package/dist/chunk-5QQAPGR2.mjs.map +1 -0
  45. package/dist/chunk-6MB4UKRX.mjs +19 -0
  46. package/dist/chunk-6MB4UKRX.mjs.map +1 -0
  47. package/dist/chunk-6T5LR34O.mjs +45 -0
  48. package/dist/chunk-6T5LR34O.mjs.map +1 -0
  49. package/dist/chunk-AYLCYDUM.mjs +275 -0
  50. package/dist/chunk-AYLCYDUM.mjs.map +1 -0
  51. package/dist/chunk-AYY7D2RJ.mjs +112 -0
  52. package/dist/chunk-AYY7D2RJ.mjs.map +1 -0
  53. package/dist/chunk-CTZEI2YS.mjs +324 -0
  54. package/dist/chunk-CTZEI2YS.mjs.map +1 -0
  55. package/dist/chunk-DA3I3Y5O.mjs +71 -0
  56. package/dist/chunk-DA3I3Y5O.mjs.map +1 -0
  57. package/dist/chunk-EYHT2EWJ.mjs +18 -0
  58. package/dist/chunk-EYHT2EWJ.mjs.map +1 -0
  59. package/dist/chunk-IHLGCS63.mjs +36 -0
  60. package/dist/chunk-IHLGCS63.mjs.map +1 -0
  61. package/dist/chunk-JVXJ6SFX.mjs +38 -0
  62. package/dist/chunk-JVXJ6SFX.mjs.map +1 -0
  63. package/dist/chunk-KBHYXLRG.mjs +13 -0
  64. package/dist/chunk-KBHYXLRG.mjs.map +1 -0
  65. package/dist/chunk-KMXUQYR3.mjs +74 -0
  66. package/dist/chunk-KMXUQYR3.mjs.map +1 -0
  67. package/dist/chunk-KNPOZ34H.mjs +88 -0
  68. package/dist/chunk-KNPOZ34H.mjs.map +1 -0
  69. package/dist/chunk-KRNWCJ4D.mjs +219 -0
  70. package/dist/chunk-KRNWCJ4D.mjs.map +1 -0
  71. package/dist/chunk-M5MESV26.mjs +124 -0
  72. package/dist/chunk-M5MESV26.mjs.map +1 -0
  73. package/dist/chunk-MJYBPJBA.mjs +115 -0
  74. package/dist/chunk-MJYBPJBA.mjs.map +1 -0
  75. package/dist/chunk-NNHPQ67J.mjs +10 -0
  76. package/dist/chunk-NNHPQ67J.mjs.map +1 -0
  77. package/dist/chunk-OCZFHVVD.mjs +71 -0
  78. package/dist/chunk-OCZFHVVD.mjs.map +1 -0
  79. package/dist/chunk-QAWSH567.mjs +19 -0
  80. package/dist/chunk-QAWSH567.mjs.map +1 -0
  81. package/dist/chunk-R2E2J42H.mjs +38 -0
  82. package/dist/chunk-R2E2J42H.mjs.map +1 -0
  83. package/dist/chunk-TPGLLSYV.mjs +65 -0
  84. package/dist/chunk-TPGLLSYV.mjs.map +1 -0
  85. package/dist/chunk-VJENJHXJ.mjs +72 -0
  86. package/dist/chunk-VJENJHXJ.mjs.map +1 -0
  87. package/dist/chunk-W22G7XHR.mjs +104 -0
  88. package/dist/chunk-W22G7XHR.mjs.map +1 -0
  89. package/dist/chunk-WDR5VPZJ.mjs +79 -0
  90. package/dist/chunk-WDR5VPZJ.mjs.map +1 -0
  91. package/dist/chunk-WLUGKBH7.mjs +37 -0
  92. package/dist/chunk-WLUGKBH7.mjs.map +1 -0
  93. package/dist/chunk-WYJYLDUA.mjs +84 -0
  94. package/dist/chunk-WYJYLDUA.mjs.map +1 -0
  95. package/dist/chunk-XZEDZVXT.mjs +38 -0
  96. package/dist/chunk-XZEDZVXT.mjs.map +1 -0
  97. package/dist/chunk-YKGP5JIL.mjs +328 -0
  98. package/dist/chunk-YKGP5JIL.mjs.map +1 -0
  99. package/dist/chunk-YM3U2OBE.mjs +99 -0
  100. package/dist/chunk-YM3U2OBE.mjs.map +1 -0
  101. package/dist/dia-text-reveal.css +18 -0
  102. package/dist/dia-text-reveal.css.map +1 -0
  103. package/dist/dia-text-reveal.d.cts +37 -0
  104. package/dist/dia-text-reveal.d.ts +37 -0
  105. package/dist/dia-text-reveal.d.ts.map +1 -0
  106. package/dist/dia-text-reveal.js +334 -0
  107. package/dist/dia-text-reveal.js.map +1 -0
  108. package/dist/dia-text-reveal.lines.d.cts +23 -0
  109. package/dist/dia-text-reveal.lines.d.ts +23 -0
  110. package/dist/dia-text-reveal.lines.d.ts.map +1 -0
  111. package/dist/dia-text-reveal.mjs +4 -0
  112. package/dist/dia-text-reveal.mjs.map +1 -0
  113. package/dist/dia-text-reveal.sweep.d.cts +49 -0
  114. package/dist/dia-text-reveal.sweep.d.ts +49 -0
  115. package/dist/dia-text-reveal.sweep.d.ts.map +1 -0
  116. package/dist/dia-text-reveal.utils.d.cts +3 -0
  117. package/dist/dia-text-reveal.utils.d.ts +3 -0
  118. package/dist/dia-text-reveal.utils.d.ts.map +1 -0
  119. package/dist/elevated-input.d.cts +11 -0
  120. package/dist/elevated-input.d.ts +11 -0
  121. package/dist/elevated-input.d.ts.map +1 -0
  122. package/dist/elevated-input.js +52 -0
  123. package/dist/elevated-input.js.map +1 -0
  124. package/dist/elevated-input.mjs +4 -0
  125. package/dist/elevated-input.mjs.map +1 -0
  126. package/dist/fade-in.d.cts +43 -0
  127. package/dist/fade-in.d.ts +43 -0
  128. package/dist/fade-in.d.ts.map +1 -0
  129. package/dist/fade-in.js +89 -0
  130. package/dist/fade-in.js.map +1 -0
  131. package/dist/fade-in.mjs +4 -0
  132. package/dist/fade-in.mjs.map +1 -0
  133. package/dist/feature-card.d.cts +23 -0
  134. package/dist/feature-card.d.ts +23 -0
  135. package/dist/feature-card.d.ts.map +1 -0
  136. package/dist/feature-card.js +38 -0
  137. package/dist/feature-card.js.map +1 -0
  138. package/dist/feature-card.mjs +4 -0
  139. package/dist/feature-card.mjs.map +1 -0
  140. package/dist/flowchart-parts.d.cts +61 -0
  141. package/dist/flowchart-parts.d.ts +61 -0
  142. package/dist/flowchart-parts.d.ts.map +1 -0
  143. package/dist/flowchart-parts.js +102 -0
  144. package/dist/flowchart-parts.js.map +1 -0
  145. package/dist/flowchart-parts.mjs +4 -0
  146. package/dist/flowchart-parts.mjs.map +1 -0
  147. package/dist/glossy-button.d.cts +21 -0
  148. package/dist/glossy-button.d.ts +21 -0
  149. package/dist/glossy-button.d.ts.map +1 -0
  150. package/dist/glossy-button.js +87 -0
  151. package/dist/glossy-button.js.map +1 -0
  152. package/dist/glossy-button.mjs +4 -0
  153. package/dist/glossy-button.mjs.map +1 -0
  154. package/dist/grainient.css +9 -0
  155. package/dist/grainient.css.map +1 -0
  156. package/dist/grainient.d.cts +5 -0
  157. package/dist/grainient.d.ts +5 -0
  158. package/dist/grainient.d.ts.map +1 -0
  159. package/dist/grainient.js +330 -0
  160. package/dist/grainient.js.map +1 -0
  161. package/dist/grainient.mjs +4 -0
  162. package/dist/grainient.mjs.map +1 -0
  163. package/dist/grainient.shader.d.cts +9 -0
  164. package/dist/grainient.shader.d.ts +9 -0
  165. package/dist/grainient.shader.d.ts.map +1 -0
  166. package/dist/grainient.types.d.cts +30 -0
  167. package/dist/grainient.types.d.ts +30 -0
  168. package/dist/grainient.types.d.ts.map +1 -0
  169. package/dist/highlighter.d.cts +47 -0
  170. package/dist/highlighter.d.ts +47 -0
  171. package/dist/highlighter.d.ts.map +1 -0
  172. package/dist/highlighter.js +125 -0
  173. package/dist/highlighter.js.map +1 -0
  174. package/dist/highlighter.mjs +5 -0
  175. package/dist/highlighter.mjs.map +1 -0
  176. package/dist/index.css +27 -0
  177. package/dist/index.css.map +1 -0
  178. package/dist/index.d.cts +56 -0
  179. package/dist/index.d.ts +56 -0
  180. package/dist/index.d.ts.map +1 -0
  181. package/dist/index.js +2723 -0
  182. package/dist/index.js.map +1 -0
  183. package/dist/index.mjs +34 -0
  184. package/dist/index.mjs.map +1 -0
  185. package/dist/input.d.cts +15 -0
  186. package/dist/input.d.ts +15 -0
  187. package/dist/input.d.ts.map +1 -0
  188. package/dist/input.js +110 -0
  189. package/dist/input.js.map +1 -0
  190. package/dist/input.mjs +4 -0
  191. package/dist/input.mjs.map +1 -0
  192. package/dist/lib/breakpoints.d.cts +36 -0
  193. package/dist/lib/breakpoints.d.ts +36 -0
  194. package/dist/lib/breakpoints.d.ts.map +1 -0
  195. package/dist/lib/breakpoints.js +41 -0
  196. package/dist/lib/breakpoints.js.map +1 -0
  197. package/dist/lib/breakpoints.mjs +3 -0
  198. package/dist/lib/breakpoints.mjs.map +1 -0
  199. package/dist/lib/cn.js +24 -0
  200. package/dist/lib/cn.js.map +1 -0
  201. package/dist/lib/cn.mjs +3 -0
  202. package/dist/lib/cn.mjs.map +1 -0
  203. package/dist/lib/ease.d.cts +19 -0
  204. package/dist/lib/ease.d.ts +19 -0
  205. package/dist/lib/ease.d.ts.map +1 -0
  206. package/dist/lib/ease.js +16 -0
  207. package/dist/lib/ease.js.map +1 -0
  208. package/dist/lib/ease.mjs +3 -0
  209. package/dist/lib/ease.mjs.map +1 -0
  210. package/dist/lib/surface.d.cts +14 -0
  211. package/dist/lib/surface.d.ts +14 -0
  212. package/dist/lib/surface.d.ts.map +1 -0
  213. package/dist/lib/surface.js +40 -0
  214. package/dist/lib/surface.js.map +1 -0
  215. package/dist/lib/surface.mjs +3 -0
  216. package/dist/lib/surface.mjs.map +1 -0
  217. package/dist/lib/use-media-query.d.cts +9 -0
  218. package/dist/lib/use-media-query.d.ts +9 -0
  219. package/dist/lib/use-media-query.d.ts.map +1 -0
  220. package/dist/lib/use-media-query.js +19 -0
  221. package/dist/lib/use-media-query.js.map +1 -0
  222. package/dist/lib/use-media-query.mjs +3 -0
  223. package/dist/lib/use-media-query.mjs.map +1 -0
  224. package/dist/lib/utils.d.cts +25 -0
  225. package/dist/lib/utils.d.ts +25 -0
  226. package/dist/lib/utils.d.ts.map +1 -0
  227. package/dist/liquid-metal.d.cts +24 -0
  228. package/dist/liquid-metal.d.ts +24 -0
  229. package/dist/liquid-metal.d.ts.map +1 -0
  230. package/dist/liquid-metal.js +43 -0
  231. package/dist/liquid-metal.js.map +1 -0
  232. package/dist/liquid-metal.mjs +4 -0
  233. package/dist/liquid-metal.mjs.map +1 -0
  234. package/dist/navigation-menu-icons.d.cts +5 -0
  235. package/dist/navigation-menu-icons.d.ts +5 -0
  236. package/dist/navigation-menu-icons.d.ts.map +1 -0
  237. package/dist/navigation-menu-icons.js +42 -0
  238. package/dist/navigation-menu-icons.js.map +1 -0
  239. package/dist/navigation-menu-icons.mjs +3 -0
  240. package/dist/navigation-menu-icons.mjs.map +1 -0
  241. package/dist/navigation-menu-styles.d.cts +45 -0
  242. package/dist/navigation-menu-styles.d.ts +45 -0
  243. package/dist/navigation-menu-styles.d.ts.map +1 -0
  244. package/dist/navigation-menu-styles.js +16 -0
  245. package/dist/navigation-menu-styles.js.map +1 -0
  246. package/dist/navigation-menu-styles.mjs +3 -0
  247. package/dist/navigation-menu-styles.mjs.map +1 -0
  248. package/dist/navigation-menu.d.cts +18 -0
  249. package/dist/navigation-menu.d.ts +18 -0
  250. package/dist/navigation-menu.d.ts.map +1 -0
  251. package/dist/navigation-menu.js +367 -0
  252. package/dist/navigation-menu.js.map +1 -0
  253. package/dist/navigation-menu.mjs +7 -0
  254. package/dist/navigation-menu.mjs.map +1 -0
  255. package/dist/password-input.d.cts +6 -0
  256. package/dist/password-input.d.ts +6 -0
  257. package/dist/password-input.d.ts.map +1 -0
  258. package/dist/password-input.js +174 -0
  259. package/dist/password-input.js.map +1 -0
  260. package/dist/password-input.mjs +5 -0
  261. package/dist/password-input.mjs.map +1 -0
  262. package/dist/rainbow-button.d.cts +16 -0
  263. package/dist/rainbow-button.d.ts +16 -0
  264. package/dist/rainbow-button.d.ts.map +1 -0
  265. package/dist/rainbow-button.js +49 -0
  266. package/dist/rainbow-button.js.map +1 -0
  267. package/dist/rainbow-button.mjs +4 -0
  268. package/dist/rainbow-button.mjs.map +1 -0
  269. package/dist/range-slider.d.cts +20 -0
  270. package/dist/range-slider.d.ts +20 -0
  271. package/dist/range-slider.d.ts.map +1 -0
  272. package/dist/range-slider.js +85 -0
  273. package/dist/range-slider.js.map +1 -0
  274. package/dist/range-slider.mjs +3 -0
  275. package/dist/range-slider.mjs.map +1 -0
  276. package/dist/rolling-badge.d.cts +11 -0
  277. package/dist/rolling-badge.d.ts +11 -0
  278. package/dist/rolling-badge.d.ts.map +1 -0
  279. package/dist/rolling-badge.js +71 -0
  280. package/dist/rolling-badge.js.map +1 -0
  281. package/dist/rolling-badge.mjs +4 -0
  282. package/dist/rolling-badge.mjs.map +1 -0
  283. package/dist/savings-badge.d.cts +10 -0
  284. package/dist/savings-badge.d.ts +10 -0
  285. package/dist/savings-badge.d.ts.map +1 -0
  286. package/dist/savings-badge.js +26 -0
  287. package/dist/savings-badge.js.map +1 -0
  288. package/dist/savings-badge.mjs +4 -0
  289. package/dist/savings-badge.mjs.map +1 -0
  290. package/dist/shine-border.d.cts +15 -0
  291. package/dist/shine-border.d.ts +15 -0
  292. package/dist/shine-border.d.ts.map +1 -0
  293. package/dist/shine-border.js +47 -0
  294. package/dist/shine-border.js.map +1 -0
  295. package/dist/shine-border.mjs +4 -0
  296. package/dist/shine-border.mjs.map +1 -0
  297. package/dist/sparkles-text.d.cts +22 -0
  298. package/dist/sparkles-text.d.ts +22 -0
  299. package/dist/sparkles-text.d.ts.map +1 -0
  300. package/dist/sparkles-text.js +87 -0
  301. package/dist/sparkles-text.js.map +1 -0
  302. package/dist/sparkles-text.mjs +5 -0
  303. package/dist/sparkles-text.mjs.map +1 -0
  304. package/dist/tabs.d.cts +16 -0
  305. package/dist/tabs.d.ts +16 -0
  306. package/dist/tabs.d.ts.map +1 -0
  307. package/dist/tabs.js +121 -0
  308. package/dist/tabs.js.map +1 -0
  309. package/dist/tabs.mjs +4 -0
  310. package/dist/tabs.mjs.map +1 -0
  311. package/dist/theme-toggle.d.cts +10 -0
  312. package/dist/theme-toggle.d.ts +10 -0
  313. package/dist/theme-toggle.d.ts.map +1 -0
  314. package/dist/theme-toggle.js +251 -0
  315. package/dist/theme-toggle.js.map +1 -0
  316. package/dist/theme-toggle.mjs +7 -0
  317. package/dist/theme-toggle.mjs.map +1 -0
  318. package/package.json +428 -0
@@ -0,0 +1,22 @@
1
+ import { type ReactNode } from 'react';
2
+ export type SparklesTextProps = {
3
+ /** The text to sparkle. Rendered inline, so it inherits the surrounding type. */
4
+ children: ReactNode;
5
+ className?: string;
6
+ /** How many sparkles orbit the text at once. @default 10 */
7
+ sparklesCount?: number;
8
+ /** The two colors sparkles alternate between. */
9
+ colors?: {
10
+ first: string;
11
+ second: string;
12
+ };
13
+ };
14
+ /**
15
+ * Wraps a word or phrase in continuously respawning sparkles.
16
+ *
17
+ * Sparkles are generated on the client only — the exported HTML ships the bare
18
+ * text, so nothing about the layout depends on JS having run. Readers who ask
19
+ * for reduced motion get that same bare text.
20
+ */
21
+ export declare function SparklesText({ children, className, sparklesCount, colors }: SparklesTextProps): import("react").JSX.Element;
22
+ //# sourceMappingURL=sparkles-text.d.ts.map
@@ -0,0 +1,22 @@
1
+ import { type ReactNode } from 'react';
2
+ export type SparklesTextProps = {
3
+ /** The text to sparkle. Rendered inline, so it inherits the surrounding type. */
4
+ children: ReactNode;
5
+ className?: string;
6
+ /** How many sparkles orbit the text at once. @default 10 */
7
+ sparklesCount?: number;
8
+ /** The two colors sparkles alternate between. */
9
+ colors?: {
10
+ first: string;
11
+ second: string;
12
+ };
13
+ };
14
+ /**
15
+ * Wraps a word or phrase in continuously respawning sparkles.
16
+ *
17
+ * Sparkles are generated on the client only — the exported HTML ships the bare
18
+ * text, so nothing about the layout depends on JS having run. Readers who ask
19
+ * for reduced motion get that same bare text.
20
+ */
21
+ export declare function SparklesText({ children, className, sparklesCount, colors }: SparklesTextProps): import("react").JSX.Element;
22
+ //# sourceMappingURL=sparkles-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sparkles-text.d.ts","sourceRoot":"","sources":["../../src/components/sparkles-text.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAkD5D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,iFAAiF;IACjF,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAkB,EAAE,MAAuB,EAAE,EAAE,iBAAiB,+BA0CnH"}
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ var framerMotion = require('framer-motion');
4
+ var react = require('react');
5
+ var clsx = require('clsx');
6
+ var tailwindMerge = require('tailwind-merge');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ function useMediaQuery(query, defaultState = false) {
10
+ const [matches, setMatches] = react.useState(defaultState);
11
+ react.useEffect(() => {
12
+ const mql = globalThis.matchMedia(query);
13
+ setMatches(mql.matches);
14
+ const onChange = (e) => setMatches(e.matches);
15
+ mql.addEventListener("change", onChange);
16
+ return () => mql.removeEventListener("change", onChange);
17
+ }, [query]);
18
+ return matches;
19
+ }
20
+ function cn(...inputs) {
21
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
22
+ }
23
+ var STAR_PATH = "M9.82531 0.843845C10.0553 0.215178 10.9446 0.215178 11.1746 0.843845L11.8618 2.72026C12.4006 4.19229 12.3916 6.39157 13.5 7.5C14.6084 8.60843 16.8077 8.59935 18.2797 9.13822L20.1561 9.82534C20.7858 10.0553 20.7858 10.9447 20.1561 11.1747L18.2797 11.8618C16.8077 12.4007 14.6084 12.3916 13.5 13.5C12.3916 14.6084 12.4006 16.8077 11.8618 18.2798L11.1746 20.1562C10.9446 20.7858 10.0553 20.7858 9.82531 20.1562L9.13819 18.2798C8.59932 16.8077 8.60843 14.6084 7.5 13.5C6.39157 12.3916 4.19225 12.4007 2.72023 11.8618L0.843814 11.1747C0.215148 10.9447 0.215148 10.0553 0.843814 9.82534L2.72023 9.13822C4.19225 8.59935 6.39157 8.60843 7.5 7.5C8.60843 6.39157 8.59932 4.19229 9.13819 2.72026L9.82531 0.843845Z";
24
+ var DEFAULT_COLORS = {
25
+ first: "var(--color-accent-brand)",
26
+ second: "var(--color-accent-brand-soft)"
27
+ };
28
+ function SparkleStar({ x, y, color, delay, scale }) {
29
+ return /* @__PURE__ */ jsxRuntime.jsx(
30
+ framerMotion.motion.svg,
31
+ {
32
+ className: "pointer-events-none absolute z-20",
33
+ initial: { opacity: 0, left: x, top: y },
34
+ animate: {
35
+ opacity: [0, 1, 0],
36
+ scale: [0, scale, 0],
37
+ rotate: [75, 120, 150]
38
+ },
39
+ transition: {
40
+ duration: 0.8,
41
+ repeat: Number.POSITIVE_INFINITY,
42
+ delay
43
+ },
44
+ width: "21",
45
+ height: "21",
46
+ viewBox: "0 0 21 21",
47
+ "aria-hidden": true,
48
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: STAR_PATH, fill: color })
49
+ }
50
+ );
51
+ }
52
+ function SparklesText({ children, className, sparklesCount = 10, colors = DEFAULT_COLORS }) {
53
+ const reducedMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
54
+ const [sparkles, setSparkles] = react.useState([]);
55
+ const { first, second } = colors;
56
+ react.useEffect(() => {
57
+ if (reducedMotion) {
58
+ setSparkles([]);
59
+ return;
60
+ }
61
+ const spawn = () => ({
62
+ x: `${Math.random() * 100}%`,
63
+ y: `${Math.random() * 100}%`,
64
+ color: Math.random() > 0.5 ? first : second,
65
+ delay: Math.random() * 2,
66
+ scale: Math.random() + 0.3,
67
+ expiresAt: performance.now() + (Math.random() * 10 + 5) * 1e3
68
+ });
69
+ setSparkles(Array.from({ length: sparklesCount }, spawn));
70
+ const interval = setInterval(() => {
71
+ setSparkles((current) => {
72
+ const now = performance.now();
73
+ if (!current.some((sparkle) => sparkle.expiresAt <= now)) return current;
74
+ return current.map((sparkle) => sparkle.expiresAt <= now ? spawn() : sparkle);
75
+ });
76
+ }, 500);
77
+ return () => clearInterval(interval);
78
+ }, [first, second, sparklesCount, reducedMotion]);
79
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("relative inline-block", className), children: [
80
+ sparkles.map((sparkle) => /* @__PURE__ */ jsxRuntime.jsx(SparkleStar, { ...sparkle }, `${sparkle.x}-${sparkle.y}-${sparkle.expiresAt}`)),
81
+ children
82
+ ] });
83
+ }
84
+
85
+ exports.SparklesText = SparklesText;
86
+ //# sourceMappingURL=sparkles-text.js.map
87
+ //# sourceMappingURL=sparkles-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/use-media-query.ts","../src/lib/utils.ts","../src/components/sparkles-text.tsx"],"names":["useState","useEffect","twMerge","clsx","jsx","motion"],"mappings":"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","file":"sparkles-text.js","sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\n/**\n * Tracks a CSS media query, returning `true` when it matches.\n *\n * The default return before hydration is `false`; pass `defaultState`\n * to set a different initial value (e.g. `true` for dark-mode queries\n * whose SSR default is dark).\n */\nexport function useMediaQuery(query: string, defaultState = false): boolean {\n const [matches, setMatches] = useState(defaultState);\n\n // biome-ignore lint/plugin: subscribes to a matchMedia listener — an imperative side effect with no derived-state equivalent.\n useEffect(() => {\n const mql = globalThis.matchMedia(query);\n setMatches(mql.matches);\n\n const onChange = (e: MediaQueryListEvent) => setMatches(e.matches);\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }, [query]);\n\n return matches;\n}\n","import { type ClassValue, clsx } from 'clsx';\nimport type { CSSProperties } from 'react';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\n/**\n * A style object that may carry CSS custom properties (CSS variables)\n * such as `--duration`. React's `CSSProperties` intentionally drops\n * its index signature, so custom properties are not accepted without help.\n */\nexport type CSSPropertiesWithVars = CSSProperties & Record<`--${string}`, string | number>;\n\n/**\n * Builds a {@link CSSProperties} value that also allows CSS custom properties,\n * so `style` props can be written without an `as React.CSSProperties` cast while\n * still type-checking both the known style keys and the custom-property values.\n *\n * @example\n * <div style={validCssVars({ '--duration': '0.35s', color: 'red' })} />\n */\nexport function validCssVars(style: CSSPropertiesWithVars): CSSProperties {\n return style;\n}\n\n/**\n * Narrows a union to the member(s) that declare `key` — a type-guard\n * replacement for the `in` operator's discriminated-union narrowing.\n */\nexport function hasKey<K extends PropertyKey>(key: K, obj: unknown): obj is Record<K, unknown> {\n return obj !== null && typeof obj === 'object' && Object.hasOwn(obj, key);\n}\n\nexport function noop(): void {\n // intentional no-op\n}\n","'use client';\n\nimport { motion } from 'framer-motion';\nimport { type ReactNode, useEffect, useState } from 'react';\nimport { useMediaQuery } from '../lib/use-media-query.js';\nimport { cn } from '../lib/utils.js';\n\n/** Four-pointed star, drawn once and reused by every sparkle. */\nconst STAR_PATH =\n 'M9.82531 0.843845C10.0553 0.215178 10.9446 0.215178 11.1746 0.843845L11.8618 2.72026C12.4006 4.19229 12.3916 6.39157 13.5 7.5C14.6084 8.60843 16.8077 8.59935 18.2797 9.13822L20.1561 9.82534C20.7858 10.0553 20.7858 10.9447 20.1561 11.1747L18.2797 11.8618C16.8077 12.4007 14.6084 12.3916 13.5 13.5C12.3916 14.6084 12.4006 16.8077 11.8618 18.2798L11.1746 20.1562C10.9446 20.7858 10.0553 20.7858 9.82531 20.1562L9.13819 18.2798C8.59932 16.8077 8.60843 14.6084 7.5 13.5C6.39157 12.3916 4.19225 12.4007 2.72023 11.8618L0.843814 11.1747C0.215148 10.9447 0.215148 10.0553 0.843814 9.82534L2.72023 9.13822C4.19225 8.59935 6.39157 8.60843 7.5 7.5C8.60843 6.39157 8.59932 4.19229 9.13819 2.72026L9.82531 0.843845Z';\n\nconst DEFAULT_COLORS = {\n first: 'var(--color-accent-brand)',\n second: 'var(--color-accent-brand-soft)',\n};\n\ntype Sparkle = {\n /** Position within the text's box, as percentages. */\n x: string;\n y: string;\n color: string;\n /** Offsets the twinkle so the group never pulses in unison. */\n delay: number;\n scale: number;\n /** `performance.now()` timestamp after which this sparkle is replaced by a fresh one. */\n expiresAt: number;\n};\n\nfunction SparkleStar({ x, y, color, delay, scale }: Omit<Sparkle, 'expiresAt'>) {\n return (\n <motion.svg\n className=\"pointer-events-none absolute z-20\"\n initial={{ opacity: 0, left: x, top: y }}\n animate={{\n opacity: [0, 1, 0],\n scale: [0, scale, 0],\n rotate: [75, 120, 150],\n }}\n transition={{\n duration: 0.8,\n repeat: Number.POSITIVE_INFINITY,\n delay,\n }}\n width=\"21\"\n height=\"21\"\n viewBox=\"0 0 21 21\"\n aria-hidden={true}\n >\n <path d={STAR_PATH} fill={color} />\n </motion.svg>\n );\n}\n\nexport type SparklesTextProps = {\n /** The text to sparkle. Rendered inline, so it inherits the surrounding type. */\n children: ReactNode;\n className?: string;\n /** How many sparkles orbit the text at once. @default 10 */\n sparklesCount?: number;\n /** The two colors sparkles alternate between. */\n colors?: { first: string; second: string };\n};\n\n/**\n * Wraps a word or phrase in continuously respawning sparkles.\n *\n * Sparkles are generated on the client only — the exported HTML ships the bare\n * text, so nothing about the layout depends on JS having run. Readers who ask\n * for reduced motion get that same bare text.\n */\nexport function SparklesText({ children, className, sparklesCount = 10, colors = DEFAULT_COLORS }: SparklesTextProps) {\n const reducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');\n const [sparkles, setSparkles] = useState<Sparkle[]>([]);\n const { first, second } = colors;\n\n // biome-ignore lint/plugin: spawns and retires sparkles on a timer — an imperative animation loop with no derived-state equivalent.\n useEffect(() => {\n if (reducedMotion) {\n setSparkles([]);\n return;\n }\n\n const spawn = (): Sparkle => ({\n x: `${Math.random() * 100}%`,\n y: `${Math.random() * 100}%`,\n color: Math.random() > 0.5 ? first : second,\n delay: Math.random() * 2,\n scale: Math.random() + 0.3,\n expiresAt: performance.now() + (Math.random() * 10 + 5) * 1000,\n });\n\n setSparkles(Array.from({ length: sparklesCount }, spawn));\n\n const interval = setInterval(() => {\n setSparkles((current) => {\n const now = performance.now();\n if (!current.some((sparkle) => sparkle.expiresAt <= now)) return current;\n return current.map((sparkle) => (sparkle.expiresAt <= now ? spawn() : sparkle));\n });\n }, 500);\n\n return () => clearInterval(interval);\n }, [first, second, sparklesCount, reducedMotion]);\n\n return (\n <span className={cn('relative inline-block', className)}>\n {sparkles.map((sparkle) => (\n <SparkleStar key={`${sparkle.x}-${sparkle.y}-${sparkle.expiresAt}`} {...sparkle} />\n ))}\n {children}\n </span>\n );\n}\n"]}
@@ -0,0 +1,5 @@
1
+ export { SparklesText } from './chunk-OCZFHVVD.mjs';
2
+ import './chunk-EYHT2EWJ.mjs';
3
+ import './chunk-QAWSH567.mjs';
4
+ //# sourceMappingURL=sparkles-text.mjs.map
5
+ //# sourceMappingURL=sparkles-text.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"sparkles-text.mjs"}
@@ -0,0 +1,16 @@
1
+ /** One segment. `id` is the value handed back to `onChange`. */
2
+ export type Tab<T extends string = string> = {
3
+ id: T;
4
+ label: string;
5
+ badge?: string;
6
+ };
7
+ export type TabsProps<T extends string> = {
8
+ tabs: readonly Tab<T>[];
9
+ value: T;
10
+ onChange: (v: T) => void;
11
+ /** Accessible name for the group, e.g. "Billing period". */
12
+ label: string;
13
+ className?: string;
14
+ };
15
+ export declare function Tabs<T extends string>({ tabs, value, onChange, label, className }: TabsProps<T>): import("react").JSX.Element;
16
+ //# sourceMappingURL=tabs.d.ts.map
package/dist/tabs.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /** One segment. `id` is the value handed back to `onChange`. */
2
+ export type Tab<T extends string = string> = {
3
+ id: T;
4
+ label: string;
5
+ badge?: string;
6
+ };
7
+ export type TabsProps<T extends string> = {
8
+ tabs: readonly Tab<T>[];
9
+ value: T;
10
+ onChange: (v: T) => void;
11
+ /** Accessible name for the group, e.g. "Billing period". */
12
+ label: string;
13
+ className?: string;
14
+ };
15
+ export declare function Tabs<T extends string>({ tabs, value, onChange, label, className }: TabsProps<T>): import("react").JSX.Element;
16
+ //# sourceMappingURL=tabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/components/tabs.tsx"],"names":[],"mappings":"AAkBA,gEAAgE;AAChE,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IAC3C,EAAE,EAAE,CAAC,CAAC;IACN,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4BF,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI;IACxC,IAAI,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACxB,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;IACzB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,+BAwG/F"}
package/dist/tabs.js ADDED
@@ -0,0 +1,121 @@
1
+ 'use strict';
2
+
3
+ var framerMotion = require('framer-motion');
4
+ var react = require('react');
5
+ var clsx = require('clsx');
6
+ var tailwindMerge = require('tailwind-merge');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ function cn(...inputs) {
10
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
11
+ }
12
+ var PULL = 0.035;
13
+ function pillTarget(activeIdx, hoverIdx, rects, pull) {
14
+ const active = rects[activeIdx];
15
+ if (!active) throw new Error(`No rect at index ${activeIdx}`);
16
+ if (hoverIdx === null || hoverIdx === activeIdx) return active;
17
+ const hover = rects[hoverIdx];
18
+ if (!hover) return active;
19
+ const activeRight = active.left + active.width;
20
+ const hoverRight = hover.left + hover.width;
21
+ const left = Math.min(active.left, active.left + (hover.left - active.left) * pull);
22
+ const right = Math.max(activeRight, activeRight + (hoverRight - activeRight) * pull);
23
+ return { left, width: right - left };
24
+ }
25
+ var labelBox = "flex items-center gap-2.5 px-5 py-1.5 font-semibold text-[13px]";
26
+ var badgeBox = "font-bold text-(--color-accent-brand-soft) text-[11px] whitespace-nowrap";
27
+ function Tabs({ tabs, value, onChange, label, className }) {
28
+ const reduced = framerMotion.useReducedMotion();
29
+ const pull = reduced ? 0 : PULL;
30
+ const trackRef = react.useRef(null);
31
+ const [rects, setRects] = react.useState([]);
32
+ const [trackWidth, setTrackWidth] = react.useState(0);
33
+ const [hovered, setHovered] = react.useState(null);
34
+ const [ready, setReady] = react.useState(false);
35
+ const foundIdx = tabs.findIndex((t) => t.id === value);
36
+ const activeIdx = foundIdx === -1 ? 0 : foundIdx;
37
+ const springConfig = reduced ? { stiffness: 700, damping: 60 } : { stiffness: 420, damping: 34, mass: 0.7 };
38
+ const pillLeft = framerMotion.useSpring(0, springConfig);
39
+ const pillWidth = framerMotion.useSpring(0, springConfig);
40
+ const innerX = framerMotion.useTransform(pillLeft, (v) => -v);
41
+ const measure = react.useCallback(() => {
42
+ const track = trackRef.current;
43
+ if (!track) return;
44
+ setTrackWidth(track.offsetWidth);
45
+ const btns = track.querySelectorAll("button[data-index]");
46
+ setRects(
47
+ Array.from(btns, (el) => ({
48
+ left: el.offsetLeft,
49
+ width: el.offsetWidth
50
+ }))
51
+ );
52
+ }, []);
53
+ react.useEffect(() => {
54
+ measure();
55
+ const track = trackRef.current;
56
+ if (!track) return;
57
+ const ro = new ResizeObserver(measure);
58
+ ro.observe(track);
59
+ return () => ro.disconnect();
60
+ }, [measure]);
61
+ react.useEffect(() => {
62
+ if (rects.length !== tabs.length) return;
63
+ const target = pillTarget(activeIdx, hovered, rects, pull);
64
+ if (ready) {
65
+ pillLeft.set(target.left);
66
+ pillWidth.set(target.width);
67
+ } else {
68
+ pillLeft.jump(target.left);
69
+ pillWidth.jump(target.width);
70
+ setReady(true);
71
+ }
72
+ }, [rects, tabs.length, activeIdx, hovered, pull, ready, pillLeft, pillWidth]);
73
+ const handleClick = react.useCallback(
74
+ (e) => onChange(tabs[Number(e.currentTarget.dataset.index)].id),
75
+ [onChange, tabs]
76
+ );
77
+ const handleEnter = react.useCallback((e) => setHovered(Number(e.currentTarget.dataset.index)), []);
78
+ const handleLeave = react.useCallback(() => setHovered(null), []);
79
+ return /* @__PURE__ */ jsxRuntime.jsx(
80
+ "fieldset",
81
+ {
82
+ "aria-label": label,
83
+ className: cn("inline-flex items-center rounded-full border border-(--color-border) bg-(--color-surface) p-1", className),
84
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: trackRef, className: "relative inline-flex items-center", children: [
85
+ tabs.map((tab, i) => /* @__PURE__ */ jsxRuntime.jsxs(
86
+ "button",
87
+ {
88
+ type: "button",
89
+ "data-index": i,
90
+ onClick: handleClick,
91
+ onMouseEnter: handleEnter,
92
+ onMouseLeave: handleLeave,
93
+ "aria-pressed": value === tab.id,
94
+ className: cn(labelBox, "text-(--color-muted-foreground) hover:text-(--color-foreground)"),
95
+ children: [
96
+ tab.label,
97
+ tab.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: badgeBox, children: tab.badge }) : null
98
+ ]
99
+ },
100
+ tab.id
101
+ )),
102
+ /* @__PURE__ */ jsxRuntime.jsx(
103
+ framerMotion.motion.div,
104
+ {
105
+ "aria-hidden": true,
106
+ className: "pointer-events-none absolute inset-y-0 select-none overflow-hidden rounded-full bg-(--color-foreground)",
107
+ style: { left: pillLeft, width: pillWidth, opacity: ready ? 1 : 0 },
108
+ children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.motion.div, { className: "absolute inset-y-0 left-0 flex items-center", style: { width: trackWidth, x: innerX }, children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn(labelBox, "text-(--color-surface)"), children: [
109
+ tab.label,
110
+ tab.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: badgeBox, children: tab.badge }) : null
111
+ ] }, tab.id)) })
112
+ }
113
+ )
114
+ ] })
115
+ }
116
+ );
117
+ }
118
+
119
+ exports.Tabs = Tabs;
120
+ //# sourceMappingURL=tabs.js.map
121
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/components/tabs.tsx"],"names":["twMerge","clsx","useReducedMotion","useRef","useState","useSpring","useTransform","useCallback","useEffect","jsx","jsxs","motion"],"mappings":"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","file":"tabs.js","sourcesContent":["import { type ClassValue, clsx } from 'clsx';\nimport type { CSSProperties } from 'react';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\n/**\n * A style object that may carry CSS custom properties (CSS variables)\n * such as `--duration`. React's `CSSProperties` intentionally drops\n * its index signature, so custom properties are not accepted without help.\n */\nexport type CSSPropertiesWithVars = CSSProperties & Record<`--${string}`, string | number>;\n\n/**\n * Builds a {@link CSSProperties} value that also allows CSS custom properties,\n * so `style` props can be written without an `as React.CSSProperties` cast while\n * still type-checking both the known style keys and the custom-property values.\n *\n * @example\n * <div style={validCssVars({ '--duration': '0.35s', color: 'red' })} />\n */\nexport function validCssVars(style: CSSPropertiesWithVars): CSSProperties {\n return style;\n}\n\n/**\n * Narrows a union to the member(s) that declare `key` — a type-guard\n * replacement for the `in` operator's discriminated-union narrowing.\n */\nexport function hasKey<K extends PropertyKey>(key: K, obj: unknown): obj is Record<K, unknown> {\n return obj !== null && typeof obj === 'object' && Object.hasOwn(obj, key);\n}\n\nexport function noop(): void {\n // intentional no-op\n}\n","'use client';\n\nimport { motion, useReducedMotion, useSpring, useTransform } from 'framer-motion';\nimport { type MouseEvent, useCallback, useEffect, useRef, useState } from 'react';\nimport { cn } from '../lib/utils.js';\n\n/* ── Tabs ───────────────────────────────────────────────────────────────\n * A segmented control whose active pill slides between tabs and, while an\n * inactive tab is hovered, stretches (\"pulls\") toward the cursor for a\n * magnetic feel. A clipped, inverted duplicate of the labels stays glued to\n * the base labels underneath, so text flips colour exactly as the pill\n * passes over it — including the partial reveal during the pull.\n *\n * Single-select only: it switches one value, so the buttons carry\n * `aria-pressed` rather than the tab/tabpanel roles, which would promise\n * associated panels and arrow-key navigation this control doesn't provide.\n */\n\n/** One segment. `id` is the value handed back to `onChange`. */\nexport type Tab<T extends string = string> = {\n id: T;\n label: string;\n badge?: string;\n};\n\n// How far the pill reaches toward the hovered tab (fraction of the gap).\nconst PULL = 0.035;\n\ntype Rect = { left: number; width: number };\n\n// Target pill rect: the active tab at rest, or stretched toward a hovered\n// neighbour so its leading edge reaches partway across the gap.\nfunction pillTarget(activeIdx: number, hoverIdx: number | null, rects: Rect[], pull: number): Rect {\n const active = rects[activeIdx];\n if (!active) throw new Error(`No rect at index ${activeIdx}`);\n if (hoverIdx === null || hoverIdx === activeIdx) return active;\n\n const hover = rects[hoverIdx];\n if (!hover) return active;\n const activeRight = active.left + active.width;\n const hoverRight = hover.left + hover.width;\n\n const left = Math.min(active.left, active.left + (hover.left - active.left) * pull);\n const right = Math.max(activeRight, activeRight + (hoverRight - activeRight) * pull);\n return { left, width: right - left };\n}\n\n// Shared box so the base labels and the inverted duplicate measure identically.\nconst labelBox = 'flex items-center gap-2.5 px-5 py-1.5 font-semibold text-[13px]';\nconst badgeBox = 'font-bold text-(--color-accent-brand-soft) text-[11px] whitespace-nowrap';\n\nexport type TabsProps<T extends string> = {\n tabs: readonly Tab<T>[];\n value: T;\n onChange: (v: T) => void;\n /** Accessible name for the group, e.g. \"Billing period\". */\n label: string;\n className?: string;\n};\n\nexport function Tabs<T extends string>({ tabs, value, onChange, label, className }: TabsProps<T>) {\n const reduced = useReducedMotion();\n const pull = reduced ? 0 : PULL;\n\n const trackRef = useRef<HTMLDivElement>(null);\n\n const [rects, setRects] = useState<Rect[]>([]);\n const [trackWidth, setTrackWidth] = useState(0);\n const [hovered, setHovered] = useState<number | null>(null);\n const [ready, setReady] = useState(false);\n\n const foundIdx = tabs.findIndex((t) => t.id === value);\n const activeIdx = foundIdx === -1 ? 0 : foundIdx;\n\n const springConfig = reduced ? { stiffness: 700, damping: 60 } : { stiffness: 420, damping: 34, mass: 0.7 };\n const pillLeft = useSpring(0, springConfig);\n const pillWidth = useSpring(0, springConfig);\n const innerX = useTransform(pillLeft, (v) => -v);\n\n const measure = useCallback(() => {\n const track = trackRef.current;\n if (!track) return;\n setTrackWidth(track.offsetWidth);\n const btns = track.querySelectorAll<HTMLButtonElement>('button[data-index]');\n setRects(\n Array.from(btns, (el) => ({\n left: el.offsetLeft,\n width: el.offsetWidth,\n })),\n );\n }, []);\n\n // biome-ignore lint/plugin: measures live DOM layout under a ResizeObserver — post-render measurement, no render-time equivalent.\n useEffect(() => {\n measure();\n const track = trackRef.current;\n if (!track) return;\n const ro = new ResizeObserver(measure);\n ro.observe(track);\n return () => ro.disconnect();\n }, [measure]);\n\n // biome-ignore lint/plugin: imperatively drives the pill's spring motion values toward a layout-derived target.\n useEffect(() => {\n if (rects.length !== tabs.length) return;\n const target = pillTarget(activeIdx, hovered, rects, pull);\n if (ready) {\n pillLeft.set(target.left);\n pillWidth.set(target.width);\n } else {\n pillLeft.jump(target.left);\n pillWidth.jump(target.width);\n setReady(true);\n }\n }, [rects, tabs.length, activeIdx, hovered, pull, ready, pillLeft, pillWidth]);\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => onChange(tabs[Number(e.currentTarget.dataset.index)]!.id),\n [onChange, tabs],\n );\n const handleEnter = useCallback((e: MouseEvent<HTMLButtonElement>) => setHovered(Number(e.currentTarget.dataset.index)), []);\n const handleLeave = useCallback(() => setHovered(null), []);\n\n return (\n <fieldset\n aria-label={label}\n className={cn('inline-flex items-center rounded-full border border-(--color-border) bg-(--color-surface) p-1', className)}\n >\n <div ref={trackRef} className=\"relative inline-flex items-center\">\n {/* Base labels — muted at rest; hidden wherever the opaque pill sits over them. */}\n {tabs.map((tab, i) => (\n <button\n key={tab.id}\n type=\"button\"\n data-index={i}\n onClick={handleClick}\n onMouseEnter={handleEnter}\n onMouseLeave={handleLeave}\n aria-pressed={value === tab.id}\n className={cn(labelBox, 'text-(--color-muted-foreground) hover:text-(--color-foreground)')}\n >\n {tab.label}\n {tab.badge ? <span className={badgeBox}>{tab.badge}</span> : null}\n </button>\n ))}\n\n {/* Sliding, pulling pill carrying a clipped inverted copy of the labels. */}\n <motion.div\n aria-hidden={true}\n className=\"pointer-events-none absolute inset-y-0 select-none overflow-hidden rounded-full bg-(--color-foreground)\"\n style={{ left: pillLeft, width: pillWidth, opacity: ready ? 1 : 0 }}\n >\n <motion.div className=\"absolute inset-y-0 left-0 flex items-center\" style={{ width: trackWidth, x: innerX }}>\n {tabs.map((tab) => (\n <span key={tab.id} className={cn(labelBox, 'text-(--color-surface)')}>\n {tab.label}\n {tab.badge ? <span className={badgeBox}>{tab.badge}</span> : null}\n </span>\n ))}\n </motion.div>\n </motion.div>\n </div>\n </fieldset>\n );\n}\n"]}
package/dist/tabs.mjs ADDED
@@ -0,0 +1,4 @@
1
+ export { Tabs } from './chunk-MJYBPJBA.mjs';
2
+ import './chunk-QAWSH567.mjs';
3
+ //# sourceMappingURL=tabs.mjs.map
4
+ //# sourceMappingURL=tabs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"tabs.mjs"}
@@ -0,0 +1,10 @@
1
+ export type ThemeToggleProps = {
2
+ /** Current theme — drives which icon/label the roll shows. */
3
+ theme: 'light' | 'dark';
4
+ /** Flip handler; the parent persists the new theme. */
5
+ onToggle: () => void;
6
+ /** Render the labeled row (mobile drawer) instead of the icon-only square. */
7
+ showLabel?: boolean;
8
+ };
9
+ export declare function ThemeToggle({ theme, onToggle, showLabel }: ThemeToggleProps): import("react").JSX.Element;
10
+ //# sourceMappingURL=theme-toggle.d.ts.map
@@ -0,0 +1,10 @@
1
+ export type ThemeToggleProps = {
2
+ /** Current theme — drives which icon/label the roll shows. */
3
+ theme: 'light' | 'dark';
4
+ /** Flip handler; the parent persists the new theme. */
5
+ onToggle: () => void;
6
+ /** Render the labeled row (mobile drawer) instead of the icon-only square. */
7
+ showLabel?: boolean;
8
+ };
9
+ export declare function ThemeToggle({ theme, onToggle, showLabel }: ThemeToggleProps): import("react").JSX.Element;
10
+ //# sourceMappingURL=theme-toggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-toggle.d.ts","sourceRoot":"","sources":["../../src/components/theme-toggle.tsx"],"names":[],"mappings":"AAgEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,8DAA8D;IAC9D,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAiB,EAAE,EAAE,gBAAgB,+BAwBnF"}
@@ -0,0 +1,251 @@
1
+ 'use strict';
2
+
3
+ var framerMotion = require('framer-motion');
4
+ var react = require('react');
5
+ var clsx = require('clsx');
6
+ var tailwindMerge = require('tailwind-merge');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+ var classVarianceAuthority = require('class-variance-authority');
9
+
10
+ // src/lib/ease.ts
11
+ var EASE_OUT = [0.16, 1, 0.3, 1];
12
+ var EASE_OUT_CSS = "cubic-bezier(0.16, 1, 0.3, 1)";
13
+ var SPRING_SWAP = { type: "spring", stiffness: 460, damping: 30, mass: 0.55 };
14
+ function cn(...inputs) {
15
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
16
+ }
17
+ var ROLL_EXIT_TRANSITION = { duration: 0.14, ease: EASE_OUT };
18
+ var ROLL_BLUR = "blur(3px)";
19
+ var AT_REST = { opacity: 1, y: 0, filter: "blur(0px)" };
20
+ var TEXT_VARIANTS = {
21
+ initial: { opacity: 0, y: "90%", filter: ROLL_BLUR },
22
+ animate: { opacity: 1, y: "0%", filter: "blur(0px)", transition: SPRING_SWAP },
23
+ exit: { opacity: 0, y: "-90%", filter: ROLL_BLUR, transition: ROLL_EXIT_TRANSITION }
24
+ };
25
+ var ICON_VARIANTS = {
26
+ initial: { opacity: 0, y: 12, filter: ROLL_BLUR },
27
+ animate: { opacity: 1, y: 0, filter: "blur(0px)", transition: SPRING_SWAP },
28
+ exit: { opacity: 0, y: -12, filter: ROLL_BLUR, transition: ROLL_EXIT_TRANSITION }
29
+ };
30
+ function ActionSwapRollText({ value, children, className }) {
31
+ const reduce = framerMotion.useReducedMotion() ?? false;
32
+ const measureRef = react.useRef(null);
33
+ const [width, setWidth] = react.useState();
34
+ react.useLayoutEffect(() => {
35
+ const nextWidth = measureRef.current?.offsetWidth;
36
+ if (!nextWidth) return;
37
+ setWidth((current) => current === nextWidth ? current : nextWidth);
38
+ });
39
+ return /* @__PURE__ */ jsxRuntime.jsxs(
40
+ "span",
41
+ {
42
+ className: cn("relative inline-block overflow-hidden whitespace-nowrap align-bottom", className),
43
+ style: { width, transition: reduce ? void 0 : `width 220ms ${EASE_OUT_CSS}` },
44
+ children: [
45
+ /* @__PURE__ */ jsxRuntime.jsx("span", { ref: measureRef, "aria-hidden": true, className: "invisible inline-block whitespace-nowrap", children }),
46
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
47
+ framerMotion.motion.span,
48
+ {
49
+ variants: TEXT_VARIANTS,
50
+ initial: reduce ? false : "initial",
51
+ animate: reduce ? AT_REST : "animate",
52
+ exit: reduce ? void 0 : "exit",
53
+ className: "absolute top-0 left-0 inline-block will-change-[opacity,filter,transform]",
54
+ children
55
+ },
56
+ value
57
+ ) })
58
+ ]
59
+ }
60
+ );
61
+ }
62
+ function ActionSwapRollIcon({ value, children, className }) {
63
+ const reduce = framerMotion.useReducedMotion() ?? false;
64
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("relative inline-grid shrink-0 place-items-center overflow-hidden", className), children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "popLayout", initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
65
+ framerMotion.motion.span,
66
+ {
67
+ "aria-hidden": true,
68
+ variants: ICON_VARIANTS,
69
+ initial: reduce ? false : "initial",
70
+ animate: reduce ? AT_REST : "animate",
71
+ exit: reduce ? void 0 : "exit",
72
+ className: "col-start-1 row-start-1 inline-flex items-center justify-center will-change-[opacity,filter,transform]",
73
+ children
74
+ },
75
+ value
76
+ ) }) });
77
+ }
78
+ var buttonVariants = classVarianceAuthority.cva(
79
+ "group inline-flex items-center justify-center gap-1.5 font-medium [transition:opacity_.24s_80ms,transform_.15s_cubic-bezier(.4,0,.2,1),box-shadow_.15s_cubic-bezier(.4,0,.2,1),filter_.15s_cubic-bezier(.4,0,.2,1)] disabled:[transition:opacity_.1s]",
80
+ {
81
+ variants: {
82
+ variant: {
83
+ filled: "bg-(--foreground) text-(--primary-foreground) shadow-elevated-3 hover:opacity-90",
84
+ bordered: "border border-(--border-hi) bg-surface-3 text-foreground shadow-elevated-3 hover:bg-(--muted)",
85
+ ghost: "text-(--foreground-secondary) hover:bg-(--surface-hover) hover:text-(--foreground)"
86
+ },
87
+ size: {
88
+ sm: "h-8 px-3.5 text-[15px]",
89
+ md: "h-9 px-5 text-[17px]",
90
+ lg: "h-11 px-6 text-[17px]",
91
+ icon: "size-11"
92
+ },
93
+ shape: {
94
+ rounded: "rounded-interactive",
95
+ pill: "rounded-full"
96
+ }
97
+ },
98
+ // Pill is the house standard — every CTA is fully rounded.
99
+ defaultVariants: { variant: "filled", size: "md", shape: "pill" }
100
+ }
101
+ );
102
+ function Button({
103
+ variant,
104
+ size,
105
+ shape,
106
+ showArrow = false,
107
+ external = false,
108
+ title,
109
+ actionOrLink,
110
+ children,
111
+ className,
112
+ "aria-label": ariaLabel,
113
+ "aria-expanded": ariaExpanded
114
+ }) {
115
+ const buttonClassName = cn(buttonVariants({ variant, size, shape }), className);
116
+ const arrowSlot = showArrow && /* @__PURE__ */ jsxRuntime.jsxs(
117
+ "svg",
118
+ {
119
+ viewBox: "0 0 14 14",
120
+ width: "14",
121
+ height: "14",
122
+ fill: "none",
123
+ stroke: "currentColor",
124
+ strokeWidth: "2",
125
+ strokeLinecap: "round",
126
+ strokeLinejoin: "round",
127
+ "aria-hidden": true,
128
+ className: "shrink-0",
129
+ children: [
130
+ /* @__PURE__ */ jsxRuntime.jsx(
131
+ "line",
132
+ {
133
+ x1: "0",
134
+ y1: "7",
135
+ x2: "9",
136
+ y2: "7",
137
+ strokeDasharray: "9",
138
+ className: "[stroke-dashoffset:9] [transition:stroke-dashoffset_.08s_cubic-bezier(.22,1,.36,1)] group-hover:[stroke-dashoffset:0] group-hover:[transition-duration:.22s]"
139
+ }
140
+ ),
141
+ /* @__PURE__ */ jsxRuntime.jsx(
142
+ "path",
143
+ {
144
+ d: "M5 3l4 4-4 4",
145
+ className: "[transform:translateX(0)] [transition:transform_.22s_cubic-bezier(.22,1,.36,1)] group-hover:[transform:translateX(2px)]"
146
+ }
147
+ )
148
+ ]
149
+ }
150
+ );
151
+ if (typeof actionOrLink === "string")
152
+ return /* @__PURE__ */ jsxRuntime.jsxs(
153
+ "a",
154
+ {
155
+ href: actionOrLink,
156
+ className: cn(buttonClassName, "no-underline"),
157
+ "aria-label": ariaLabel,
158
+ title,
159
+ ...external ? { target: "_blank", rel: "noopener noreferrer" } : {},
160
+ children: [
161
+ children,
162
+ arrowSlot
163
+ ]
164
+ }
165
+ );
166
+ return /* @__PURE__ */ jsxRuntime.jsxs(
167
+ "button",
168
+ {
169
+ type: "button",
170
+ className: buttonClassName,
171
+ onClick: actionOrLink,
172
+ "aria-label": ariaLabel,
173
+ "aria-expanded": ariaExpanded,
174
+ title,
175
+ children: [
176
+ children,
177
+ arrowSlot
178
+ ]
179
+ }
180
+ );
181
+ }
182
+ var ICON_LAYOUT = "w-8 p-0";
183
+ var LABELED_LAYOUT = "gap-2 px-3 text-[13px]";
184
+ function SunIcon() {
185
+ return /* @__PURE__ */ jsxRuntime.jsxs(
186
+ "svg",
187
+ {
188
+ width: 14,
189
+ height: 14,
190
+ viewBox: "0 0 24 24",
191
+ fill: "none",
192
+ stroke: "currentColor",
193
+ strokeWidth: 2,
194
+ strokeLinecap: "round",
195
+ strokeLinejoin: "round",
196
+ "aria-hidden": true,
197
+ children: [
198
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4" }),
199
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "1" }),
200
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "23", x2: "12", y2: "21" }),
201
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "12", x2: "1", y2: "12" }),
202
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "23", y1: "12", x2: "21", y2: "12" }),
203
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "5", y1: "5", x2: "3.5", y2: "3.5" }),
204
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "20.5", y1: "20.5", x2: "19", y2: "19" }),
205
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "19", y1: "5", x2: "20.5", y2: "3.5" }),
206
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4.5", y1: "20.5", x2: "3", y2: "19" })
207
+ ]
208
+ }
209
+ );
210
+ }
211
+ function MoonIcon() {
212
+ return /* @__PURE__ */ jsxRuntime.jsx(
213
+ "svg",
214
+ {
215
+ width: 14,
216
+ height: 14,
217
+ viewBox: "0 0 24 24",
218
+ fill: "none",
219
+ stroke: "currentColor",
220
+ strokeWidth: 2,
221
+ strokeLinecap: "round",
222
+ strokeLinejoin: "round",
223
+ "aria-hidden": true,
224
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z" })
225
+ }
226
+ );
227
+ }
228
+ function ThemeToggle({ theme, onToggle, showLabel = false }) {
229
+ const size = showLabel ? "md" : "sm";
230
+ const layoutClassName = showLabel ? LABELED_LAYOUT : ICON_LAYOUT;
231
+ const isDark = theme === "dark";
232
+ const label = isDark ? "Switch to light mode" : "Switch to dark mode";
233
+ return /* @__PURE__ */ jsxRuntime.jsxs(
234
+ Button,
235
+ {
236
+ variant: "filled",
237
+ size,
238
+ actionOrLink: onToggle,
239
+ className: layoutClassName,
240
+ "aria-label": label,
241
+ children: [
242
+ /* @__PURE__ */ jsxRuntime.jsx(ActionSwapRollIcon, { value: theme, className: "h-4 w-4", children: isDark ? /* @__PURE__ */ jsxRuntime.jsx(SunIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(MoonIcon, {}) }),
243
+ showLabel ? /* @__PURE__ */ jsxRuntime.jsx(ActionSwapRollText, { value: theme, children: label }) : null
244
+ ]
245
+ }
246
+ );
247
+ }
248
+
249
+ exports.ThemeToggle = ThemeToggle;
250
+ //# sourceMappingURL=theme-toggle.js.map
251
+ //# sourceMappingURL=theme-toggle.js.map