@stacksjs/components 0.2.3

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 (342) hide show
  1. package/README.md +477 -0
  2. package/dist/Accordion-x2gjedap.stx +108 -0
  3. package/dist/Audio-0fk6p9tn.stx +50 -0
  4. package/dist/Avatar-5m0v0s4x.stx +88 -0
  5. package/dist/Badge-ynes82vv.stx +70 -0
  6. package/dist/Breadcrumb-h9n427sk.stx +79 -0
  7. package/dist/Button-7xtrzn14.stx +89 -0
  8. package/dist/Calendar-egvx1pma.stx +232 -0
  9. package/dist/Card-r9w7vy1a.stx +74 -0
  10. package/dist/Checkbox-m8v46pmx.stx +92 -0
  11. package/dist/Checkout-vcnnr176.stx +39 -0
  12. package/dist/CodeBlock-mwex82cm.stx +57 -0
  13. package/dist/Combobox-npb08vkp.stx +13 -0
  14. package/dist/ComboboxButton-vcdyq4zw.stx +13 -0
  15. package/dist/ComboboxInput-c9d2p02m.stx +25 -0
  16. package/dist/ComboboxOption-7d0h9ash.stx +52 -0
  17. package/dist/ComboboxOptions-4dtqc0qa.stx +12 -0
  18. package/dist/CommandPalette-ah00bf8v.stx +39 -0
  19. package/dist/CommandPaletteItem-2yx0gvtj.stx +32 -0
  20. package/dist/DefaultPaymentMethod-emw5fg8s.stx +62 -0
  21. package/dist/Dialog-1w4rz7x0.stx +19 -0
  22. package/dist/DialogBackdrop-qbm3y5ab.stx +9 -0
  23. package/dist/DialogDescription-01w5j74b.stx +11 -0
  24. package/dist/DialogPanel-brsszrrs.stx +16 -0
  25. package/dist/DialogTitle-3rxhwg21.stx +11 -0
  26. package/dist/Drawer-yfaz8q7f.stx +137 -0
  27. package/dist/Dropdown-ng7nx556.stx +24 -0
  28. package/dist/DropdownButton-vc4zpyrb.stx +23 -0
  29. package/dist/DropdownItem-z491y23c.stx +43 -0
  30. package/dist/DropdownItems-7ykf29sj.stx +18 -0
  31. package/dist/EmailInput-bfshpj92.stx +41 -0
  32. package/dist/Footer-xdaf02nr.stx +66 -0
  33. package/dist/Form-z4f7w6r1.stx +209 -0
  34. package/dist/Hero-9vvp7pxf.stx +25 -0
  35. package/dist/Image-ktk0451c.stx +197 -0
  36. package/dist/Installation-33bj4rm7.stx +39 -0
  37. package/dist/Listbox-xcejcmyc.stx +14 -0
  38. package/dist/ListboxButton-nx5kmw19.stx +17 -0
  39. package/dist/ListboxLabel-aky8a73n.stx +11 -0
  40. package/dist/ListboxOption-6h4hsdz5.stx +57 -0
  41. package/dist/ListboxOptions-2sacdtm5.stx +16 -0
  42. package/dist/Login-s2jkvd9y.stx +163 -0
  43. package/dist/Navigator-tqqdgdkw.stx +138 -0
  44. package/dist/Notification-sry8egea.stx +71 -0
  45. package/dist/NumberInput-zm93ddfw.stx +138 -0
  46. package/dist/Pagination-yza9v4qw.stx +183 -0
  47. package/dist/PasswordInput-4xnndp5y.stx +155 -0
  48. package/dist/PaymentMethods-jxba6kek.stx +113 -0
  49. package/dist/Popover-scbqatjq.stx +24 -0
  50. package/dist/PopoverButton-rbxhzj4k.stx +23 -0
  51. package/dist/PopoverPanel-9ng1rm6e.stx +23 -0
  52. package/dist/Portal-j111yvqp.stx +74 -0
  53. package/dist/Progress-gbmw3akr.stx +142 -0
  54. package/dist/Radio-1n9qamv1.stx +81 -0
  55. package/dist/RadioGroup-12dw2vhk.stx +13 -0
  56. package/dist/RadioGroupDescription-bjm3mdht.stx +11 -0
  57. package/dist/RadioGroupLabel-aky8a73n.stx +11 -0
  58. package/dist/RadioGroupOption-9pcxgwmd.stx +51 -0
  59. package/dist/SearchInput-g240e2nq.stx +63 -0
  60. package/dist/Select-p9f94b20.stx +89 -0
  61. package/dist/Sidebar-gwxw8q38.stx +288 -0
  62. package/dist/SidebarFooter-887kxret.stx +95 -0
  63. package/dist/SidebarHeader-qjeyx0hv.stx +100 -0
  64. package/dist/SidebarItem-4dpwtmb4.stx +118 -0
  65. package/dist/SidebarSection-kh92fk4m.stx +108 -0
  66. package/dist/Signup-3nxq5zag.stx +161 -0
  67. package/dist/Skeleton-g0nx1njm.stx +48 -0
  68. package/dist/Spinner-w4zj58xp.stx +109 -0
  69. package/dist/Stepper-rwc5actp.stx +17 -0
  70. package/dist/StepperStep-h0k657nm.stx +53 -0
  71. package/dist/SubscriptionCheckout-4cbxdg7w.stx +353 -0
  72. package/dist/Switch-kjq8qd2p.stx +74 -0
  73. package/dist/Table-59j9qwp0.stx +14 -0
  74. package/dist/TableBody-reka2gwv.stx +10 -0
  75. package/dist/TableCell-j7nh4sgr.stx +10 -0
  76. package/dist/TableHead-ecbrwpqm.stx +10 -0
  77. package/dist/TableHeader-jn2shwsv.stx +13 -0
  78. package/dist/TableRow-xz8rrxhc.stx +11 -0
  79. package/dist/Tabs-kb6ex6m7.stx +145 -0
  80. package/dist/Teleport-819cg9zm.stx +101 -0
  81. package/dist/TextInput-zx9kgr79.stx +146 -0
  82. package/dist/Textarea-5xvmrb0z.stx +119 -0
  83. package/dist/Tooltip-5ehqsy49.stx +76 -0
  84. package/dist/Transition-2j39s044.stx +174 -0
  85. package/dist/TwoFactorChallenge-2q4gg89m.stx +125 -0
  86. package/dist/Video-6aq4shh4.stx +45 -0
  87. package/dist/VirtualList-weh5qtm1.stx +83 -0
  88. package/dist/VirtualTable-59a1eskc.stx +113 -0
  89. package/dist/components/index.d.ts +4 -0
  90. package/dist/composables/index.d.ts +4 -0
  91. package/dist/composables/index.js +3 -0
  92. package/dist/composables/useCopyCode.d.ts +19 -0
  93. package/dist/composables/useDarkMode.d.ts +25 -0
  94. package/dist/composables/useSEO.d.ts +36 -0
  95. package/dist/composables/utilities/index.d.ts +5 -0
  96. package/dist/composables/utilities/useClickOutside.d.ts +28 -0
  97. package/dist/composables/utilities/useFocusTrap.d.ts +34 -0
  98. package/dist/composables/utilities/useKeyboard.d.ts +55 -0
  99. package/dist/composables/utilities/useLocalStorage.d.ts +33 -0
  100. package/dist/composables/utilities/useMediaQuery.d.ts +64 -0
  101. package/dist/index.d.ts +86 -0
  102. package/dist/index.js +19 -0
  103. package/dist/ui/accordion/index.d.ts +12 -0
  104. package/dist/ui/audio/index.d.ts +12 -0
  105. package/dist/ui/auth/index.d.ts +29 -0
  106. package/dist/ui/avatar/index.d.ts +11 -0
  107. package/dist/ui/badge/index.d.ts +9 -0
  108. package/dist/ui/breadcrumb/index.d.ts +13 -0
  109. package/dist/ui/button/index.d.ts +45 -0
  110. package/dist/ui/calendar/index.d.ts +13 -0
  111. package/dist/ui/card/index.d.ts +12 -0
  112. package/dist/ui/checkbox/index.d.ts +15 -0
  113. package/dist/ui/combobox/index.d.ts +35 -0
  114. package/dist/ui/command-palette/index.d.ts +13 -0
  115. package/dist/ui/dialog/index.d.ts +27 -0
  116. package/dist/ui/drawer/index.d.ts +8 -0
  117. package/dist/ui/dropdown/index.d.ts +25 -0
  118. package/dist/ui/form/index.d.ts +48 -0
  119. package/dist/ui/image/index.d.ts +21 -0
  120. package/dist/ui/input/index.d.ts +73 -0
  121. package/dist/ui/listbox/index.d.ts +34 -0
  122. package/dist/ui/navigator/index.d.ts +18 -0
  123. package/dist/ui/notification/index.d.ts +11 -0
  124. package/dist/ui/pagination/index.d.ts +10 -0
  125. package/dist/ui/payment/index.d.ts +53 -0
  126. package/dist/ui/popover/index.d.ts +19 -0
  127. package/dist/ui/portal/index.d.ts +5 -0
  128. package/dist/ui/progress/index.d.ts +11 -0
  129. package/dist/ui/radio/index.d.ts +14 -0
  130. package/dist/ui/radio-group/index.d.ts +26 -0
  131. package/dist/ui/select/index.d.ts +19 -0
  132. package/dist/ui/sidebar/index.d.ts +129 -0
  133. package/dist/ui/skeleton/index.d.ts +10 -0
  134. package/dist/ui/spinner/index.d.ts +8 -0
  135. package/dist/ui/stepper/index.d.ts +17 -0
  136. package/dist/ui/storage/index.d.ts +40 -0
  137. package/dist/ui/switch/index.d.ts +9 -0
  138. package/dist/ui/table/index.d.ts +29 -0
  139. package/dist/ui/tabs/index.d.ts +14 -0
  140. package/dist/ui/teleport/index.d.ts +6 -0
  141. package/dist/ui/textarea/index.d.ts +19 -0
  142. package/dist/ui/tooltip/index.d.ts +9 -0
  143. package/dist/ui/transition/index.d.ts +24 -0
  144. package/dist/ui/video/index.d.ts +15 -0
  145. package/dist/ui/virtual-list/index.d.ts +9 -0
  146. package/dist/ui/virtual-table/index.d.ts +19 -0
  147. package/dist/utils/accessibility.d.ts +285 -0
  148. package/dist/utils/animation/easing.d.ts +112 -0
  149. package/dist/utils/animation/helpers.d.ts +119 -0
  150. package/dist/utils/animation/index.d.ts +3 -0
  151. package/dist/utils/animation/keyframes.d.ts +334 -0
  152. package/dist/utils/error-handling.d.ts +182 -0
  153. package/dist/utils/highlighter.d.ts +27 -0
  154. package/dist/utils/highlighter.js +2 -0
  155. package/dist/utils/i18n.d.ts +245 -0
  156. package/dist/utils/keep-alive.d.ts +171 -0
  157. package/dist/utils/lazy-loading.d.ts +174 -0
  158. package/dist/utils/prop-validation.d.ts +106 -0
  159. package/dist/utils/theme.d.ts +234 -0
  160. package/package.json +69 -0
  161. package/src/components/CodeBlock.stx +57 -0
  162. package/src/components/Footer.stx +66 -0
  163. package/src/components/Hero.stx +25 -0
  164. package/src/components/Installation.stx +39 -0
  165. package/src/components/index.ts +4 -0
  166. package/src/composables/index.ts +4 -0
  167. package/src/composables/useCopyCode.ts +73 -0
  168. package/src/composables/useDarkMode.ts +100 -0
  169. package/src/composables/useSEO.ts +229 -0
  170. package/src/composables/utilities/index.ts +7 -0
  171. package/src/composables/utilities/useClickOutside.ts +72 -0
  172. package/src/composables/utilities/useFocusTrap.ts +159 -0
  173. package/src/composables/utilities/useKeyboard.ts +124 -0
  174. package/src/composables/utilities/useLocalStorage.ts +132 -0
  175. package/src/composables/utilities/useMediaQuery.ts +118 -0
  176. package/src/index.ts +99 -0
  177. package/src/stx.d.ts +4 -0
  178. package/src/ui/accordion/Accordion.stx +108 -0
  179. package/src/ui/accordion/index.ts +14 -0
  180. package/src/ui/audio/Audio.stx +50 -0
  181. package/src/ui/audio/README.md +42 -0
  182. package/src/ui/audio/index.ts +13 -0
  183. package/src/ui/auth/Login.stx +163 -0
  184. package/src/ui/auth/README.md +145 -0
  185. package/src/ui/auth/Signup.stx +161 -0
  186. package/src/ui/auth/TwoFactorChallenge.stx +125 -0
  187. package/src/ui/auth/index.ts +32 -0
  188. package/src/ui/avatar/Avatar.stx +88 -0
  189. package/src/ui/avatar/index.ts +12 -0
  190. package/src/ui/badge/Badge.stx +70 -0
  191. package/src/ui/badge/index.ts +10 -0
  192. package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
  193. package/src/ui/breadcrumb/index.ts +15 -0
  194. package/src/ui/button/Button.stx +89 -0
  195. package/src/ui/button/README.md +100 -0
  196. package/src/ui/button/index.ts +85 -0
  197. package/src/ui/calendar/Calendar.stx +232 -0
  198. package/src/ui/calendar/README.md +254 -0
  199. package/src/ui/calendar/index.ts +14 -0
  200. package/src/ui/card/Card.stx +74 -0
  201. package/src/ui/card/index.ts +13 -0
  202. package/src/ui/checkbox/Checkbox.stx +92 -0
  203. package/src/ui/checkbox/index.ts +16 -0
  204. package/src/ui/combobox/Combobox.stx +13 -0
  205. package/src/ui/combobox/ComboboxButton.stx +13 -0
  206. package/src/ui/combobox/ComboboxInput.stx +25 -0
  207. package/src/ui/combobox/ComboboxOption.stx +52 -0
  208. package/src/ui/combobox/ComboboxOptions.stx +12 -0
  209. package/src/ui/combobox/README.md +154 -0
  210. package/src/ui/combobox/index.ts +40 -0
  211. package/src/ui/command-palette/CommandPalette.stx +39 -0
  212. package/src/ui/command-palette/CommandPaletteItem.stx +32 -0
  213. package/src/ui/command-palette/README.md +42 -0
  214. package/src/ui/command-palette/index.ts +15 -0
  215. package/src/ui/dialog/Dialog.stx +19 -0
  216. package/src/ui/dialog/DialogBackdrop.stx +9 -0
  217. package/src/ui/dialog/DialogDescription.stx +11 -0
  218. package/src/ui/dialog/DialogPanel.stx +16 -0
  219. package/src/ui/dialog/DialogTitle.stx +11 -0
  220. package/src/ui/dialog/README.md +133 -0
  221. package/src/ui/dialog/index.ts +32 -0
  222. package/src/ui/drawer/Drawer.stx +137 -0
  223. package/src/ui/drawer/README.md +110 -0
  224. package/src/ui/drawer/index.ts +9 -0
  225. package/src/ui/dropdown/Dropdown.stx +24 -0
  226. package/src/ui/dropdown/DropdownButton.stx +23 -0
  227. package/src/ui/dropdown/DropdownItem.stx +43 -0
  228. package/src/ui/dropdown/DropdownItems.stx +18 -0
  229. package/src/ui/dropdown/README.md +142 -0
  230. package/src/ui/dropdown/index.ts +29 -0
  231. package/src/ui/form/Form.stx +209 -0
  232. package/src/ui/form/index.ts +67 -0
  233. package/src/ui/heatmap/Heatmap.stx +315 -0
  234. package/src/ui/image/Image.stx +197 -0
  235. package/src/ui/image/README.md +171 -0
  236. package/src/ui/image/index.ts +22 -0
  237. package/src/ui/input/EmailInput.stx +41 -0
  238. package/src/ui/input/NumberInput.stx +138 -0
  239. package/src/ui/input/PasswordInput.stx +155 -0
  240. package/src/ui/input/SearchInput.stx +63 -0
  241. package/src/ui/input/TextInput.stx +146 -0
  242. package/src/ui/input/index.ts +180 -0
  243. package/src/ui/listbox/Listbox.stx +14 -0
  244. package/src/ui/listbox/ListboxButton.stx +17 -0
  245. package/src/ui/listbox/ListboxLabel.stx +11 -0
  246. package/src/ui/listbox/ListboxOption.stx +57 -0
  247. package/src/ui/listbox/ListboxOptions.stx +16 -0
  248. package/src/ui/listbox/README.md +143 -0
  249. package/src/ui/listbox/index.ts +39 -0
  250. package/src/ui/navigator/Navigator.stx +138 -0
  251. package/src/ui/navigator/README.md +352 -0
  252. package/src/ui/navigator/index.ts +20 -0
  253. package/src/ui/notification/Notification.stx +71 -0
  254. package/src/ui/notification/README.md +118 -0
  255. package/src/ui/notification/index.ts +12 -0
  256. package/src/ui/pagination/Pagination.stx +183 -0
  257. package/src/ui/pagination/index.ts +11 -0
  258. package/src/ui/payment/Checkout.stx +39 -0
  259. package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
  260. package/src/ui/payment/PaymentMethods.stx +113 -0
  261. package/src/ui/payment/README.md +427 -0
  262. package/src/ui/payment/SubscriptionCheckout.stx +353 -0
  263. package/src/ui/payment/index.ts +59 -0
  264. package/src/ui/popover/Popover.stx +24 -0
  265. package/src/ui/popover/PopoverButton.stx +23 -0
  266. package/src/ui/popover/PopoverPanel.stx +23 -0
  267. package/src/ui/popover/README.md +95 -0
  268. package/src/ui/popover/index.ts +22 -0
  269. package/src/ui/portal/Portal.stx +74 -0
  270. package/src/ui/portal/index.ts +6 -0
  271. package/src/ui/progress/Progress.stx +142 -0
  272. package/src/ui/progress/index.ts +12 -0
  273. package/src/ui/radio/Radio.stx +81 -0
  274. package/src/ui/radio/index.ts +15 -0
  275. package/src/ui/radio-group/README.md +145 -0
  276. package/src/ui/radio-group/RadioGroup.stx +13 -0
  277. package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
  278. package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
  279. package/src/ui/radio-group/RadioGroupOption.stx +51 -0
  280. package/src/ui/radio-group/index.ts +30 -0
  281. package/src/ui/select/README.md +204 -0
  282. package/src/ui/select/Select.stx +89 -0
  283. package/src/ui/select/index.ts +21 -0
  284. package/src/ui/sidebar/Sidebar.stx +288 -0
  285. package/src/ui/sidebar/SidebarFooter.stx +95 -0
  286. package/src/ui/sidebar/SidebarHeader.stx +100 -0
  287. package/src/ui/sidebar/SidebarItem.stx +118 -0
  288. package/src/ui/sidebar/SidebarSection.stx +108 -0
  289. package/src/ui/sidebar/index.ts +175 -0
  290. package/src/ui/skeleton/Skeleton.stx +48 -0
  291. package/src/ui/skeleton/index.ts +11 -0
  292. package/src/ui/spinner/Spinner.stx +109 -0
  293. package/src/ui/spinner/index.ts +9 -0
  294. package/src/ui/stepper/README.md +36 -0
  295. package/src/ui/stepper/Stepper.stx +17 -0
  296. package/src/ui/stepper/StepperStep.stx +53 -0
  297. package/src/ui/stepper/index.ts +19 -0
  298. package/src/ui/storage/README.md +193 -0
  299. package/src/ui/storage/index.ts +347 -0
  300. package/src/ui/switch/README.md +133 -0
  301. package/src/ui/switch/Switch.stx +74 -0
  302. package/src/ui/switch/index.ts +10 -0
  303. package/src/ui/table/README.md +39 -0
  304. package/src/ui/table/Table.stx +14 -0
  305. package/src/ui/table/TableBody.stx +10 -0
  306. package/src/ui/table/TableCell.stx +10 -0
  307. package/src/ui/table/TableHead.stx +10 -0
  308. package/src/ui/table/TableHeader.stx +13 -0
  309. package/src/ui/table/TableRow.stx +11 -0
  310. package/src/ui/table/index.ts +35 -0
  311. package/src/ui/tabs/Tabs.stx +145 -0
  312. package/src/ui/tabs/index.ts +16 -0
  313. package/src/ui/teleport/Teleport.stx +101 -0
  314. package/src/ui/teleport/index.ts +7 -0
  315. package/src/ui/textarea/Textarea.stx +119 -0
  316. package/src/ui/textarea/index.ts +20 -0
  317. package/src/ui/tooltip/Tooltip.stx +76 -0
  318. package/src/ui/tooltip/index.ts +10 -0
  319. package/src/ui/transition/README.md +30 -0
  320. package/src/ui/transition/Transition.stx +174 -0
  321. package/src/ui/transition/index.ts +25 -0
  322. package/src/ui/video/README.md +48 -0
  323. package/src/ui/video/Video.stx +45 -0
  324. package/src/ui/video/index.ts +16 -0
  325. package/src/ui/virtual-list/VirtualList.stx +83 -0
  326. package/src/ui/virtual-list/index.ts +10 -0
  327. package/src/ui/virtual-table/VirtualTable.stx +113 -0
  328. package/src/ui/virtual-table/index.ts +21 -0
  329. package/src/utils/PROP_VALIDATION.md +458 -0
  330. package/src/utils/THEME_SYSTEM.md +702 -0
  331. package/src/utils/accessibility.ts +841 -0
  332. package/src/utils/animation/easing.ts +368 -0
  333. package/src/utils/animation/helpers.ts +223 -0
  334. package/src/utils/animation/index.ts +5 -0
  335. package/src/utils/animation/keyframes.ts +230 -0
  336. package/src/utils/error-handling.ts +473 -0
  337. package/src/utils/highlighter.ts +93 -0
  338. package/src/utils/i18n.ts +634 -0
  339. package/src/utils/keep-alive.ts +818 -0
  340. package/src/utils/lazy-loading.ts +530 -0
  341. package/src/utils/prop-validation.ts +478 -0
  342. package/src/utils/theme.ts +626 -0
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Create a custom theme by merging with default theme
3
+ *
4
+ * @param customTheme - Partial theme configuration
5
+ * @returns Complete theme object
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const myTheme = createTheme({
10
+ * colors: {
11
+ * primary: '#3b82f6',
12
+ * secondary: '#8b5cf6'
13
+ * },
14
+ * typography: {
15
+ * fontFamily: {
16
+ * sans: 'Inter, sans-serif'
17
+ * }
18
+ * }
19
+ * })
20
+ * ```
21
+ */
22
+ export declare function createTheme(customTheme: Partial<Theme>): Theme;
23
+ /**
24
+ * Get the current active theme
25
+ *
26
+ * @returns Current theme object
27
+ */
28
+ export declare function getTheme(): Theme;
29
+ /**
30
+ * Convert theme to CSS custom properties
31
+ *
32
+ * @param theme - Theme object
33
+ * @returns CSS custom properties string
34
+ */
35
+ export declare function themeToCSSProperties(theme: Theme): string;
36
+ /**
37
+ * Apply theme to the document
38
+ *
39
+ * @param theme - Theme object to apply
40
+ * @param target - Target element (defaults to document.documentElement)
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const theme = createTheme({ colors: { primary: '#3b82f6' } })
45
+ * applyTheme(theme)
46
+ * ```
47
+ */
48
+ export declare function applyTheme(theme: Theme, target?: HTMLElement): void;
49
+ /**
50
+ * Hook to use theme in components
51
+ *
52
+ * @returns Current theme object
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const theme = useTheme()
57
+ * const primaryColor = theme.colors.primary
58
+ * ```
59
+ */
60
+ export declare function useTheme(): Theme;
61
+ /**
62
+ * Get a specific theme value by path
63
+ *
64
+ * @param path - Dot-notation path to theme value
65
+ * @returns Theme value or undefined
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * const primary = getThemeValue('colors.primary')
70
+ * const fontSize = getThemeValue('typography.fontSize.base')
71
+ * ```
72
+ */
73
+ export declare function getThemeValue(path: string): any;
74
+ /**
75
+ * Generate CSS file content for a theme
76
+ *
77
+ * @param theme - Theme object
78
+ * @returns CSS file content
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const theme = createTheme({ colors: { primary: '#3b82f6' } })
83
+ * const css = generateThemeCSS(theme)
84
+ * // Write to file or inject into page
85
+ * ```
86
+ */
87
+ export declare function generateThemeCSS(theme: Theme): string;
88
+ /**
89
+ * Default theme
90
+ */
91
+ export declare const defaultTheme: Theme;
92
+ /**
93
+ * Predefined theme presets
94
+ */
95
+ export declare const themePresets: {
96
+ default: Theme
97
+ purple: Theme
98
+ green: Theme
99
+ orange: Theme
100
+ dark: Theme
101
+ minimal: Theme
102
+ };
103
+ /**
104
+ * Theme system for @stacksjs/components
105
+ *
106
+ * Provides customizable color palettes, typography, spacing, and component variants
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * import { createTheme, applyTheme, useTheme } from '@stacksjs/components'
111
+ *
112
+ * const myTheme = createTheme({
113
+ * colors: {
114
+ * primary: '#3b82f6',
115
+ * secondary: '#8b5cf6'
116
+ * }
117
+ * })
118
+ *
119
+ * applyTheme(myTheme)
120
+ * ```
121
+ */
122
+ /**
123
+ * Color palette definition
124
+ */
125
+ export declare interface ColorPalette {
126
+ primary?: string
127
+ secondary?: string
128
+ success?: string
129
+ warning?: string
130
+ danger?: string
131
+ info?: string
132
+ gray?: string
133
+ white?: string
134
+ black?: string
135
+ }
136
+ /**
137
+ * Typography scale
138
+ */
139
+ export declare interface Typography {
140
+ fontFamily?: {
141
+ sans?: string
142
+ serif?: string
143
+ mono?: string
144
+ }
145
+ fontSize?: {
146
+ 'xs'?: string
147
+ 'sm'?: string
148
+ 'base'?: string
149
+ 'lg'?: string
150
+ 'xl'?: string
151
+ '2xl'?: string
152
+ '3xl'?: string
153
+ '4xl'?: string
154
+ }
155
+ fontWeight?: {
156
+ light?: string
157
+ normal?: string
158
+ medium?: string
159
+ semibold?: string
160
+ bold?: string
161
+ }
162
+ lineHeight?: {
163
+ tight?: string
164
+ normal?: string
165
+ relaxed?: string
166
+ }
167
+ }
168
+ /**
169
+ * Spacing scale
170
+ */
171
+ export declare interface Spacing {
172
+ 'xs'?: string
173
+ 'sm'?: string
174
+ 'md'?: string
175
+ 'lg'?: string
176
+ 'xl'?: string
177
+ '2xl'?: string
178
+ '3xl'?: string
179
+ '4xl'?: string
180
+ }
181
+ /**
182
+ * Border radius scale
183
+ */
184
+ export declare interface BorderRadius {
185
+ none?: string
186
+ sm?: string
187
+ md?: string
188
+ lg?: string
189
+ full?: string
190
+ }
191
+ /**
192
+ * Shadow scale
193
+ */
194
+ export declare interface Shadows {
195
+ 'sm'?: string
196
+ 'md'?: string
197
+ 'lg'?: string
198
+ 'xl'?: string
199
+ '2xl'?: string
200
+ 'none'?: string
201
+ }
202
+ /**
203
+ * Component-specific overrides
204
+ */
205
+ export declare interface ComponentOverrides {
206
+ button?: {
207
+ borderRadius?: string
208
+ padding?: string
209
+ fontSize?: string
210
+ fontWeight?: string
211
+ }
212
+ input?: {
213
+ borderRadius?: string
214
+ padding?: string
215
+ fontSize?: string
216
+ borderWidth?: string
217
+ }
218
+ card?: {
219
+ borderRadius?: string
220
+ padding?: string
221
+ shadow?: string
222
+ }
223
+ }
224
+ /**
225
+ * Complete theme configuration
226
+ */
227
+ export declare interface Theme {
228
+ colors: ColorPalette
229
+ typography: Typography
230
+ spacing: Spacing
231
+ borderRadius: BorderRadius
232
+ shadows: Shadows
233
+ components?: ComponentOverrides
234
+ }
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@stacksjs/components",
3
+ "type": "module",
4
+ "version": "0.2.3",
5
+ "description": "A modern component library built with STX, featuring headwind utility classes and TypeScript syntax highlighting.",
6
+ "author": "Chris Breuer <chris@stacksjs.org>",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/stacksjs/stx/tree/main/packages/components#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/stacksjs/stx.git",
12
+ "directory": "./packages/components"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/stacksjs/stx/issues"
16
+ },
17
+ "keywords": [
18
+ "components",
19
+ "ui",
20
+ "stx",
21
+ "headwind",
22
+ "typescript",
23
+ "bun",
24
+ "component-library"
25
+ ],
26
+ "sideEffects": [
27
+ "**/*.css"
28
+ ],
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js"
33
+ },
34
+ "./*": {
35
+ "types": "./dist/*/index.d.ts",
36
+ "import": "./dist/*/index.js"
37
+ }
38
+ },
39
+ "module": "./dist/index.js",
40
+ "types": "./dist/index.d.ts",
41
+ "files": [
42
+ "README.md",
43
+ "dist",
44
+ "src"
45
+ ],
46
+ "scripts": {
47
+ "build": "bun --bun build.ts",
48
+ "dev": "bun --bun build.ts --watch",
49
+ "test": "bun test",
50
+ "test:watch": "bun test --watch",
51
+ "test:coverage": "bun test --coverage",
52
+ "lint": "bunx --bun pickier lint",
53
+ "lint:fix": "bunx --bun pickier lint --fix",
54
+ "typecheck": "bun tsc --noEmit",
55
+ "prepublishOnly": "bun run build"
56
+ },
57
+ "dependencies": {
58
+ "@stacksjs/headwind": "^0.1.3",
59
+ "@stacksjs/stx": "workspace:*",
60
+ "bun-plugin-stx": "workspace:*",
61
+ "ts-syntax-highlighter": "^0.2.1"
62
+ },
63
+ "devDependencies": {
64
+ "@types/bun": "^1.3.4",
65
+ "bun-plugin-dtsx": "^0.9.9",
66
+ "pickier": "^0.1.10",
67
+ "typescript": "^5.9.3"
68
+ }
69
+ }
@@ -0,0 +1,57 @@
1
+ <script>
2
+ import { highlight, applyHeadwindClasses } from '../utils/highlighter'
3
+
4
+ export const code = $props.code || ''
5
+ export const language = $props.language || 'typescript'
6
+ export const theme = $props.theme || 'auto'
7
+ export const lineNumbers = $props.lineNumbers !== false
8
+ export const copyable = $props.copyable !== false
9
+ export const className = $props.className || ''
10
+
11
+ export let highlightedCode = ''
12
+ export let copied = false
13
+
14
+ // Highlight the code
15
+ const result = await highlight(code, { language, theme, lineNumbers })
16
+ highlightedCode = applyHeadwindClasses(result.html, className)
17
+
18
+ export async function copyToClipboard() {
19
+ try {
20
+ await navigator.clipboard.writeText(code)
21
+ copied = true
22
+ setTimeout(() => {
23
+ copied = false
24
+ }, 2000)
25
+ } catch (err) {
26
+ console.error('Failed to copy code:', err)
27
+ }
28
+ }
29
+ </script>
30
+
31
+ <div class="relative group">
32
+ {!! highlightedCode !!}
33
+
34
+ @if(copyable)
35
+ <button
36
+ @click="copyToClipboard"
37
+ class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 px-3 py-1.5 bg-neutral-800 hover:bg-neutral-700 text-white text-xs rounded-md font-medium"
38
+ aria-label="Copy code"
39
+ >
40
+ @if(copied)
41
+ <span class="flex items-center gap-1.5">
42
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
43
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
44
+ </svg>
45
+ Copied!
46
+ </span>
47
+ @else
48
+ <span class="flex items-center gap-1.5">
49
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
50
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
51
+ </svg>
52
+ Copy
53
+ </span>
54
+ @endif
55
+ </button>
56
+ @endif
57
+ </div>
@@ -0,0 +1,66 @@
1
+ <script>
2
+ export const author = $props.author || '@chrisbbreuer'
3
+ export const authorUrl = $props.authorUrl || 'https://github.com/chrisbbreuer'
4
+ export const project = $props.project || '@stacksjs'
5
+ export const projectUrl = $props.projectUrl || 'https://github.com/stacksjs/stacks'
6
+ export const twitterUrl = $props.twitterUrl || 'https://twitter.com/chrisbbreuer'
7
+ export const sponsorUrl = $props.sponsorUrl || 'https://github.com/sponsors/chrisbbreuer'
8
+ export const coffeeUrl = $props.coffeeUrl || 'https://www.buymeacoffee.com/xlbd'
9
+
10
+ export const currentYear = new Date().getFullYear()
11
+ </script>
12
+
13
+ <footer class="flex items-center justify-center mt-16 w-full pb-4 text-neutral-600 dark:text-neutral-400 opacity-60">
14
+ <div class="flex flex-col items-center justify-center">
15
+ <p class="text-sm">
16
+ With 💙 by
17
+ <a href="{{ authorUrl }}" class="text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 transition-colors">
18
+ {{ author }}
19
+ </a>
20
+ &
21
+ <a href="{{ projectUrl }}" class="text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 transition-colors">
22
+ {{ project }}
23
+ </a>
24
+ <span> © {{ currentYear }}</span>
25
+ </p>
26
+
27
+ <div class="mt-2 flex flex-col items-center sm:flex-row space-x-1 space-y-2 sm:space-y-0">
28
+ <div class="flex items-center space-x-1">
29
+ <svg class="w-4 h-4 text-blue-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
30
+ <path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
31
+ </svg>
32
+ <span class="text-sm">
33
+ <a href="{{ twitterUrl }}" target="_blank" rel="noopener noreferrer" class="text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 transition-colors">
34
+ Follow me on Twitter
35
+ </a>
36
+ </span>
37
+ </div>
38
+
39
+ <span class="hidden px-2 text-neutral-400 sm:block">|</span>
40
+
41
+ <div class="flex items-center space-x-1">
42
+ <svg class="w-4 h-4 text-blue-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
43
+ <path d="M20.216 6.415l-.132-.666c-.119-.598-.388-1.163-1.001-1.379-.197-.069-.42-.098-.57-.241-.152-.143-.196-.366-.231-.572-.065-.378-.125-.756-.192-1.133-.057-.325-.102-.69-.25-.987-.195-.4-.597-.634-.996-.788a5.723 5.723 0 00-.626-.194c-1-.263-2.05-.36-3.077-.416a25.834 25.834 0 00-3.7.062c-.915.083-1.88.184-2.75.5-.318.116-.646.256-.888.501-.297.302-.393.77-.177 1.146.154.267.415.456.692.58.36.162.737.284 1.123.366 1.075.238 2.189.331 3.287.37 1.218.05 2.437.01 3.65-.118.299-.033.598-.073.896-.119.352-.054.578-.513.474-.834-.124-.383-.457-.531-.834-.473-.466.074-.96.108-1.382.146-1.177.08-2.358.082-3.536.006a22.228 22.228 0 01-1.157-.107c-.086-.01-.18-.025-.258-.036-.243-.036-.484-.08-.724-.13-.111-.027-.111-.185 0-.212h.005c.277-.06.557-.108.838-.147h.002c.131-.009.263-.032.394-.048a25.076 25.076 0 013.426-.12c.674.019 1.347.067 2.017.144l.228.031c.267.04.533.088.798.145.392.085.895.113 1.07.542.055.137.08.288.111.431l.319 1.484a.237.237 0 01-.199.284h-.003c-.037.006-.075.01-.112.015a36.704 36.704 0 01-4.743.295 37.059 37.059 0 01-4.699-.304c-.14-.017-.293-.042-.417-.06-.326-.048-.649-.108-.973-.161-.393-.065-.768-.032-1.123.161-.29.16-.527.404-.675.701-.154.316-.199.66-.267 1-.069.34-.176.707-.135 1.056.087.753.613 1.365 1.37 1.502a39.69 39.69 0 0011.343.376.483.483 0 01.535.53l-.071.697-1.018 9.907c-.041.41-.047.832-.125 1.237-.122.637-.553 1.028-1.182 1.171-.577.131-1.165.2-1.756.205-.656.004-1.31-.025-1.966-.022-.699.004-1.556-.06-2.095-.58-.475-.458-.54-1.174-.605-1.793l-.731-7.013-.322-3.094c-.037-.351-.286-.695-.678-.678-.336.015-.718.3-.678.679l.228 2.185.949 9.112c.147 1.344 1.174 2.068 2.446 2.272.742.12 1.503.144 2.257.156.966.016 1.942.053 2.892-.122 1.408-.258 2.465-1.198 2.616-2.657.34-3.332.683-6.663 1.024-9.995l.215-2.087a.484.484 0 01.39-.426c.402-.078.787-.212 1.074-.518.455-.488.546-1.124.385-1.766zm-1.478.772c-.145.137-.363.201-.578.233-2.416.359-4.866.54-7.308.46-1.748-.06-3.477-.254-5.207-.498-.17-.024-.353-.055-.47-.18-.22-.236-.111-.71-.054-.995.052-.26.152-.609.463-.646.484-.057 1.046.148 1.526.22.577.088 1.156.16 1.737.212 2.48.226 5.002.19 7.472-.14.45-.06.899-.13 1.345-.21.399-.072.84-.206 1.08.206.166.281.188.657.162.974a.544.544 0 01-.169.364zm-6.159 3.9c-.862.37-1.84.788-3.109.788a5.884 5.884 0 01-1.569-.217l.877 9.004c.065.78.717 1.38 1.5 1.38 0 0 1.243.065 1.658.065.447 0 1.786-.065 1.786-.065.783 0 1.434-.6 1.499-1.38l.94-9.95a3.996 3.996 0 00-1.322-.238c-.826 0-1.491.284-2.26.613z" />
44
+ </svg>
45
+ <span class="text-sm">
46
+ <a href="{{ coffeeUrl }}" target="_blank" rel="noopener noreferrer" class="text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 transition-colors">
47
+ Buy me a coffee
48
+ </a>
49
+ </span>
50
+ </div>
51
+
52
+ <span class="hidden px-2 text-neutral-400 sm:block">|</span>
53
+
54
+ <div class="flex items-center space-x-1">
55
+ <svg class="w-4 h-4 text-blue-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
56
+ <path d="m11.645 20.91-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z" />
57
+ </svg>
58
+ <span class="text-sm">
59
+ <a href="{{ sponsorUrl }}" target="_blank" rel="noopener noreferrer" class="text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 transition-colors">
60
+ Sponsor me on GitHub
61
+ </a>
62
+ </span>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </footer>
@@ -0,0 +1,25 @@
1
+ <script>
2
+ export const title = $props.title || 'Component'
3
+ export const description = $props.description || 'A modern component built with STX'
4
+ export const githubUrl = $props.githubUrl || '#'
5
+ </script>
6
+
7
+ <div class="flex flex-col items-center gap-3">
8
+ <h1 class="text-5xl font-bold mb-3 -mt-5 bg-gradient-to-r from-blue-500 to-purple-600 bg-clip-text text-transparent">
9
+ {{ title }}
10
+ </h1>
11
+ <p class="mb-3 mt-0 text-lg text-neutral-600 dark:text-neutral-400">
12
+ {{ description }}
13
+ </p>
14
+ <div class="flex gap-2">
15
+ <slot name="demo" />
16
+ <a
17
+ href="{{ githubUrl }}"
18
+ target="_blank"
19
+ rel="noopener noreferrer"
20
+ class="inline-flex items-center justify-center h-10 px-8 rounded-md text-sm font-semibold bg-gradient-to-b from-white to-neutral-100 dark:from-neutral-800 dark:to-neutral-900 text-neutral-900 dark:text-white shadow-md hover:shadow-lg transition-all duration-200"
21
+ >
22
+ GitHub
23
+ </a>
24
+ </div>
25
+ </div>
@@ -0,0 +1,39 @@
1
+ <script>
2
+ import { CodeBlock } from './CodeBlock.stx'
3
+
4
+ export const packageName = $props.packageName || '@stacksjs/component'
5
+ export const installCode = `bun add ${packageName}`
6
+ export const npmCode = `npm install ${packageName}`
7
+ export const pnpmCode = `pnpm add ${packageName}`
8
+ export const yarnCode = `yarn add ${packageName}`
9
+ </script>
10
+
11
+ <div class="installation">
12
+ <h2 class="my-2 text-lg font-semibold">Installation</h2>
13
+
14
+ <p class="my-3 text-base">
15
+ Install the component using your preferred package manager:
16
+ </p>
17
+
18
+ <div class="space-y-4">
19
+ <div>
20
+ <h3 class="text-sm font-medium mb-2 text-neutral-700 dark:text-neutral-300">Bun (recommended)</h3>
21
+ @component('CodeBlock', { code: installCode, language: 'bash' })
22
+ </div>
23
+
24
+ <div>
25
+ <h3 class="text-sm font-medium mb-2 text-neutral-700 dark:text-neutral-300">npm</h3>
26
+ @component('CodeBlock', { code: npmCode, language: 'bash' })
27
+ </div>
28
+
29
+ <div>
30
+ <h3 class="text-sm font-medium mb-2 text-neutral-700 dark:text-neutral-300">pnpm</h3>
31
+ @component('CodeBlock', { code: pnpmCode, language: 'bash' })
32
+ </div>
33
+
34
+ <div>
35
+ <h3 class="text-sm font-medium mb-2 text-neutral-700 dark:text-neutral-300">Yarn</h3>
36
+ @component('CodeBlock', { code: yarnCode, language: 'bash' })
37
+ </div>
38
+ </div>
39
+ </div>
@@ -0,0 +1,4 @@
1
+ export { default as CodeBlock } from './CodeBlock.stx'
2
+ export { default as Footer } from './Footer.stx'
3
+ export { default as Hero } from './Hero.stx'
4
+ export { default as Installation } from './Installation.stx'
@@ -0,0 +1,4 @@
1
+ export * from './useCopyCode'
2
+ export * from './useDarkMode'
3
+ export * from './useSEO'
4
+ export * from './utilities'
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Copy code to clipboard with visual feedback
3
+ */
4
+ export interface CopyCodeOptions {
5
+ code: string
6
+ onSuccess?: () => void
7
+ onError?: (error: Error) => void
8
+ timeout?: number
9
+ }
10
+
11
+ export interface CopyCodeResult {
12
+ copied: boolean
13
+ copy: () => Promise<void>
14
+ reset: () => void
15
+ }
16
+
17
+ export function useCopyCode(options: CopyCodeOptions): CopyCodeResult {
18
+ const { code, onSuccess, onError, timeout = 2000 } = options
19
+
20
+ let copied = false
21
+ let timeoutId: ReturnType<typeof setTimeout> | null = null
22
+
23
+ const reset = () => {
24
+ copied = false
25
+ if (timeoutId) {
26
+ clearTimeout(timeoutId)
27
+ timeoutId = null
28
+ }
29
+ }
30
+
31
+ const copy = async () => {
32
+ try {
33
+ if (!navigator.clipboard) {
34
+ throw new Error('Clipboard API not available')
35
+ }
36
+
37
+ await navigator.clipboard.writeText(code)
38
+ copied = true
39
+ onSuccess?.()
40
+
41
+ // Auto-reset after timeout
42
+ timeoutId = setTimeout(() => {
43
+ copied = false
44
+ timeoutId = null
45
+ }, timeout)
46
+ }
47
+ catch (error) {
48
+ const err = error instanceof Error ? error : new Error('Failed to copy code')
49
+ onError?.(err)
50
+ console.error('Failed to copy code:', err)
51
+ }
52
+ }
53
+
54
+ return {
55
+ copied,
56
+ copy,
57
+ reset,
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Simple synchronous copy function for direct use
63
+ */
64
+ export async function copyToClipboard(text: string): Promise<boolean> {
65
+ try {
66
+ await navigator.clipboard.writeText(text)
67
+ return true
68
+ }
69
+ catch (error) {
70
+ console.error('Failed to copy to clipboard:', error)
71
+ return false
72
+ }
73
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Dark mode utilities for STX components
3
+ */
4
+
5
+ export interface DarkModeOptions {
6
+ storageKey?: string
7
+ defaultTheme?: 'light' | 'dark' | 'system'
8
+ }
9
+
10
+ export interface DarkModeResult {
11
+ isDark: boolean
12
+ theme: 'light' | 'dark' | 'system'
13
+ toggle: () => void
14
+ setTheme: (theme: 'light' | 'dark' | 'system') => void
15
+ }
16
+
17
+ /**
18
+ * Check if system prefers dark mode
19
+ */
20
+ export function getSystemTheme(): 'light' | 'dark' {
21
+ if (typeof window === 'undefined')
22
+ return 'light'
23
+
24
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
25
+ }
26
+
27
+ /**
28
+ * Get the effective theme (resolving 'system' to actual theme)
29
+ */
30
+ export function getEffectiveTheme(theme: 'light' | 'dark' | 'system'): 'light' | 'dark' {
31
+ return theme === 'system' ? getSystemTheme() : theme
32
+ }
33
+
34
+ /**
35
+ * Use dark mode hook
36
+ */
37
+ export function useDarkMode(options: DarkModeOptions = {}): DarkModeResult {
38
+ const { storageKey = 'theme', defaultTheme = 'system' } = options
39
+
40
+ // Get initial theme from storage or use default
41
+ let theme: 'light' | 'dark' | 'system' = defaultTheme
42
+
43
+ if (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {
44
+ const stored = localStorage.getItem(storageKey) as 'light' | 'dark' | 'system' | null
45
+ if (stored)
46
+ theme = stored
47
+ }
48
+
49
+ const isDark = getEffectiveTheme(theme) === 'dark'
50
+
51
+ // Apply theme to document
52
+ const applyTheme = (newTheme: 'light' | 'dark' | 'system') => {
53
+ const effective = getEffectiveTheme(newTheme)
54
+
55
+ if (typeof document !== 'undefined') {
56
+ if (effective === 'dark') {
57
+ document.documentElement.classList.add('dark')
58
+ }
59
+ else {
60
+ document.documentElement.classList.remove('dark')
61
+ }
62
+ }
63
+
64
+ if (typeof localStorage !== 'undefined') {
65
+ localStorage.setItem(storageKey, newTheme)
66
+ }
67
+ }
68
+
69
+ // Set theme
70
+ const setTheme = (newTheme: 'light' | 'dark' | 'system') => {
71
+ theme = newTheme
72
+ applyTheme(newTheme)
73
+ }
74
+
75
+ // Toggle between light and dark
76
+ const toggle = () => {
77
+ const currentEffective = getEffectiveTheme(theme)
78
+ const newTheme = currentEffective === 'dark' ? 'light' : 'dark'
79
+ setTheme(newTheme)
80
+ }
81
+
82
+ // Apply initial theme
83
+ applyTheme(theme)
84
+
85
+ // Listen for system theme changes if using system theme
86
+ if (typeof window !== 'undefined' && theme === 'system') {
87
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (_e) => {
88
+ if (theme === 'system') {
89
+ applyTheme('system')
90
+ }
91
+ })
92
+ }
93
+
94
+ return {
95
+ isDark,
96
+ theme,
97
+ toggle,
98
+ setTheme,
99
+ }
100
+ }